Inline functions with default parameters

I have a doubt about default arguments. In the Functions documentation it says that I can use optional arguments aswell as defining a function with a simplyfied syntax of
y(x)=x**2, but can’t I have such a function with optional parameters? For example this works

t=1:0.1:10
y(t,v_0=1,a=-9.8,y_0=0)=y_0+v_0*t+a/2*t^2 

If I call it with


y_vec=y.(t)

But not if I call it with


y_vec=y.(t,v_0=10)

You should include error messages:

julia> y.(t,v_0=10)
ERROR: MethodError: no method matching y(::Float64; v_0::Int64)

Closest candidates are:
  y(::Any, ::Any, ::Any, ::Any) got unsupported keyword argument "v_0"
   @ Main REPL[11]:1
  y(::Any, ::Any, ::Any) got unsupported keyword argument "v_0"
   @ Main REPL[11]:1
  y(::Any, ::Any) got unsupported keyword argument "v_0"
   @ Main REPL[11]:1
  ...

Stacktrace:
 [1] (::Base.Broadcast.var"#43#44"{@Kwargs{v_0::Int64}, typeof(y)})(args::Float64)
   @ Base.Broadcast .\broadcast.jl:1327
 [2] _broadcast_getindex_evalf
   @ .\broadcast.jl:709 [inlined]
 [3] _broadcast_getindex
   @ .\broadcast.jl:682 [inlined]
 [4] getindex
   @ .\broadcast.jl:636 [inlined]
 [5] copy
   @ .\broadcast.jl:942 [inlined]
 [6] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, Base.Broadcast.var"#43#44"{@Kwargs{…}, typeof(y)}, Tuple{StepRangeLen{…}}})
   @ Base.Broadcast .\broadcast.jl:903
 [7] top-level scope
   @ REPL[13]:1
Some type information was truncated. Use `show(err)` to see complete types.

Your last argument is call is treating v_0 as if it were a keyword argument, instead of a positional argument. You should just do y.(t, 10), or change the function to use keyword arguments in the definition.

2 Likes

Oh I fell for that as well recently. though there is a , in y_vec=y.(t,v_0=10) the v_0= makes Julia start the “keyword section”, so what you wrote is the same as y_vec=y.(t; v_0=10). I felt accepting a , here as well is maybe misleading.

As Daniel wrote all your arguments are positional (optional) and no keywords. Besides that I am not 100% sure how keywords work together with your broadcasting, but that is maybe a next point to check then.

1 Like

The keyword arguments are not broadcasted over, as far as I can tell:

julia> function f(x; v)
       @show x, v
       end
f (generic function with 2 methods)

julia> f.([1, 2, 3]; v = [1, 2, 3])
(x, v) = (1, [1, 2, 3])
(x, v) = (2, [1, 2, 3])
(x, v) = (3, [1, 2, 3])

julia> f.(1, v = [1, 2, 3])
(x, v) = (1, [1, 2, 3])
(1, [1, 2, 3])

This is handled in Base.broadcasted_kwsyntax:

julia> Meta.@lower f.([1, 2, 3]; v = [1, 2, 3])
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = Base.vect(1, 2, 3)
│   %2 = Base.broadcasted_kwsyntax
│   %3 = Core.tuple(:v)
│   %4 = Core.apply_type(Core.NamedTuple, %3)
│   %5 = Base.vect(1, 2, 3)
│   %6 = Core.tuple(%5)
│   %7 = (%4)(%6)
│   %8 = Core.kwcall(%7, %2, f, %1)
│   %9 = Base.materialize(%8)
└──      return %9
))))
3 Likes

The NamedPositionals.jl package allows function calls closer to OP’s intent:

using NamedPositionals
y1 = (u->@np y(u, v_0=10;)).(t)
y2 = (u->@np y(u, v_0=5, a=-1.62;)).(t)
y3 = (u->@np y(u, v_0=1, a=-9.8, y_0=7.5;)).(t)

I could not get broadcasting to work with the @np macro, hence the broadcasting of anonymous functions above.

But difficult to see the advantage of this compared to the simple calls:

v_0 = 10
y1b = y.(t, v_0)

v_0 = 0.1; a = -1.62
y2b = y.(t, v_0, a)

v_0 = 1; a = -9.8; y_0 = 7.5
y3b = y.(t, v_0, a, y_0)
1 Like

An orthogonal point to consider - I often find that when I’m writing function signatures like that, it’s probably time to make a struct that encodes a bunch of those parameters :person_shrugging:

2 Likes

See also Keyword vs Optional Function Arguments? - #3 by digital_carver

1 Like