How to add an action to an item in the GtkListStore using Julia? - 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

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.

Julia: Parametric types with inner constructor: new and typeof

Trying to understand parametric types and the new function available for inner methods. The manual states "special function available to inner constructors which created a new object of the type". See the section of the manual on new here and the section of the manual on inner constructor methods here.
Consider an inner method designed to calculate the sum of x, where x could be, say, a vector or a tuple, and is given the parametric type T. A natural thing to want is for the type of the elements of x to be inherited by their sum s. I don't seem to need new for that, correct?
struct M{T}
x::T
s
function M(x)
s = sum(x)
x,s
end
end
julia> M([1,2,3])
([1, 2, 3], 6)
julia> M([1.,2.,3.])
([1.0, 2.0, 3.0], 6.0)
julia> typeof(M([1.,2.,3.]))
Tuple{Vector{Float64}, Float64}
Edit: Correction! I intended to have the last line of the inner constructor be M(x,s)... It's still an interesting question, so I won't correct it. How does M(x,s) differ from new{typeof(x)}(x,s)?
One usage of new I have seen is in combination with typeof(), something like:
struct M{T}
x::T
s
function M(x)
s = sum(x)
new{typeof(x)}(x,s)
end
end
julia> M([1,2,3])
M{Vector{Int64}}([1, 2, 3], 6)
julia> M([1.,2.,3.])
M{Vector{Float64}}([1.0, 2.0, 3.0], 6.0)
What if wanted to constrain s to the same type as x? That is, for instance, if x is a vector, then s should be a vector (in this case, a vector of one element). How would I do that? If I replace the last line of the inner constructor with x, new{typeof(x)}(s), I get the understandable error:
MethodError: Cannot `convert` an object of type Int64 to an object of type Vector{Int64}
Here are the rules:
If you are writing an outer constructor for a type M, the constructor should return an instance of M by eventually calling the inner constructor, like this: M(<args>).
If you are writing an inner constructor, this will override the default inner constructor. So you must return an instance of M by calling new(<args>).
The new "special function" exists to allow the construction of a type that doesn't have a constructor yet. Observe the following example:
julia> struct A
x::Int
function A(x)
A(x)
end
end
julia> A(4)
ERROR: StackOverflowError:
Stacktrace:
[1] A(::Int64) at ./REPL[3]:4 (repeats 79984 times)
This is a circular definition of the constructor for A, which results in a stack overflow. You cannot pull yourself up by your bootstraps, so Julia provides the new function as a way to circumvent this problem.
You should provide the new function with a number of arguments equal to the number of fields in your struct. Note that the new function will attempt to convert the types of its inputs to match the declared types of the fields of your struct:
julia> struct B
x::Float64
B(x) = new(x)
end
julia> B(5)
B(5.0)
julia> B('a')
B(97.0)
julia> B("a")
ERROR: MethodError: Cannot `convert` an object of type String to an object
of type Float64
(The inner constructor for B above is exactly the same as the default inner constructor.)
When you're defining parametric types, the new function must be provided with a number of parameters equal to the number of parameters for your type (and in the same order), analogously to the default inner constructor for parametric types. First observe how the default inner constructor for parametric types is used:
julia> struct Foo{T}
x::T
end
julia> Foo{String}("a")
Foo{String}("a")
Now if you were writing an inner constructor for Foo, instead of writing Foo{T}(x) inside the constructor, you would replace the Foo with new, like this: new{T}(x).
You might need typeof to help define the constructor, but often you don't. Here's one way you could define your M type:
struct M{I, T}
x::I
s::T
function M(x::I) where I
s = sum(x)
new{I, typeof(s)}(x, s)
end
end
I'm using typeof here so that I could be any iterable type that returns numbers:
julia> typeof(M(1:3))
M{UnitRange{Int64},Int64}
julia> g = (rand() for _ in 1:10)
Base.Generator{UnitRange{Int64},var"#5#6"}(var"#5#6"(), 1:10)
julia> typeof(M(g))
M{Base.Generator{UnitRange{Int64},var"#5#6"},Float64}
Note that providing the parameters for your type is required when you are using new inside an inner constructor for a parametric type:
julia> struct C{T}
x::Int
C(x) = new(x)
end
ERROR: syntax: too few type parameters specified in "new{...}" around REPL[6]:1
Remember, a constructor is designed to construct something. Specifically, the constructor M is designed to construct a value of type M. Your example constructor
struct M{T}
x::T
s
function M(x)
s = sum(x)
x,s
end
end
means that the result of evaluating the expression M([1 2 3]) is a tuple, not an instance of M. If I encountered such a constructor in the wild, I'd assume it was a bug and report it. new is the internal magic that allows you to actually construct a value of type M.
It's a matter of abstraction. If you just want a tuple in the first place, then forget about the structure called M and just define a function m at module scope that returns a tuple. But if you intend to treat this as a special data type, potentially for use with dynamic dispatch but even just for self-documentation purposes, then your constructor should return a value of type M.

ambuigity on function call in julia

