I see that structs can have constructors, but can they also contain functions? I tried
julia> mutable struct b; x::Int64; function y() x=22; end; end
which gave no error, but b.y()
does not work.
I see that structs can have constructors, but can they also contain functions? I tried
julia> mutable struct b; x::Int64; function y() x=22; end; end
which gave no error, but b.y()
does not work.
Sure, you just can’t initialize them like that:
julia> struct Foo
f
end
julia> F = Foo(x -> 2x)
Foo(#9)
julia> F.f(3)
6
For this to be fast you’d want to parametrize Foo
on the type of f
though:
struct Foo{T}
f::T
end
Just noticed that you probably wanted your y
to mutate b.x
or be a static method or something, so the above is probably irrelevant.
thanks, pfitzseb. alas, I did not mean just the ability for structs to contain functions. I meant member functions with convenient default access to the contents of the struct (aka, C++ member functions). I can accomplish the same with
function y(Foo foo)
foo.x=22
end#function y
## not, as in C++, grouped inside the struct, and with access to member [this->x]
struct Foo
x::Int64
function y() x=22; end#function
end#struct
so, it is more of a style question, coming from C++.
Short answer: No. You need to pass the this
-pointer (mutables are de-facto pointers) explicitly, i.e. define
function y(some_b::b) some_b.x=22; end;
If this isn’t about syntactic sugar but name-spaces, consider writing a module where you would otherwise write a class with lots of methods (and then just don’t export the stuff you consider private).
Or will this end up about virtual functions / OOP? Then it is complicated and you need to plan somewhat differently in julia.
Inner constructors are AFAIK really only meant for immutable structs.
edit: Forget about the pointer/mutable/immutable, you always need to pass this
explicitly, regardless of whether it is mutable or not. C
-style.
You could do the following…
function Foo()
x::Int = -1
setx(x_) = (x = x_)
getx() = x
() -> (getx;setx)
end
f = Foo()
f.getx()
f.setx(22)
f.getx()
f.setx("22") # (fails)
@vvjn do you know, whether the fact, that getx
and setx
are fields of f
is just an implementation detail, that may go away at any point, or if this is behaviour that one can rely on?
I’m not completely sure but I don’t think this will go away. Besides, in 1.0 you will be able to overload the .
operator. So, this can be replicated pretty easily by overload .
.
And btw, this style is not really recommended for julia since it doesn’t look very pretty to most julia people and you can’t perform dispatch on the resulting object.
I came across a similar discussion on stack overflow that might be of interest.
My understanding is that object methods are not part of the design, and won’t be in order to preserve the benefits of multiple dispatch. There’s much written on this out there even in the language doc (see here in Methods for a start) .
Don’t do this!
using BenchmarkTools
function Foo()
x::Int = -1
setx(x_) = (x = x_)
getx() = x
() -> (getx;setx)
end
f = Foo()
function fob(f, x)
f.setx(x)
end
mutable struct Foo2
x::Int64
end
function setx(a::Foo2, v)
a.x=v
end
foo=Foo2(4)
@btime fob($f, $22)
72.118 ns
@btime setx($foo, $22)
2.209 ns
Alternatively, read the @code_native
or @code_llvm
generated by this abomination to see why it is not nice.
the stack overflow example is interesting and unexpected, so let me repeat it:
julia> function Person(name, age)
getName() = name
getAge() = age
getOlder() = (age+=1)
()->(getName;getAge;getOlder)
end
Person (generic function with 1 method)
julia> o = Person("bob", 26)
(::#3) (generic function with 1 method)
julia> o.getName()
"bob"
julia> o.getAge()
26
julia> o.getOlder()
27
julia> o.getAge()
27
as to my original question, I am perfectly happy with julia’s way of doing things, so no object-orientation ala C++ or Java. I just wanted to be sure that I am using julia as intended.
Got curious, and it looks like f.setx is faster than python method calling.
class Foo:
def __init__(self):
self.x = -1
def setx(self, x_):
self.x = x_
if __name__ == '__main__':
import timeit
n = 10000000
t = timeit.timeit("f.setx(22)", setup="from __main__ import Foo; f = Foo()", number=n)
print(str((t/n)/1e-9) + " ns")
Above gives me around 100ns, while running @btime fob($f, $22)
from your code gives me around 40ns.
I strongly suggest you read this and this before designing anything that contains struct
s with functions as members. Multiple dispatch is a beautiful paradigm for writing code, it’s definitely worth getting familiar with it and how Julia instantiates it. I came to Julia for the performance, but it’s probably this more than anything else that keeps me not wanting to ever use another language. Sometimes it make sense to have functions as struct
members even within the paradigm of multiple dispatch, but usually it doesn’t.
Also bear in mind that Julia 0.7 is slated to have simple get/set accessors for struct fields. So, there is little point in writing lots of those.
Mainly I second @ExpandingMan - each language will have slightly different usage patterns, and Multiple Dispatch is amazing in use.
You could also do this…
type Foo
x::Int
setx
getx
function Foo(x)
f = new(x)
f.setx = (x_) -> (f.x = x_)
f.getx = () -> f.x
f
end
end
setx
and getx
are still slow but faster than the function one, though x
is modifiable now.
In 0.7, this will be:
mutable struct Foo
x::Int
end
function Base.setproperty!(f::Foo, v, s::Symbol)
if s == :x
f.x = v
else
error("unknown property $s")
end
end
function Base.getproperty(f::Foo, s::Symbol)
if s == :x
return f.x
else
error("unknown property $s")
end
end
And will have no performance penalty.
I’m curious, this would seem to imply that Julia runs the setproperty!
and getproperty
functions at compile time. Otherwise, there certainly would be a performance penalty because running getproperty
at run-time and checking a conditional structure would certainly be slower than simply accessing a field.
Is that indeed what happens, getproperty
and setproperty!
will run at compile time?
The functions themselves can’t be run at compile time (you won’t know what e.g. f.x
is when you compile) but all the branches and stuff will be gotten rid of since the symbol is a constant, and 0.7 have new fancy constant propagations spanning function boundaries.
Hi, could you update the link for your second reference were it is says “this” (101) ? It appears the link is broken. thanks in advance !
It seems discourse removes the ability to edit very old posts. Here is the updated url to the Julia manual section on methods.