Why function receive extra argument - functional-programming

When I run an function with the code below:
elements = ["A", "B"]
for element <- elements, into: [] do
struct(element, [])
end
Elixir raise this exception:
** (FunctionClauseError) no function clause matching in Kernel.struct/3
The following arguments were given to Kernel.struct/3:
# 1
"A"
# 2
[]
# 3
#Function<18.114860832/2 in Kernel.struct/2>
Attempted function clauses (showing 4 out of 4):
defp struct(struct, [], _fun) when is_atom(struct)
defp struct(struct, fields, fun) when is_atom(struct)
defp struct(%_{} = struct, [], _fun)
defp struct(%_{} = struct, fields, fun)
Why Elixir pass an function as third parameter to Kernel.struct function?

The reason you are seeing Kernel.struct/3 is that Kernel.struct/2 calls a private Kernel.struct/3 clause internally.
The reason for the error, that there is no matching function clause, is because the first argument "A" is not a struct or an atom. Please read the documentation for struct/2:
The struct argument may be an atom (which defines defstruct) or a struct itself.
You are passing a string, which is not a valid argument.

Related

Trying to pass an array into a function

I'm very new to Julia, and I'm trying to just pass an array of numbers into a function and count the number of zeros in it. I keep getting the error:
ERROR: UndefVarError: array not defined
I really don't understand what I am doing wrong, so I'm sorry if this seems like such an easy task that I can't do.
function number_of_zeros(lst::array[])
count = 0
for e in lst
if e == 0
count + 1
end
end
println(count)
end
lst = [0,1,2,3,0,4]
number_of_zeros(lst)
There are two issues with your function definition:
As noted in Shayan's answer and Dan's comment, the array type in Julia is called Array (capitalized) rather than array. To see:
julia> array
ERROR: UndefVarError: array not defined
julia> Array
Array
Empty square brackets are used to instantiate an array, and if preceded by a type, they specifically instantiate an array holding objects of that type:
julia> x = Int[]
Int64[]
julia> push!(x, 3); x
1-element Vector{Int64}:
3
julia> push!(x, "test"); x
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
Thus when you do Array[] you are actually instantiating an empty vector of Arrays:
julia> y = Array[]
Array[]
julia> push!(y, rand(2)); y
1-element Vector{Array}:
[0.10298669573927233, 0.04327245960128345]
Now it is important to note that there's a difference between a type and an object of a type, and if you want to restrict the types of input arguments to your functions, you want to do this by specifying the type that the function should accept, not an instance of this type. To see this, consider what would happen if you had fixed your array typo and passed an Array[] instead:
julia> f(x::Array[])
ERROR: TypeError: in typeassert, expected Type, got a value of type Vector{Array}
Here Julia complains that you have provided a value of the type Vector{Array} in the type annotation, when I should have provided a type.
More generally though, you should think about why you are adding any type restrictions to your functions. If you define a function without any input types, Julia will still compile a method instance specialised for the type of input provided when first call the function, and therefore generate (most of the time) machine code that is optimal with respect to the specific types passed.
That is, there is no difference between
number_of_zeros(lst::Vector{Int64})
and
number_of_zeros(lst)
in terms of runtime performance when the second definition is called with an argument of type Vector{Int64}. Some people still like type annotations as a form of error check, but you also need to consider that adding type annotations makes your methods less generic and will often restrict you from using them in combination with code other people have written. The most common example of this are Julia's excellent autodiff capabilities - they rely on running your code with dual numbers, which are a specific numerical type enabling automatic differentiation. If you strictly type your functions as suggested (Vector{Int}) you preclude your functions from being automatically differentiated in this way.
Finally just a note of caution about the Array type - Julia's array's can be multidimensional, which means that Array{Int} is not a concrete type:
julia> isconcretetype(Array{Int})
false
to make it concrete, the dimensionality of the array has to be provided:
julia> isconcretetype(Array{Int, 1})
true
First, it might be better to avoid variable names similar to function names. count is a built-in function of Julia. So if you want to use the count function in the number_of_zeros function, you will undoubtedly face a problem.
Second, consider returning the value instead of printing it (Although you didn't write the print function in the correct place).
Third, You can update the value by += not just a +!
Last but not least, Types in Julia are constantly introduced with the first capital letter! So we don't have an array standard type. It's an Array.
Here is the correction of your code.
function number_of_zeros(lst::Array{Int64})
counter = 0
for e in lst
if e == 0
counter += 1
end
end
return counter
end
lst = [0,1,2,3,0,4]
number_of_zeros(lst)
would result in 2.
Additional explanation
First, it might be better to avoid variable names similar to function names. count is a built-in function of Julia. So if you want to use the count function in the number_of_zeros function, you will undoubtedly face a problem.
Check this example:
function number_of_zeros(lst::Array{Int64})
count = 0
for e in lst
if e == 0
count += 1
end
end
return count, count(==(1), lst)
end
number_of_zeros(lst)
This code will lead to this error:
ERROR: MethodError: objects of type Int64 are not callable
Maybe you forgot to use an operator such as *, ^, %, / etc. ?
Stacktrace:
[1] number_of_zeros(lst::Vector{Int64})
# Main \t.jl:10
[2] top-level scope
# \t.jl:16
Because I overwrote the count variable on the count function! It's possible to avoid such problems by calling the function from its module:
function number_of_zeros(lst::Array{Int64})
count = 0
for e in lst
if e == 0
count += 1
end
end
return count, Base.count(==(1), lst)
The point is I used Base.count, then the compiler knows which count I mean by Base.count.

How to add an action to an item in the GtkListStore using Julia?

I need to attach an action to the row in the list. The code below displays a window with a list, but the activation of a row (double click on it) leads to an error:
ls = GtkListStore(String, Int)
push!(ls,("Peter",20))
push!(ls,("Paul",30))
push!(ls,("Mary",25))
tv = GtkTreeView(GtkTreeModel(ls))
rTxt = GtkCellRendererText()
c1 = GtkTreeViewColumn("Name", rTxt, Dict([("text",0)]))
c2 = GtkTreeViewColumn("Age", rTxt, Dict([("text",1)]))
push!(tv, c1, c2)
signal_connect(tv_row_activated, tv, "row-activated")
win = GtkWindow(tv, "List View")
showall(win)
function tv_row_activated(w)
println("Works")
end
As the error message says, it is expecting the callback method to be tv_row_activated(::GtkTreeViewLeaf, ::Gtk.GLib.GBoxedUnknown, GtkTreeViewColumnLeaf) i.e. the callback method should accept three arguments. Currently yours accepts a single argument w (mentioned under "Closest candidates" as tv_row_activated(::Any)).
Looking at the signature of "row-activated" in the underlying C Gtk library,
void
row_activated (
GtkTreeView* self,
GtkTreePath* path,
GtkTreeViewColumn* column,
gpointer user_data
)
it seems that the arguments passed to the callback in Julia might be the first three arguments mentioned here (though I'm not familiar enough with the library to say that for sure).
Gtk is attempting to call your tv_row_activated function with three arguments:
An argument of type GtkTreeViewLeaf
An argument of type Gtk.Glib.BoxedUnknown
An argument of type GtkTreeViewColumnLeaf
As defined your function tv_row_activated takes a single argument, w.
Since you appear to be trying to debug or explore, I suggest redefining tv_row_activated to take any number of arguments as follows:
function tv_row_activated(w...)
println("Works")
end
Let's test this in the REPL:
julia> function tv_row_activated(w...)
println(w)
println(typeof.(w))
println("Works")
end
tv_row_activated (generic function with 1 method)
julia> tv_row_activated(1,2,3)
(1, 2, 3)
(Int64, Int64, Int64)
Works
julia> tv_row_activated(nothing)
(nothing,)
(Nothing,)
Works
julia> tv_row_activated(5.0)
(5.0,)
(Float64,)
Works

How to instantiate a struct in Julia where I have the struct name in a string variable?

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

Julia pass optional parameter to inner function call

I have something like the following
function test(; testvar=nothing)
# only pass testvar to function if it has a useful value
inner_function(testvar != nothing ? testvar2=testvar : # leave it out)
end
# library function, outside my control, testvar2 can't be nothing
function inner_function(; testvar2=useful value)
# do something
end
I know I can use if/else statements within test() but inner_function has lots of parameters so I would prefer to avoid that from a code duplication standpoint. Is this possible?
Note: inner_function cannot have testvar2 = nothing, if testvar2 is passed it has to have a valid value.
As #elsuizo points out, multiple dispatch is one of the core julia features which allows you to perform such operations. Find bellow a quick example:
>>> inner_func(x) = true;
>>> inner_func(::Type{Void}) = false;
Note: it seems Nothing has been renamed to Void (as a warning prompts out when I try to use it).
inner_func has 2 method definitions, if Void is passed as a parameter, the function will just return false (change this behaviour by do nothing or whatever you want to do). Instead, if the function receives anything else than Void, it will just do something else (in this case, return true).
Your test wouldn't have to perform any check at all. Just pass the parameters to the inner_func, and it will decide what to do (which of the 2 methods of the function inner_func to call) depending on the parameter type.
An example:
>>> a = [1, 2, 3, Void, 5];
>>> filter(inner_func, a)
4-element Array{Any,1}:
1
2
3
5
For the numerical elements, the function calls the method of the function that returns true, and thus, the numerical elements are returned. For the Void element, the second method of the function is called, returning false, and thus, not returning such element.

Keyword argument when instantiating a Type

Suppose I have the following type:
type Foo
a::Int64
b::Int64
end
I can instantiate this with
bar = Foo(1,2)
Is there a way to use keywords here, because in the above I have to remember that a is first, and b is second. Something like this:
bar = Foo(a=1, b=2)
Edit:
The solution by spencerlyon2 doesn't work if called from the function:
#!/usr/bin/env julia
type Foo
a::Float64
b::Float64
end
function main()
Foo(;a=1, b=2.0) = Foo(a,b)
bar = Foo(a=1, b=2.0)
println(bar.a)
end
main()
Why? Is there a workaround?
Edit 2:
Doesn't work from inside a function:
#!/usr/bin/env julia
type Foo
a::Int64
b::Int64
end
function main()
Foo(;a=1, b=2) = Foo(a,b)
bar = Foo(a=1, b=2)
println(bar.a)
end
main()
but if take it out of the function -- it works:
#!/usr/bin/env julia
type Foo
a::Int64
b::Int64
end
# function main()
Foo(;a=1, b=2) = Foo(a,b)
bar = Foo(a=1, b=2)
println(bar.a)
# end
# main()
Yep, but you will need default values for the arguments:
julia> type Foo
a::Int64
b::Int64
end
julia> Foo(;a=1, b=2) = Foo(a, b)
Foo
julia> Foo(b=10)
Foo(1,10)
julia> Foo(a=40)
Foo(40,2)
julia> Foo(a=100, b=200)
Foo(100,200)
Edit
Let's break down the syntax Foo(;a=1, b=1) = Foo(a, b).
First, defining a function with the same name as a type defines a new constructor for that type. This means we are defining another function that will create objects of type Foo. There is a whole chapter on constructors in the manual, so if that term is unfamiliar to you you should read up on them.
Second, Julia distinguishes between positional and keyword arguments. Positional arguments are the default in Julia. With positional arguments names are assigned to function arguments based on the order in which the arguments were defined and then passed into the function. For example if I define a function f(a, b) = .... I know that the first argument I pass to f will be referred to as a within the body of the function (no matter what the name of the variable is in the calling scope).
Keyword arguments are treated differently in Julia. You give a function's keyword arguments non-default values using the syntax argument=value when calling the function. In Julia you tell the compiler that certain arguments are to be keyword arguments by separating them from the standard positional arguments with a semicolon (;) and giving them default values. For example, if we define g(a; b=4) = ... we can give a a value by making it the first thing passed to g and b a value by saying b=something. If we wanted to call the g function with arguments a=4, b=5 we would write g(4; b=5) (note the ; here can be replaced by a ,, but I have found it helps me remember that b is a keyword argument if I use a ; instead).
With that out of the way, we can finally understand the syntax above:
Foo(;a=1, b=2) = Foo(a, b)
This creates a new constructor with zero positional arguments and two keyword arguments: a and b, where a is given a default value of 1 and b defaults to 2. The right hand side of that function declaration simply takes the a and the b and passes them in order to the default inner constructor (that was defined automatically for us when we declared the type) Foo.
EDIT 2
I figured out the problem you were having when defining a new outer constructor inside a function.
The lines
function main()
Foo(;a=1, b=2.0) = Foo(a,b)
actually create a completely new function Foo that is local to the main function. So, the left hand side creates a new local Foo and the the right hand side tries to call that new local Foo. The problem is that there is not a method defined for the local Foo that takes two positional Int64 arguments.
If you really want to do this you need to tell the main function to add a method to the Foo outer function, by specifying that Foo belongs to the global scope. This works:
function main()
global Foo
Foo(;a=1, b=2.0) = Foo(a,b)
bar = Foo(a=1, b=2.0)
println(bar.a)
end
About using inner constructors. Sure you can do this, but you will also want to define a default inner constructor. This is because if you do not define any new inner constructors, Julia generates a default one for you. If you do decide to create one of your own, then you must create the default constructor by hand if you want to have it. The syntax for doing this is
type Foo
a::Int64
b::Int64
# Default constructor
Foo(a::Int64, b::Int64) = new(a, b)
# our new keyword constructor
Foo(;a::Int64=1, b::Int64=2) = new (a, b)
end
I should note that for this particular use case you almost certainly do not want to define the keyword version as an inner constructor, but rather as an outer constructor like I did at the beginning of my answer. It is convention in Julia to use the minimum number of inner constructors as possible -- using them only in cases where you need to ensure invariant relationships between fields or partially initialize an object.

Resources