Can I pass keyword args to do-blocks in julia? - julia

Below is an example:
def(f::Function) = f
f1 = def() do x
x
end
f2 = def() do x, y
x, y
end
f3 = def() do x, y; z # this syntax is accepted
x, y, z
end
f1(1)
f2(1, 2)
f3(1, 2, z=3) # ERROR: LoadError: function #13 does not accept keyword arguments
The example below is not my actual use case, but it illustrates the question. How should I handle kwargs in a do-block?

As far as I know this is not possible. If you look at /src/julia-parser.scm file you can find that parsing of a do block is defined in parse-do here. And you can see that do-block is rewritten to an anonymous function that can only accept positional arguments in doargs variable separated by commas.
In this line you can see that ; explicitly terminates parsing of arguments of do-block. This means that:
julia> z = "hello"
"hello"
julia> f3 = def() do x, y; println(z) # this syntax is accepted
x, y, z
end
#24 (generic function with 1 method)
julia> f3(100, 1000)
hello
(100, 1000, "hello")
will work as the code past ; is treated as a part of do-block body.
You could try to put ( and ) around the arguments like this:
julia> f3 = def() do (x, y; z)
x, y, z
end
ERROR: syntax: invalid assignment location "; z"
julia> f3 = def() do (y; z)
y, z
end
ERROR: syntax: "begin
y
# REPL[52], line 1
z
end" is not a valid function argument name
This will go through parser, but then julia-syntax.scm will throw an error. This probably could be improved as:
f3 = def(function(x, y; z)
x, y, z
end)
works correctly (you can consider opening an issue for this on https://github.com/JuliaLang/julia/issues).
Until this is fixed the only thing you can do is to use anonymous function definition approach I have given above (although it is not very clean).

You can do this, you just need to pass the kwargs before the do:
julia> f(g, xs; kwargs...) = begin
println(kwargs...)
map(g,xs)
end
f (generic function with 1 method)
julia> f(1:5; thisworks=true) do x x^2 end
:thisworks => true
5-element Array{Int64,1}:
1
4
9
16
25

Related

pass parameters to insided functions

Suppose I have a very simple function,
function func_in(x; y, z)
println(x + z)
end
which is called by another function
function test_dict(x, y, z...)
println(x + y )
func_in(z...)
end
I would like to pass parameter in z... to func_in. How could I do it in the right way? Note in func_in there are kargs and I could not find a way to pass parameter correctly.
The following code doesn't work
test_dict(1, 2, 15, 17, 19)
Many thanks!
UPDATE
I would like to be a little more elaborate about possible scenarios that parameters in z... could be very flexible.
function func_in(x; y, z, t1, t2, t3, t4)
println(x + t4)
end
Then how do I only pass x and t4, something like:
test_dict(1, 2, (x = 15, t4 = 19))
In R language, the ... works straightforwardly. Anything in ... would be used when it is cited. Like following, ... is directly passed to fn_2 and depatached accordingly. How to get similar behivor in Julia? I hope I have made myself clear.
fn_1 <- function(x, y, ...) {
fn_2(...)
}
Positional args and kwargs must be splatted separately:
func_in(args...; kwargs...)
Right now, you only pass positional inputs.
If you want to only pass kwargs to a function, you must do (note leading ;)
foo(; kwargs...)
BTW, it is considered good practice to always distinguish kwargs with a leading ;, even when it is not strictly necessary:
foo(x, y, a="hello", b=4) # legal
foo(x, y; a="hello", b=4) # better style
foo(a="hello", b=4) # legal
foo(; a="hello", b=4) # better style
Response to update: Keyword arguments must either have a default value, or you must provide a value for it when you call the function. You can for example write:
function func_in(x; y=nothing, z=nothing, t1=nothing, t2=nothing, t3=nothing, t4=nothing)
Another problem in your function test_dict, is that you must call it with three positional arguments, two for println and one for func_in, but you are only calling it with two positional arguments.
This will work:
function func_in(x; y=nothing, z=nothing, t1=nothing, t2=nothing, t3=nothing, t4=nothing)
println(x + t4)
end
function test_dict(x, y, z; w...) # note: *three* positional args
println(x + y )
func_in(z; w...)
end
test_dict(1, 2, 3; y = 15, t4 = 19) # three positional args
You can also use a named tuple and splat it:
kwargs = (y = 15, t4 = 19) # named tuple
test_dict(1, 2, 3; kwargs...)
Is this what you have in mind? :
julia> function func_in(x; y, z)
println(x + z)
end
func_in (generic function with 1 method)
julia> function test_dict(x, y, z...)
println(x + y )
func_in(z[1]; y = z[2], z = z[3])
end
test_dict (generic function with 1 method)
julia> test_dict(1, 2, 15, 17, 19)
3
34

How to define tuple of types in julia type declaration's

How do I declare a tuple of specific types for a julia function?
This works:
function f(x, y)::Int8
x+y
end
julia> f(2, 3)
5
This works too:
function g(x, y)::Tuple
x+y, x*y
end
julia> g(2, 3)
(5, 6)
But I can't figure out how to define the types in the tuple.
For example, this throws an error:
function h(x, y)::Tuple(::Int8, ::Int8)
x+y, x*y
end
ERROR: syntax: invalid "::" syntax around REPL[48]:2
An this too:
function k(x, y)::Tuple(Int8, Int8)
x+y, x*y
end
julia> k(2, 3)
ERROR: MethodError: no method matching Tuple(::Type{Int8}, ::Type{Int8})
Use curly braces and omit the :: for the tuple's elements' types:
function k(x, y)::Tuple{Int8, Int8}
x + y, x * y
end
julia> k(2, 3)
(5, 6)

Using ForwardDiff.jl for a function of many variables and parameters Julia

The github repo for ForwardDiff.jl has some examples. I am trying to extend the example to take in addition to a vector of variables, a parameter. I cannot get it to work.
This is the example (it is short so I will show it rather than linking)
using ForwardDiff
x = rand(5)
f(x::Vector) = sum(sin, x) .+ prod(tan, x) * sum(sqrt, x);
g = x -> ForwardDiff.gradient(f, x);
g(x) # this outputs the gradient.
I want to modify this since I use functions with multiple parameters as well as variables. As a simple modification I have tried adding a single parameter.
f(x::Vector, y) = (sum(sin, x) .+ prod(tan, x) * sum(sqrt, x)) * y;
I have tried the following to no avail:
fp = x -> ForwardDiff.gradient(f, x);
fp = x -> ForwardDiff.gradient(f, x, y);
y = 1
println("test grad: ", fp(x, y))
I get the following error message:
ERROR: LoadError: MethodError: no method matching (::var"#73#74")(::Array{Float64,1}, ::Int64)
A similar question was not answered in 2017. A comment led me to here and it seems the function can only accept one input?
The target function must be unary (i.e., only accept a single argument). ForwardDiff.jacobian is an exception to this rule.
Has this changed? It seems very limited to only be able to differentiate unary functions.
A possible workaround would be to concatenate the list of variables and parameters and then just slice the returned gradient to not include the gradients with respect to the parameters, but this seems silly.
I personally think it makes sense to have this unary-only syntax for ForwardDiff. In your case, you could just pack/unpack x and y into a single vector (nominally x2 below):
julia> using ForwardDiff
julia> x = rand(5)
5-element Array{Float64,1}:
0.4304735670747184
0.3939269364431113
0.7912705403776603
0.8942024934250143
0.5724373306715196
julia> f(x::Vector, y) = (sum(sin, x) .+ prod(tan, x) * sum(sqrt, x)) * y;
julia> y = 1
1
julia> f(x2::Vector) = f(x2[1:end-1], x2[end]) % unpacking in f call
f (generic function with 2 methods)
julia> fp = x -> ForwardDiff.gradient(f, x);
julia> println("test grad: ", fp([x; y])) % packing in fp call
test grad: [2.6105844240785796, 2.741442601659502, 1.9913192377198885, 1.9382805843854594, 2.26202717745402, 3.434350946190029]
But my preference would be to explicitly name the partial derivatives differently:
julia> ∂f∂x(x,y) = ForwardDiff.gradient(x -> f(x,y), x)
∂f∂x (generic function with 1 method)
julia> ∂f∂y(x,y) = ForwardDiff.derivative(y -> f(x,y), y)
∂f∂y (generic function with 1 method)
julia> ∂f∂x(x, y)
5-element Array{Float64,1}:
2.6105844240785796
2.741442601659502
1.9913192377198885
1.9382805843854594
2.26202717745402
julia> ∂f∂y(x, y)
3.434350946190029
Here's a quick attempt at a function which takes multiple arguments, the same signature as Zygote.gradient:
julia> using ForwardDiff, Zygote
julia> multigrad(f, xs...) = ntuple(length(xs)) do i
g(y) = f(ntuple(j -> j==i ? y : xs[j], length(xs))...)
xs[i] isa AbstractArray ? ForwardDiff.gradient(g, xs[i]) :
xs[i] isa Number ? ForwardDiff.derivative(g, xs[i]) : nothing
end;
julia> f1(x,y,z) = sum(x.^2)/y;
julia> multigrad(f1, [1,2,3], 4)
([0.5, 1.0, 1.5], -0.875)
julia> Zygote.gradient(f1, [1,2,3], 4)
([0.5, 1.0, 1.5], -0.875)
For a function with several scalar arguments, this evaluates each derivative separately, and perhaps it would be more efficient to use one evaluation with some Dual(x, (dx, dy, dz)). With large-enough array arguments, ForwardDiff.gradient will already perform multiple evaluations, each with some number of perturbations (the chunk size, which you can control).

Specializing method calls in order in meta-programming

I have issue after calling my macro:
#introspectable square(x) = x * x
Then when calling
square(3)
i should be able to get 9, cause the function call has been specialized to execute an attribute of the structure which is Julia code, however when I enter the macro, the code seems to be directly evaluated.
What i have tried:
struct IntrospectableFunction
name
parameters
native_function
end
(f::IntrospectableFunction)(x) = f.native_function(x)
macro introspectable(expr)
name = expr.args[1].args[1]
parameters = tuple(expr.args[1].args[2:end]...)
body = expr.args[2].args[2]
:( global $name = IntrospectableFunction( :( name ), $parameters, :( body ) ))
end
#introspectable square(x) = x * x
square(3)
The answer should be 9 , however i get "Object of type symbol are not callable ". However if i replace :( body ) with x -> x * x i get the desired result, my objective is generalizing the macro-call.
I usually find it easier to work with expressions in macros (it is not the shortest way to write things, but, from my experience, it is much easier to control what gets generated).
Therefore I would rewrite your code as:
macro introspectable(expr)
name = expr.args[1].args[1]
parameters = expr.args[1].args[2:end]
anon = Expr(Symbol("->"), Expr(:tuple, parameters...), expr.args[2].args[2])
constr = Expr(:call, :IntrospectableFunction, QuoteNode(name), Tuple(parameters), anon)
esc(Expr(:global, Expr(Symbol("="), name, constr)))
end
Now, as you said you wanted generality I would define your functor like this:
(f::IntrospectableFunction)(x...) = f.native_function(x...)
(in this way you allow multiple positional arguments to be passed).
Now let us test our definitions:
julia> #introspectable square(x) = x * x
IntrospectableFunction(:square, (:x,), getfield(Main, Symbol("##3#4"))())
julia> square(3)
9
julia> #macroexpand #introspectable square(x) = x * x
:(global square = IntrospectableFunction(:square, (:x,), ((x,)->x * x)))
julia> #introspectable toarray(x,y) = [x,y]
IntrospectableFunction(:toarray, (:x, :y), getfield(Main, Symbol("##5#6"))())
julia> toarray("a", 10)
2-element Array{Any,1}:
"a"
10
julia> #macroexpand #introspectable toarray(x,y) = [x,y]
:(global toarray = IntrospectableFunction(:toarray, (:x, :y), ((x, y)->[x, y])))
julia> function localscopetest()
#introspectable globalfun(x...) = x
end
localscopetest (generic function with 1 method)
julia> localscopetest()
IntrospectableFunction(:globalfun, (:(x...),), getfield(Main, Symbol("##9#10"))())
julia> globalfun(1,2,3,4,5)
(1, 2, 3, 4, 5)
julia> function f()
v = 100
#introspectable localbinding(x) = (v, x)
end
f (generic function with 1 method)
julia> f()
IntrospectableFunction(:localbinding, (:x,), getfield(Main, Symbol("##11#12")){Int64}(100))
julia> localbinding("x")
(100, "x")
(note that it is useful to use #macroexpand to make sure our macro works as expected)
EDIT - how to handle a minimal multiple dispatch
I am writing a non-macro example because it is related to the data structure:
Use e.g. such a definition:
struct IntrospectableFunction
name::Symbol
method_array::Vector{Pair{Type{<:Tuple}, Function}}
end
function (f::IntrospectableFunction)(x...)
for m in f.method_array
if typeof(x) <: first(m)
return last(m)(x...)
end
end
error("signature not found")
end
and now you can write:
julia> square = IntrospectableFunction(:square, [Tuple{Any}=>x->x*x,Tuple{Any,Any}=>(x,y)->x*y])
IntrospectableFunction(:square, Pair{DataType,Function}[Tuple{Any}=>##9#11(), Tuple{Any,Any}=>##10#12()])
julia> square(3)
9
julia> square(2,3)
6
Keep in mind that the approach I present is not perfect and universal - it just serves to give a very simple example how you could do it.

Escaping a collection of symbols

I'm trying to escape a collection of symbols so that way I get a collection of the variables, but am running into issues. Here's an MWE:
macro escape_all(x...)
:($(esc.(x))...)
end
x = 1
y = 2
z = 3
macroexpand(:(#escape_all x y z))
This returns
:(((:($(Expr(:escape, :x))), :($(Expr(:escape, :y))), :($(Expr(:escape, :z))))...,))
but what I'm looking for it to return is just
(x,y,z)
Calling Expr explicitely works:
julia> macro escape_all(xs...)
Expr(:tuple, esc.(xs)...)
end
#escape_all (macro with 1 method)
julia> #macroexpand #escape_all x y z
:((x, y, z))
But you can also use the following unquote syntax in a context where list-splicing (like ,# in Lisp, I guess) makes sense:
julia> macro escape_list(xs...)
:([$(esc.(xs)...)])
end
#escape_list (macro with 1 method)
julia> macro escape_f(xs...)
:(f($(esc.(xs)...)))
end
#escape_f (macro with 1 method)
julia> #macroexpand #escape_list x y z
:([x, y, z])
julia> #macroexpand #escape_f x y z
:((Main.f)(x, y, z))
Funnily, I never saw $(x...) to be talked about anywhere. I stumbled upon it recently reading someone's code. But it's mentioned in the current "latest" docs as splatting interpolation.

Resources