Julia permits function and method definitions with unnamed arguments.
This is not mentioned in the functions documentation, nor is it explicitly discussed in the methods documentation. For example:
function myfunc(::Int)
println("Hello!")
end
How should I describe this behavior (I've googled "anonymous arguments" without success), and when is it useful?
This behavior is useful for method dispatch, when you care only about argument type not argument value. Most often this is a case when what you dispatch on is a singleton type.
An example is:
julia> Vector{String}(undef, 3)
3-element Array{String,1}:
#undef
#undef
#undef
This function is defined in the following way:
Array{T,1}(::UndefInitializer, m::Int) where {T} =
ccall(:jl_alloc_array_1d, Array{T,1}, (Any, Int), Array{T,1}, m)
And you can see that we only care that the first argument was of UndefInitializer type, which is in turn defined as:
struct UndefInitializer end
const undef = UndefInitializer()
We see that UndefInitializer is a singleton type, so we do not care about the value of a variable of this type, but only about its type.
Another common singleton type in Base is Missing. Here are example definitions from Base of standard functions getting a Missing as an argument:
for f in (:(acos), :(acosh), :(asin), :(asinh), :(atan), :(atanh),
:(sin), :(sinh), :(cos), :(cosh), :(tan), :(tanh),
:(exp), :(exp2), :(expm1), :(log), :(log10), :(log1p),
:(log2), :(exponent), :(sqrt))
#eval $(f)(::Missing) = missing
end
(again - you can see that we do not care about the value of the variable - we know its type is Missing so we return missing)
In the Julia manual you have examples of such methods e.g. here but admittedly as far as I can tell the manual does not give a name for this style of method definition.
Related
Copied this into a jupyter notebook cell but can't get it to run and the message doesn't really help. Everything looks right.
mutable struct CircularArray{T} <: AbstractArray{T,1}
data::Array{T,1}
first::Int
CircularArray{T}(length::Int) where {T} = new{T}(Array{T, 1}(undef, length), 1)
end
a = CircularArray(10)
MethodError: no method matching CircularArray(::Int64)
I think the error is clear: you need to define CircularArray(length::Int). What you implemented, however, is a parametric constructor. To call your parametric constructor, you need to pass the parameter T with your constructor call, e.g.
a = CircularArray{Float64}(10);
You can also implement non-parametric constructor for a default type of your choice. For example;
CircularArray(length::Int) = CircularArray{Float64}(length)
After this your call to this constructor, CircularArray(10);, won't give a MethodError: no method matching CircularArray(::Int64).
Note the ; at the end of the commands. You need to define other methods (like size) for your array type so that display can work. Otherwise, you may get an error in REPL if you omit ; after the evaluations that return a CircularArray.
I want to generate a Dict with undef values so I can later loop over the keys and fill in correct values. I can initialise an such a Dict using concrete types in the following way and it all works fine:
currencies = ["USD", "AUD", "GBP"]
struct example
num::Float64
end
undef_array = Array{example}(undef,3)
Dict{String,example}(zip(currencies, undef_array))
When my struct has an abstract type however I can still generate the undef array but I cannot create the dict. I get an error "UndefRefError: access to undefined reference"
abstract type abstract_num end
struct example2
num::abstract_num
end
undef_array = Array{example2}(undef,3)
Dict{String,example2}(zip(currencies, undef_array))
Although it is possible to create such a Dict with a concrete array:
struct numnum <: abstract_num
num::Float64
end
def_array = [example2(numnum(5.0)), example2(numnum(6.0)), example2(numnum(4.5))]
Dict{String,example2}(zip(currencies, def_array))
Question
My question is whether it is possible to generate a Dict with undef values of a type that relies on an abstract type? Is it is possible what is the best way to do it?
In your second (not working) example, undef_array is an array whos elements aren't initialized:
julia> undef_array = Array{example2}(undef,3)
3-element Array{example2,1}:
#undef
#undef
#undef
The reason is that it's not possible to instantiate an object of type example2 because your abstract type abstract_num (the type of the field of example2) doesn't have any concrete subtypes and, thus, can't be instantiated either. As a consequence even indexing undef_array[1] gives an UndefRefError and, hence, also zip won't work.
Compare this to the first case where the array elements are (arbitrarily) initialized:
julia> undef_array = Array{example}(undef,3)
3-element Array{example,1}:
example(1.17014136e-315)
example(1.17014144e-315)
example(1.17014152e-315)
and undef_array[1] works just fine.
Having said that, I'm not really sure what you try to achieve here. Why not just create a mydict = Dict{String, example2}() and fill it with content when the time comes? (As said above, you would have to define concrete subtypes of abstract_num first)
For performance reasons you should, in general, avoid creating types with fields of an abstract type.
Try:
a=Dict{String,Union{example3,UndefInitializer}}(currencies .=>undef)
However, for representing missing values the type Missing is usually more appropriate:
b=Dict{String,Union{example3,Missing}}(currencies .=>missing)
Please note that typeof(undef) yields UndefInitializer while typeof(missing) yields Missing - hence the need for Union types in the Dict. The dot (.) you can see above (.=>) is the famous Julia dot operator.
Moreover, I recommend to keep to Julia's naming conversion - struct and DataType names should start with a Capital Letter.
Last but not least, in your first example where concrete type Float64 was given, Julia has allocated the array to some concrete address in memory - beware that it can contain some garbage data (have a look at console log below):
julia> undef_array = Array{example}(undef,3)
3-element Array{example,1}:
example(9.13315366e-316)
example(1.43236026e-315)
example(1.4214423e-316)
The problem is the following:
I have an abstract type MyAbstract and derived composite types MyType1 and MyType2:
abstract type MyAbstract end
struct MyType1 <: MyAbstract
somestuff
end
struct MyType2 <: MyAbstract
someotherstuff
end
I want to specify some general behaviour for objects of type MyAbstract, so I have a function
function dosth(x::MyAbstract)
println(1) # instead of something useful
end
This general behaviour suffices for MyType1 but when dosth is called with an argument of type MyType2, I want some additional things to happen that are specific for MyType2 and, of course, I want to reuse the existing code, so I tried the following, but it did not work:
function dosth(x::MyType2)
dosth(x::MyAbstract)
println(2)
end
x = MyType2("")
dosth(x) # StackOverflowError
This means Julia did not recognize my attempt to treat x like its "supertype" for some time.
Is it possible to call an overloaded function from the overwriting function in Julia? How can I elegantly solve this problem?
You can use the invoke function
function dosth(x::MyType2)
invoke(dosth, Tuple{MyAbstract}, x)
println(2)
end
With the same setup, this gives the follow output instead of a stack overflow:
julia> dosth(x)
1
2
There's a currently internal and experimental macro version of invoke which can be called like this:
function dosth(x::MyType2)
Base.#invoke dosth(x::MyAbstract)
println(2)
end
This makes the calling syntax quite close to what you wrote.
I often see something like the following in Julia:
convert(::Type{Point{Float64}}, ::Float64)
how does the (:: work? And what is the terminology for this?
Your answer can be found in the Julia documentation for defining conversions. To quote (with types switched to make it even more straightforward to read):
The type of the first argument of this method is a singleton
type, Type{Point{Float64}}, the only instance of which is
Point{Float64}. Thus, this method is only invoked when the first
argument is the type value Point{Float64}. Notice the syntax used
for the first argument: the argument name is omitted prior to the ::
symbol, and only the type is given. This is the syntax in Julia for a
function argument whose type is specified but whose value is never
used in the function body. In this example, since the type is a
singleton, there would never be any reason to use its value within the
body.
(Emphasis mine)
You will also encounter the foo(::SomeType) syntax in error messages, when trying to invoke a function with arguments of the wrong type (after all you can't show the argument names of a variant that does not exist). E.g:
julia> foo(x::Bool) = 3
foo (generic function with 1 method)
julia> foo(5)
ERROR: `foo` has no method matching foo(::Int64)
In Julia, is there any way to get the name of a passed to a function?
x = 10
function myfunc(a)
# do something here
end
assert(myfunc(x) == "x")
Do I need to use macros or is there a native method that provides introspection?
You can grab the variable name with a macro:
julia> macro mymacro(arg)
string(arg)
end
julia> #mymacro(x)
"x"
julia> #assert(#mymacro(x) == "x")
but as others have said, I'm not sure why you'd need that.
Macros operate on the AST (code tree) during compile time, and the x is passed into the macro as the Symbol :x. You can turn a Symbol into a string and vice versa. Macros replace code with code, so the #mymacro(x) is simply pulled out and replaced with string(:x).
Ok, contradicting myself: technically this is possible in a very hacky way, under one (fairly limiting) condition: the function name must have only one method signature. The idea is very similar the answers to such questions for Python. Before the demo, I must emphasize that these are internal compiler details and are subject to change. Briefly:
julia> function foo(x)
bt = backtrace()
fobj = eval(current_module(), symbol(Profile.lookup(bt[3]).func))
Base.arg_decl_parts(fobj.env.defs)[2][1][1]
end
foo (generic function with 1 method)
julia> foo(1)
"x"
Let me re-emphasize that this is a bad idea, and should not be used for anything! (well, except for backtrace display). This is basically "stupid compiler tricks", but I'm showing it because it can be kind of educational to play with these objects, and the explanation does lead to a more useful answer to the clarifying comment by #ejang.
Explanation:
bt = backtrace() generates a ... backtrace ... from the current position. bt is an array of pointers, where each pointer is the address of a frame in the current call stack.
Profile.lookup(bt[3]) returns a LineInfo object with the function name (and several other details about each frame). Note that bt[1] and bt[2] are in the backtrace-generation function itself, so we need to go further up the stack to get the caller.
Profile.lookup(...).func returns the function name (the symbol :foo)
eval(current_module(), Profile.lookup(...)) returns the function object associated with the name :foo in the current_module(). If we modify the definition of function foo to return fobj, then note the equivalence to the foo object in the REPL:
julia> function foo(x)
bt = backtrace()
fobj = eval(current_module(), symbol(Profile.lookup(bt[3]).func))
end
foo (generic function with 1 method)
julia> foo(1) == foo
true
fobj.env.defs returns the first Method entry from the MethodTable for foo/fobj
Base.decl_arg_parts is a helper function (defined in methodshow.jl) that extracts argument information from a given Method.
the rest of the indexing drills down to the name of the argument.
Regarding the restriction that the function have only one method signature, the reason is that multiple signatures will all be listed (see defs.next) in the MethodTable. As far as I know there is no currently exposed interface to get the specific method associated with a given frame address. (as an exercise for the advanced reader: one way to do this would be to modify the address lookup functionality in jl_getFunctionInfo to also return the mangled function name, which could then be re-associated with the specific method invocation; however, I don't think we currently store a reverse mapping from mangled name -> Method).
Note also that (1) backtraces are slow (2) there is no notion of "function-local" eval in Julia, so even if one has the variable name, I believe it would be impossible to actually access the variable (and the compiler may completely elide local variables, unused or otherwise, put them in a register, etc.)
As for the IDE-style introspection use mentioned in the comments: foo.env.defs as shown above is one place to start for "object introspection". From the debugging side, Gallium.jl can inspect DWARF local variable info in a given frame. Finally, JuliaParser.jl is a pure-Julia implementation of the Julia parser that is actively used in several IDEs to introspect code blocks at a high level.
Another method is to use the function's vinfo. Here is an example:
function test(argx::Int64)
vinfo = code_lowered(test,(Int64,))
string(vinfo[1].args[1][1])
end
test (generic function with 1 method)
julia> test(10)
"argx"
The above depends on knowing the signature of the function, but this is a non-issue if it is coded within the function itself (otherwise some macro magic could be needed).