Function arguments

Is it possible to pass a named tuple to a function and have the components available as variables without explicitly specifying them as arguments.

f( X ) = A + B
X = (A=1,B=2)
f( X )

output : 3

Background: I have about 20 variables and a dozen functions. Each function uses a subset of the variables. I don’t want to have to specify each subset at the start of each function. I’d rather just send a single named tuple, but don’t want to have to write X.A, X.B if its possible to just write A, B

No, not automatically. You will have to specify the arguments manually or unpack the tuple.

1 Like

Take a look at @unpack in Parameters.jl. You may also be interested in ComponentArrays.jl.

2 Likes

will do thanks

Had a look at these. It seems you still have to list the variable names. e.g. a, b = unpack(X)
The only way I found to unpack without listing the variable names is something messy like:

`@pipe names(X) .|> @eval( $(Symbol(X)) = $(r[X]) )`

@eval will evaluate those names into the global scope rather than into the function as locals, so probably not what you’d want either maybe if you’re wanting to unpack function arguments?

1 Like

yes

This is I think close to what you want

julia> f((A,B)) = A + B
f (generic function with 1 method)

julia> f((A=1, B=2))
3

Sorry Tomak. I was hoping to not specify the A and B at all

As I said before - unpacking without specifying which variables you want is not possible, sorry. Variable names don’t exist anymore at the time your named tuple is passed in. You could do it with a generated function, but that’s really going to be more of a hassle than convenience (which is what you’re looking for) since now every function you’d write would have to be generated. There are also a bunch of limitations of generated functions that make them unsuitable for regular development.

2 Likes

Your simplest option if you don’t want to repeat argument names everywhere and keep them in sync is to use Parameters like @amrods has suggested:

julia> using Parameters

julia> @with_kw struct T
           A
           B
       end
T

julia> f(X::T) = (@unpack_T X; A + B)
f (generic function with 1 method)

julia> f(T(A = 1, B = 2))
3

I don’t believe you can use NamedTuples for the @unpack_T feature.

2 Likes

That has the disadvantage of clobbering function scope with (potentially) unnecessary variables, which may shadow other variables and can lead to very subtle bugs.

Try the @with macro from StaticModules.jl.

function f(nt)
    @with nt begin
        a + b
    end
end

x = (a = 1, b = 2)
julia> f(x)
3
4 Likes

Which seems to be exactly what OP wants, they do not want to need to declare which variables they will be taking from the struct so necessarily they want to dump all names on some scope.

In this simple example, yes, but if OP only wants a subset of all fields (as indicated in the OP), the additional names may clobber other variables that are incidentally named the same, which I don’t think was the intention.

This will destructure by index instead of by name, so f((B=2, A=1)) will set A to 2 and B to 1. In 1.7, you will be able to write this as f((; A, B)) = A + B, which will destructure by name instead.

4 Likes

Well, to me it seems to be exactly what they want as they say:

f((; A, B)) = A + B

I like this. Can you add defaults ?

f((; A=1, B=2)) = A+B
X = (A=2)

>julia f( X )
4

More generally will 1.7 allow

  • Define all arguments with keywords and defaults
  • Call with a named tuple such that:
    elements in function def but not in tuple use default values (or error if default not specified)
    elements in tuple but not function def are ignored
Julia> f((; A=1, B=2)) = A+B
ERROR: syntax: invalid argument destructuring syntax "(; A = 1, B = 2,)" around REPL[84]:1

What has changed in the past 3 years that I have an error now?