Reputation: 15336
I'm trying to create flexible function that would allow variety of uses:
save(image_url = "http://...")
save(image_url = "http://...", title = "Cats", id = "cats")
save(text = "Some text", comments = "Some comments")
save(text = "Another text", title = "News", compression = true)
Basically it's 3 (or more) functions (save_image_url
, save_image_path
, save_text
) combined .
All of them have 3 same optional arguments title
, descriptions
, compression
. And each can have any number of specific arguments.
I don't want to use positional arguments because it would be hard to remember the order of arguments. Also, the first argument would have the same String
type for image_url
and image_path
and text
.
Unfortunately it seems multiple dispatch is not working on named arguments. What are the patterns to handle such cases then?
Not working implementation
function save(;
image_url:: String,
title:: Union{String, Nothing} = nothing,
description:: Union{String, Nothing} = nothing,
compression:: Union{Bool, Nothing} = nothing
)::Nothing
nothing
end
function save(;
image_path:: String,
title:: Union{String, Nothing} = nothing,
description:: Union{String, Nothing} = nothing,
compression:: Union{Bool, Nothing} = nothing
)::Nothing
nothing
end
function save(;
text:: String,
comments:: Union{String, Nothing} = nothing,
title:: Union{String, Nothing} = nothing,
description:: Union{String, Nothing} = nothing,
compression:: Union{Bool, Nothing} = nothing
)::Nothing
nothing
end
Upvotes: 5
Views: 1027
Reputation: 6956
Multiple dispatch is great, but it sometimes is the wrong tool for the job. In this case, basic dispatch on function names is simple and sufficient. I would prefer this option if at all possible. However, if a consistent function name is necessary, then you can always dispatch on custom types.
function save_image_url(url::String; kwargs...)
handle_general_options(; kwargs...)
# url specific code
end
function save_image_path(path::String; kwargs...)
handle_general_options(; kwargs...)
# path specific code
end
function handle_general_options(; title=nothing, description=nothing)
# shared code
end
struct ImageURL
val::String
end
function save(url::ImageURL; kwargs...)
handle_metadata(; kwargs...)
# url specific code
end
function handle_general_options(; title=nothing, description=nothing)
# shared code
end
You can make dispatch clear at the call site by immediately creating the object to dispatch on:
save(ImageURL("https://..."); title="SomeTitle")
Upvotes: 7
Reputation: 1390
I would suggest splitting the user interface and implementation details like this:
function save(;
image_url=nothing,
image_path=nothing,
text=nothing,
title=nothing,
description=nothing,
compression=nothing
)
save(image_url, image_path, text, title, description, compression)
end
function save(image_url::String, ::Nothing, ::Nothing, title, description, compression)
# do something
end
function save(::Nothing, image_path::String, ::Nothing, title, description, compression)
# do something
end
function save(::Nothing, ::Nothing, text::String, title, description, compression)
# do something
end
function save(image_url, image_path, text, title, description, compression)
@error "only one of image_url, image_path, text are acceptable"
end
When things become too complicated, you can always create a new struct from keyword arguments and dispatch it by using traits.
Upvotes: 7