I have this error
ERROR: MethodError: vcat(::Array{Real,2}, ::TrackedArray{…,Array{Float32,2}}) is ambiguous. Candidates:
vcat(364::AbstractArray, x::Union{TrackedArray, TrackedReal}, xs::Union{Number, AbstractArray}...) in Tracker at C:\Users\Henri\.julia\packages\Tracker\6wcYJ\src\lib\array.jl:167
vcat(A::Union{AbstractArray{T,2}, AbstractArray{T,1}} where T...) in Base at abstractarray.jl:1296
Possible fix, define
vcat(::Union{AbstractArray{T,2}, AbstractArray{T,1}} where T, ::Union{TrackedArray{T,1,A} where A<:AbstractArray{T,1} where T, TrackedArray{T,2,A} where A<:AbstractArray{T,2} where T}, ::Vararg{Union{AbstractArray{T,2}, AbstractArray{T,1}} where T,N} where N)
Telling me that two vcat() functions are ambiguous. I want to use the Base.vcat() function but using it explicitly throws the same error. Why is that ? And what is this "possible fix" proposed by the error throw?
Moreover, when I call manually each line in the REPL no error is thrown. I do not understand this behavior. This only happens when vcat() is in a function called inside another function. Like in my example below.
Here is a code that reproduces the error:
using Flux
function loss(a, b, net, net2)
net2(vcat(net(a),a))
end
function test()
opt = ADAM()
net = Chain(Dense(3,3))
net2 = Chain(Dense(6,1))
L(a, b) = loss(a, b, net, net2)
data = tuple(rand(3,1), rand(3,1))
xs = Flux.params(net)
gs = Tracker.gradient(() -> L(data...), xs)
Tracker.update!(opt, xs, gs)
end
As mentionned in comments with Henri.D, we've managed to fix it by being carreful with the type of a which was an Array of Float64, default type returned by rand whereas net(a) returned a TrackedArray of Float32 and made impossible to vcat it with a.
I've managed to fix vcat by changing your loss function with this: net2(vcat(net(a),Float32.(a))) because vcat couldn't concatenate as net(a) was a Float32 Array and a a Float64 one. Then L(data...) is a TrackedArray of 1 element whereas I think you need a Float32 that's why I finally replace loss function by net2(vcat(net(a),Float32.(a)))[1]

Parametric return types

I'm trying to understand how parametric types work in Julia.
Suppose I have a function foo, which takes an Integer (i.e. non-parametric type), and I want to assert this function to return a vector, whose elements are subtypes of Real. From documentation I deduced that it should be implemented as follows:
function foo{T<:Real}(n::Integer)
# come code to generate variable vec
return vec::Vector{T}
end
But it is not working. What am I doing wrong?
Parametric types are used to parameterise the inputs to a function, not the outputs. If your function is type-stable (read about what that means in the performance tips), then the output of the function can be automatically inferred by the compiler based on the input types, so there should be no need to specify it.
So your function might look something like this:
function foo{T<:Real}(n::T)
#some code to generate vec
return(vec)
end
For example, you can generate a vector of the appropriate type within the body of your function, e.g. something like this:
function f1{T<:Number}(x::T)
y = Array(T, 0)
#some code to fill out y
return(y)
end
But note that T must appear in the argument definitions of the inputs to the function, because this is the purpose of parametric types. You'll get an error if T does not appear in the description of the inputs. Something like this doesn't work:
f{T<:Number}(x::Vector) = x
WARNING: static parameter T does not occur in signature for f at none:1.
The method will not be callable.
f (generic function with 1 method)
But this does, and is type stable, since if the input types are known, then so are the outputs
f{T<:Number}(x::Vector{T}) = x
A parametric method or a constructor of a parametric type
A function is called using the traditional parenthesis syntax:
julia> f(2,3)
5
The above rule is true for both parametric and non-parametric methods.
So, one should not try to call a parametric method like: f{atype}(2,3). I think the source of this mall-usage could be the syntax of calling a parametric type constructor:
julia> typeof(Array{Int})
DataType
julia> Array{Int}(2)
2-element Array{Int32,1}:
57943068
72474848
But in the case of a parametric method:
julia> same_type{T}(x::T, y::T) = true;
julia> typeof(same_type)
Function
julia> same_type{Int}
ERROR: TypeError: Type{...} expression: expected Type{T}, got Function
So with this introduction it has become clear that something like:
function foo{T<:Real}(n::Integer)
.....
end
would be useless, because the value of T won't be determined from caller the arguments.
Make benefit of parametric methods
julia> same_type{T}(x::T, y::T) = true;
julia> same_type(x,y) = false;
julia> same_type(1, 2)
true
julia> same_type(1, 2.0)
false
The main purpose of parametric methods is to let dispatch find the right method to call with respect to argument types when calling a function, but also it has the following idiomatic side effect:
Method type parameters are not restricted to being used as the types
of parameters: they can be used anywhere a value would be in the
signature of the function or body of the function.
julia> mytypeof{T}(x::T) = T
mytypeof (generic function with 1 method)
julia> mytypeof(1)
Int64
Now, let's go back to the main problem of:
How to write a method with Parametric return types?
If your method is already parametric, you could use the parameter value as return type as in the above mytypeof sample or e.g:
julia> newoftype{T}(x::T,n) = T(n)
newoftype (generic function with 1 methods)
julia> newoftype([1,2],4)
4-element Array{Int32,1}:
57943296
72475184
141142104
1970365810
But making return type of a method, a function of arguments value is a straightforward functionality and could be simply done without need to a parametric method. really many of typical methods do this job e.g:
julia> Array(Int,4)
4-element Array{Int32,1}:
126515600
72368848
72474944
0
julia> Array(Float64,4)
4-element Array{Float64,1}:
-2.122e-314
0.0
5.12099e-292
5.81876e-292
It could be done using a argument type of Type e.g:
julia> myarray(T::Type,n::Int)=Array(T,n);

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