Recently started to try and learn Julia through examples. I am basically trying to figure out how to access a struct property from within a function inside the struct itself. E.g:
struct Test
a::Int
foo::Function
function Test()
return new(777, xfoo)
end
function xfoo()
println(a)
end
end
t = Test()
t.foo()
I get:
ERROR: LoadError: UndefVarError: a not defined
Stacktrace:
[1] (::var"#xfoo#1")()
# Main /tmp/j.jl:10
[2] top-level scope
# /tmp/j.jl:15
in expression starting at /tmp/j.jl:15
Am I using Julia wrong or am I missing something?
Julia is not object oriented language so object oriented patterns are usually not a good idea.
Hence xfoo should be outside of Test:
function xfoo(t::Test)
println(t.a)
end
There are packages that try to emulate OOP with Julia (however this is not a Julian pattern): https://github.com/Suzhou-Tongyuan/ObjectOriented.jl
You can also easily find quite a lot of discussion behind the design decision no to make Julia OOP. Start with: https://discourse.julialang.org/t/why-there-is-no-oop-object-oriented-programming-in-julia/86723
Workaround
Just out of curiosity one can find some workaround to attach a function to a struct (not a recommended design pattern!). For an example:
mutable struct MyTest
a::Int
foo::Function
function MyTest()
s = Ref{MyTest}()
s[] = new(777, () -> println(s[].a))
s[]
end
end
And some sample usage:
julia> t = MyTest();
julia> t.foo()
777
julia> t.a = 900;
julia> t.foo()
900
Related
Some of the parameters to a simulation I am writing are functions. When the output is generated, I want to put the definition of these functional parameters in the output. I have in mind a macro that somehow saves the definition as a string, and then defines it. For example, here's what I do now:
borda_score_fn(p) = exp(1/p)
global g_borda_score_fn_string = "exp(1/p)"
And then I write g_borda_score_fn_string to my output. But this is really ugly!
What I would like to do is something like this:
#paramfn borda_score_fn(p) = exp(1/p)
And later be able to both call borda_score_fn(p), and have the form (i.e., "exp(1/p)") available for writing to my output log. (The string form might get stashed in a global dict, actually, they both could.)
I have tried many version of this, but can't get the right set of parses and calls to get it to work. Any help would be appreciated.
This may be a bit different than what you have in mind, but one perhaps "Julian" approach might be to have the function itself return the form string via multiple dispatch, rather than defining a whole new global variable just for that. For example, say we have a type
struct Form end
that we can use for dispatch, then we can write
borda_score_fn(p) = exp(1/p)
borda_score_fn(::Form) = "exp(1/p)"
which can then be retrieved just by calling the function with our type
julia> borda_score_fn(2)
1.6487212707001282
julia> borda_score_fn(Form())
"exp(1/p)"
That might actually be not bad on its own. But, if you want a macro to do both parts at once, then something along the lines of
macro paramfn(e)
name = esc(e.args[1].args[1])
str = string(e.args[2].args[2])
f = esc(e)
quote
$name(::Form) = $str
$f
end
end
would let you write
julia> #paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 2 methods)
julia> borda_score_fn(1)
2.718281828459045
julia> borda_score_fn(Form())
"exp(1 / p)"
For completeness, here's how you can do it in a way more similar to your original approach, but more idiomatically than with a global variable:
julia> module FormOf
export formof, #paramfn
function formof end
macro paramfn(expr)
name = esc(expr.args[1].args[1])
form_str = string(expr.args[2].args[2])
quote
$(esc(expr))
$FormOf.formof(::typeof($name)) = $form_str
$name
end
end
end
Main.FormOf
julia> FormOf.#paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 1 method)
julia> FormOf.formof(borda_score_fn)
"exp(1 / p)"
However, since it defines a new method of FormOf.formof, this only works in global scope:
julia> function bla()
FormOf.#paramfn fn(p) = exp(1/p)
fn(10) + 1
end
ERROR: syntax: Global method definition around REPL[45]:10 needs to be placed at the top level, or use "eval".
Stacktrace:
[1] top-level scope
# REPL[50]:1
#cbk's solution does not have this limitation.
I know that Julia does not have OOP but that multiple dispatch enables similar ideas. Given how seemingly contentious the use of singletons are in Python, I am wondering if there is a similar idea Julia (i.e. a struct that can only be instantiated once).
I am wondering if there's a way to have the constructor keep track of the number of times an object was instantiated with a global var or something like that? Or it's altogether not be possible?
The main way people make singletons in Julia is to define an empty struct (which means that it has 0 size), and define methods that return information for it.
struct Singleton
end
data(::Singleton) = "whatever you want it to do"
etc.
From this book, a singleton can be defined as a type without fields:
struct MySingleton end
julia> MySingleton() === MySingleton()
true
You can also use Val, which can receive any value (of bit type):
julia> Val(1) === Val(1)
true
julia> Val(:foo) === Val(:foo)
true
using Val you can write something like this:
julia> do_something(::Val{:asymbol}) = println("foo")
julia> do_something(::Val{:anothersymbol}) = println("bar")
julia> do_something(s::String) = do_something(Val{Symbol(s)})
julia> do_something("asymbol")
foo
The definition of Val is:
struct Val{x} end
So, for a more clear readability of your code, you could define your own singleton type as, for example:
struct Command{x} end
The name of the struct to instantiate will be passed by the caller to my program. Then I would need to instantiate the corresponding struct for the same for further processing.
For example, if the struct is defined like this
struct A end
and I have a function defined as
function load(struct_name::AbstractString)
if struct_name == "A"
return A()
elseif struct_name == "B"
return B()
elseif ..... # and so on
end
end
it will work. But is there a more direct way like return struct_name() instead of having n number of if else statements? I see that Julia supports reflection. How can that be used to support the above use case?
I would recommend not doing it in production code, but you can do the following:
function load(struct_name::AbstractString)
invoke(eval(Symbol(struct_name)),Tuple{})
end
strut_name via eval will get resolved in the global scope of the module.
It is safer to use a dictionary as #EPo suggested.
An example of dictionary-based dispatch. Dict("a" => A, "b" => B)[tag] selects a constructor, and () calls it.
struct A end
struct B end
function dispatch(tag)
return Dict("a" => A, "b" => B)[tag]()
end
#assert dispatch("a") == A()
If you care about default values to handle unexpected parameter, for example dispatch('zzz'),
you can make recourse to get().
As a side note about risks of eval() there is a small collection of powerful warning references in a neighboring Python question. In short, eval() is a big security hole and a 'smell' (warning sign) for a questionable design of a program.
You could use a macro instead:
julia> module Load
export #load
macro load(struct_name::Symbol)
return :($(esc(struct_name))())
end
end
Main.Load
julia> using Main.Load: #load
julia> struct A end
julia> struct B end
julia> #load A
A()
julia> #macroexpand #load B
:(B())
julia> #load C
ERROR: UndefVarError: C not defined
Stacktrace:
[1] top-level scope at none:0
I want to define a struct:
struct unit_SI_gen
x::Float32
const c = 2.99792458e8
speed(x)=c*x
end
However, it raise an error :
syntax: "c = 2.99792e+08" inside type definition is reserved
I know I cant use struct as class in python, but I can not find how to solve this problem.
How to define a constant in struct ?
Given I agree with what was said above about normal usage of struct in Julia, it is actually possible to define what was requested in the question using an inner constructor:
struct unit_SI_gen{F} # need a parametric type to make it fast
x::Float32
c::Float64 # it is a constant across all unit_SI_gen instances
speed::F # it is a function
function unit_SI_gen(x)
c = 2.99792458e8
si(x) = c*x
new{typeof(si)}(x, c, si)
end
end
I second #Tasos's comment, you should probably make yourself familiar with Julia's structs first. The relevant part of the documentation is probably here.
Since you declared your struct as struct (in contrast to mutable struct) it is immutable and hence all (immutable) fields of the struct are constants in the sense that they can't be changed.
julia> struct A
x::Int
end
julia> a = A(3)
A(3)
julia> a.x = 4
ERROR: type A is immutable
Stacktrace:
[1] setproperty!(::A, ::Symbol, ::Int64) at .\sysimg.jl:19
[2] top-level scope at none:0
Note, that they get their unchangeable value in the construction process and not in the struct definition.
Also, methods should generally live outside of the struct definition.
I'm quite new to Julia and I'm looking into porting some Python code to Julia. This code uses __repr__() overloading to display cutsom types. I understand that Julia provides the string() method for this functionality. But I can't figure it out.
julia> type Thomas
t::Integer
end
julia> function Base.string(t::Thomas)
"---> $(t.t) <---"
end
julia> r = Thomas(8);
With these definitions I expected my string(::Thomas) function to be called whenever a value of type Thomas needed to be converted to a string. In one case, it works as expected:
julia> println("$r")
---> 8 <---
But, for the most cases it does not:
julia> println(r)
Thomas(8)
julia> println(" $r")
Thomas(8)
julia> println("r = $r")
r = Thomas(8)
julia> repr(r)
"Thomas(8)"
What did I get wrong ? Is there some other function I should define for my new custom type ?
I am running Julia 0.4.0-dev. (the code above was pasted from the REPL of Version 0.4.0-dev+3607 (2015-02-26 07:41 UTC), Commit bef6bf3*, x86_64-linux-gnu)
At the moment just overriding Base.show should be enough, as follows.
type Thomas
t::Int # note Int not Integer
end
Base.show(io::IO, x::Thomas) = print(io, "Thomas with $(x.t)")
Note that in the definition of the type, you should use the concrete type Int (equivalent to Int64 or Int32, depending on your machine's word size), not the abstract type Integer, which will lead to bad performance.
The situation with Base.show, Base.print etc. is indeed confusing at the moment, but with some recent work (look up IOContext) should get simplified and clarified soon.
You have to override two versions of Base.print actually to get a consistent behavior of string interpolation:
Base.print(io::IOBuffer, t::Thomas) = Base.print(io, "---> $(t.t) <---")
Base.print(t::Thomas) = Base.print("---> $(t.t) <---")
Then you'll have:
print(t)
string(t)
string(t, t, ...)
"$t"
"t = $t"
"$t $t $t"
and co.
You may want to override the show method as well.
Base.show(io::IO, x::Thomas) = show(io, string(x))