Do julia structs have member functions (and this->)

thanks !

Old post, but could not find a more recent example.
In oo-lingo, closures getx and setx are made to “public methods”.

() → (getx;setx)

I understand something like “tuple”, “map”, “tuple of functions”, but not the syntax.
Could someone please translate the working of this line into plain English?
Which part of the doc to consult for such an expression?
Thx

This defines an anonymous function with zero arguments. Calling it returns a named tuple containing the functions getx and setx.

I.e. you’d use it like this:

julia> f = Foo()

julia> f().getx()
-1

julia> f().setx(1)
1

julia> f().getx()
1

https://docs.julialang.org/en/v1/manual/functions/#man-anonymous-functions

1 Like

Thanks @Sukera.

This defines an anonymous function with zero arguments.

Right

Calling it returns a named tuple containing the functions getx and setx .

Can’t follow here.
While the dot notation is also available for named tuples, I only know their definition as (name1 = val1, ).
How get the nested functions attached to the outer function?

Don’t want to be too fussy, if it’s just the way it works, ok. Maybe there is something basic to learn.

They’re not attached at all! :smiley: They’re defined in Foo and capture the variable x, i.e. they keep a reference to x. Finally, the last anonymous function captures (keeps a reference) to both getx and setx, which can be retrieved due to the trick of having them saved in a named tuple. Finally, Foo()().getx is the same as f = Foo(); vars = f(); getx_func = vars.getx; getx_func(). Internally, it’s a little more complicated because anonymous functions are basically callable structs, which are defined before Foo during lowering - but that’s just an implementation detail.

2 Likes

I think it’s fine to find this confusing. (getx;setx) does not create a named tuple, it just returns setx. With a recent enough Julia (;getx, setx) does create a named tuple though. Compare these implementations:

julia> function Foo1()
             x::Int = -1
             setx(x_) = (x = x_) 
             getx() = x
             () -> (getx;setx)
       end
Foo1 (generic function with 1 method)

julia> function Foo2()
             x::Int = -1
             setx(x_) = (x = x_) 
             getx() = x
             () -> (;getx, setx)
       end
Foo2 (generic function with 1 method)

julia> f1=Foo1()
#27 (generic function with 1 method)

julia> f2=Foo2()
#31 (generic function with 1 method)

julia> f2().getx()
-1

julia> f1().getx()
ERROR: type #setx#28 has no field getx
Stacktrace:
 [1] getproperty(x::Function, f::Symbol)
   @ Base ./Base.jl:33
 [2] top-level scope
   @ REPL[58]:1

However, the magic of the original example is that you in both cases can do

julia> f2.getx()
-1

julia> f1.getx()
-1
1 Like

Related: How to understand this code that seemingly creates an anonymous type on the fly? - #2 by rdeits

2 Likes

I like the explicit syntax using named tuples!

function Foo()
    x = -1 # should make it Int
    setx(xnew) = (x = Int(xnew)) 
    getx() = x
    (; getx, setx)
end

julia> f = Foo()
(getx = var"#getx#18"(Core.Box(-1)), setx = var"#setx#17"(Core.Box(-1)))

Is the Box(ing) above a bad sign for performance?
The compiler should be informed enough about types. Why does it appear?

The absolute horror of that.