Julia multiple dispatch based on named arguments? - julia

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

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.
Different Function Names
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
Custom Types
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")

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.

Related

Julia Macro to Save the Overt form of a Function, and Define it

Some of the parameters to a simulation I am writing are functions. When the output is generated, I want to put the definition of these functional parameters in the output. I have in mind a macro that somehow saves the definition as a string, and then defines it. For example, here's what I do now:
borda_score_fn(p) = exp(1/p)
global g_borda_score_fn_string = "exp(1/p)"
And then I write g_borda_score_fn_string to my output. But this is really ugly!
What I would like to do is something like this:
#paramfn borda_score_fn(p) = exp(1/p)
And later be able to both call borda_score_fn(p), and have the form (i.e., "exp(1/p)") available for writing to my output log. (The string form might get stashed in a global dict, actually, they both could.)
I have tried many version of this, but can't get the right set of parses and calls to get it to work. Any help would be appreciated.
This may be a bit different than what you have in mind, but one perhaps "Julian" approach might be to have the function itself return the form string via multiple dispatch, rather than defining a whole new global variable just for that. For example, say we have a type
struct Form end
that we can use for dispatch, then we can write
borda_score_fn(p) = exp(1/p)
borda_score_fn(::Form) = "exp(1/p)"
which can then be retrieved just by calling the function with our type
julia> borda_score_fn(2)
1.6487212707001282
julia> borda_score_fn(Form())
"exp(1/p)"
That might actually be not bad on its own. But, if you want a macro to do both parts at once, then something along the lines of
macro paramfn(e)
name = esc(e.args[1].args[1])
str = string(e.args[2].args[2])
f = esc(e)
quote
$name(::Form) = $str
$f
end
end
would let you write
julia> #paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 2 methods)
julia> borda_score_fn(1)
2.718281828459045
julia> borda_score_fn(Form())
"exp(1 / p)"
For completeness, here's how you can do it in a way more similar to your original approach, but more idiomatically than with a global variable:
julia> module FormOf
export formof, #paramfn
function formof end
macro paramfn(expr)
name = esc(expr.args[1].args[1])
form_str = string(expr.args[2].args[2])
quote
$(esc(expr))
$FormOf.formof(::typeof($name)) = $form_str
$name
end
end
end
Main.FormOf
julia> FormOf.#paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 1 method)
julia> FormOf.formof(borda_score_fn)
"exp(1 / p)"
However, since it defines a new method of FormOf.formof, this only works in global scope:
julia> function bla()
FormOf.#paramfn fn(p) = exp(1/p)
fn(10) + 1
end
ERROR: syntax: Global method definition around REPL[45]:10 needs to be placed at the top level, or use "eval".
Stacktrace:
[1] top-level scope
# REPL[50]:1
#cbk's solution does not have this limitation.

Julia: how to write a printing method for repl for a new array type?

Apologies the code is a bit long but it's a MWE.
Say, I have defined a module which defines a new vector type called CmpVector. I want to override the text printed to the repo. So I've overwritten, print, show and display but it still prints its own thing. How do I overload the print to REPL for a new array??
module CmpVectors
import Base:size,print,show,getindex, setindex!, display
mutable struct CmpVector{T} <: AbstractVector{T}
# compressed::Vector{UInt8}
# vector_pointer::Ptr{T}
inited::Bool
# size::Tuple
end
size(pf::CmpVector{T}) where T = (1,)
display(io::IO, pf::CmpVector{T}) where T = begin
if pf.inited
display(io, "NOO")
else
display(io, "Vector in compressed state")
end
end
show(io::IO, pf::CmpVector{T}) where T = begin
if pf.inited
show(io, "NOO")
else
show(io, "Vector in compressed state")
end
end
print(io::IO, pf::CmpVector{T}) where T = begin
if pf.inited
show(io, "NOO")
else
print(io, "Vector in compressed state")
end
end
getindex(pf::CmpVector{T}, i...) where T = zero(T)
end # module
I ran this
using Revise
using CmpVectors
CmpVectors.CmpVector{Int}(true)
it prints
1-element CmpVectors.CmpVector{Int64}:
0
You'd want to overload the show which is in Base (Base.show), not define your own show. Also, you should specify which MIME type you are overloading.
mutable struct MyType
val::Symbol
end
function Base.show(io::IO, ::MIME"text/plain", mytype::MyType)
println(io, "This is my type which contains $(mytype.val)")
end
MyType(:something)
## outputs
This is my type which contains something

In Julia: initialize fields with nothing

I have a mutable struct with optional fields like this:
mutable struct MyStruct
field1::Union{Int, Nothing}
field2::Union{String, Nothing}
field3::Union{Int, Nothing}
field4::Union{String, Nothing}
# ...
end
I can now write a default constructor which initializes the fields with nothing:
MyStruct() = MyStruct(nothing, nothing, nothing, nothing)
This is not so nice when my struct has many fields. Also, I have to count the fields to get the constructor call with all the 'nothings' correct in this case. Is there a better way to do that?
Depending on the field content, I want to call different functions later:
if mystruct.field1 == nothing
do_this()
else
do_that()
end
You can use fieldcount function to achieve that. This function gives you the number of fields of that an instance of given type would have. Here is an example containing a mutable struct and an outer constructor.
julia> mutable struct Foo
x
y
z
end
julia> Foo() = Foo(ntuple(x->nothing, fieldcount(Foo))...); # you can also fill an array and use `...`
julia> Foo()
Foo(nothing, nothing, nothing)

Julia: How to iterate with Channel

When I run the following code, I get a deprecation saying produce has been replace with channels.
function source(dir)
filelist = readdir(dir)
for filename in filelist
name,ext = splitext(filename)
if ext == ".jld"
produce(filename)
end
end
end
path = "somepathdirectoryhere"
for fname in Task(source(path))
println(fname)
end
I cannot find an example on how to do this with channels. I've tried creating a global channel and using put! instead of produce with no luck.
Any ideas?
Here's one way. Modify your function to accept a channel argument, and put! data in it:
function source(dir, chnl)
filelist = readdir(dir)
for filename in filelist
name, ext = splitext(filename)
if ext == ".jld"
put!(chnl, filename) % this blocks until "take!" is used elsewhere
end
end
end
Then create your task implicitly using the Channel constructor (which takes a function with a single argument only representing the channel, so we need to wrap the source function around an anonymous function):
my_channel = Channel( (channel_arg) -> source( pwd(), channel_arg) )
Then, either check the channel is still open (i.e. task hasn't finished) and if so take an argument:
julia> while isopen( my_channel)
take!( my_channel) |> println;
end
no.jld
yes.jld
or, use the channel itself as an iterator (iterating over Tasks is becoming deprecated, along with the produce / consume functionality)
julia> for i in my_channel
i |> println
end
no.jld
yes.jld
Alternatively you can use #schedule with bind etc as per the documentation, but it seems like the above is the most straightforward way.

constrain argument to be in a set of values in Julia function signature

Is there a way in Julia to specify that a function argument can take one of a set of values through type annotations? For example, let's say I have function foo which accepts a single argument
function foo(x::String)
print(x)
end
the argument x can only be a String. Is there a way to further constrain it in the function signature so that it can only be for example one of the strings "right", "left", or "center"?
In Julia, the motto should be "There's a type for that!".
One way of handling this would be to create a type with a constructor that only allows the values you want (and possibly stores them in a more efficient manner).
Here is one example:
const directions = ["left", "right", "center"]
immutable MyDirection
Direction::Int8
function MyDirection(str::AbstractString)
i = findnext(directions, str, 1)
i == 0 && throw(ArgumentError("Invalid direction string"))
return new(i)
end
end
Base.show(io::IO, x::MyDirection) = print(io, string("MyDirection(\"",directions[x.Direction],"\")"))
function foo(x::MyDirection)
println(x)
end
function foo(str::AbstractString)
x = MyDirection(str)
println(x)
end
test = MyDirection("left")
foo(test)
foo("right")
Note: my example is written with Julia 0.4!
Edit:
Another approach would be to use symbols, such as :left, :right, and :center,
instead of strings.
These have the advantage of being interned (so that they can be compared simply by comparing their address), and they can also be used directly for type parameters.
For example:
immutable MyDirection{Symbol} ; end
function MyDirection(dir::Symbol)
dir in (:left, :right, :center) || error("invalid direction")
MyDirection{dir}()
end
MyDirection(dir::AbstractString) = MyDirection(symbol(dir))
That will let you do things like:
x = MyDirection("left")
which will create an immutable object of type MyDirection{:left}.
No, it is not. That would be dispatching on values, which isn't possible in Julia.
I'm not sure what your actual application is, but there are some possibly-appropriate workarounds to this, e.g.
abstract Sam81Args
type ArgRight <:Sam81Args end
type ArgLeft <:Sam81Args end
type ArgCenter <:Sam81Args end
function foo{T<:Sam81Args}(x::Type{T})
println(T)
end
foo(ArgCenter)

Resources