User Tools

Site Tools


baffling

Table of Contents

Common and Baffling Errors

Julia is an unusual language in many respects. It is also very novel both for the (rather creative) developers and for its users. This is a collection of common errors and/or weird and unintuitive outcomes.

The Julia FAQ covers many other such issues.

For now, the following collection is unordered. They will be ordered as a final step.

Confusing Types and Objects of Types

This is a common beginner's mistake.

statement result explanation
v= Float64 a type desired? v is now an alias for a type. x= v(1) now converts integer to Float64
v= Float64(NaN) an initialized variable v is a now a variable initialized with NaN.

Inquiring explained how you can learn more about both types and variables (use dump()).

Local and Global Variables

WARNING This is currently changing.

snippet.juliarepl
julia> beforefor= true;

julia> for i in 1:2; beforefor= false; end#for#

julia> beforefor
true

julia> beforeif= true;

julia> if (true); beforeif= false; end#if#
false

julia> beforeif
false
  • Module.x= 2 fails, because global assignment only works module-local.

"Typos"

Comma is not a Semicolon Statement Operator

snippet.juliarepl
julia> tmp= ax; ax= bx, bx= tmp
ERROR: UndefVarError: ax not defined
Stacktrace:
 [1] top-level scope at none:0
  • The middle part means ax=(bx,bx), not ( ax=bx ),.

Semicolon Error in Iterator

snippet.juliarepl
julia> for i= –1:0.5;1; (i>0) && info("Found You!"); end#for#

julia> for i= –1.0:0.5;1.0; @info("Found $i"); end;
[ Info: Found –1.0
[ Info: Found 0.0

Look closely at the iterator. You probably meant

snippet.juliarepl
julia> for i= –1.0:0.5:1.0; @info("Found $i"); end;
[ Info: Found –1.0
[ Info: Found –0.5
[ Info: Found 0.0
[ Info: Found 0.5
[ Info: Found 1.0

Confusing Periods (in or not in Numbers)

snippet.juliarepl
julia> 1.+2
ERROR: syntax: invalid syntax "1.+"; add space(s) to clarify

It could have been 1.0+2 (which is Float64 3.0) or 1 .+ 2 (which is Int 3).

Precedence of Multiplication and Importance of Clarifying Space

snippet.juliarepl
julia> x=2;

julia> ( 3^2x , 3^2*x)
(81, 18)
  • 3^2 x is a syntax error.

Control Flow

Looping Over Strings vs. Characters in Strings

snippet.juliarepl
julia> for i in ("hi",);  println(i); end
hi

julia> for i in ("hi");  println(i); end
h
i

Precedence of `&&`

snippet.juliarepl
julia> x= 2;  (true) && x= 3
ERROR: syntax: invalid assignment location "true && x"
  • This means ((true) && x)= 3, and not (true) && (x=3).

Iterating Over (Solo-) Tuples

snippet.juliarepl
julia> for i in (1); println(i); end		## a solo works
1

julia> for i in (:ab,:cd); println(i); end	## a tuple works
ab
cd

julia> for i in (:ab,); println(i); end		## a clearly designated solo tuple
ab

julia> for i in (:ab); println(i); end		## but this is a solo ':ab'
ERROR: MethodError: no method matching iterate(::Symbol)
Closest candidates are:
  iterate(!Matched::Core.SimpleVector) at essentials.jl:589
  iterate(!Matched::Core.SimpleVector, !Matched::Any) at essentials.jl:589
  iterate(!Matched::ExponentialBackOff) at error.jl:171
  ...
Stacktrace:
 [1] top-level scope at ./none:0

Functions

  • Functions can modify their arguments…as long as these arguments are not scalars (e.g., Float) but arrays, structs, dicts, etc. Be careful. Very careful. Be afraid. Very afraid.

Modifying Const Function Arguments

snippet.juliarepl
julia> f!(x)= (x[1]=2);

julia> y= [5,6]; f!(y); y	## x[1] was propagated back up
2-element Array{Int64,1}:
 2
 6

julia> f!([5,6])	## x is not a pointer to [5,6], but its own new object; assignment of '2' was lost
2
  • If x were a pointer to [5,6], you could not assign into it, because 5 is immutable.

Scalars as Arguments Do Not Modify

The function returns the assigned variable, but the assignment does not leave the function

snippet.juliarepl
julia> s1(x)= (x= 3);

julia> a= 10;  s1(a)
3

julia> a
10

When Are Objects Truly Equal?

snippet.juliarepl
julia> 1 === 1
true

julia> ( 1 ) === ( 1 )
true

julia> ( 1, 2 ) === ( 1, 2 )
true

julia> [ 1 ] === [ 1 ]			## two different object containers, that happen to have the same content
false

Comparing Structures vs. Comparing Vectors

snippet.juliarepl
julia> struct I; x::Int; end;#struct#

julia> I(1) == I(1)			## good boy
true

julia> mutable struct M; x::Int; end;#mutable struct#

julia> M(1) == M(1)			## bad boy --- unlike vectors, structs with equal contents are not '=='
false

Object Modification on Non-Scalars

snippet.juliarepl
julia> function n1(x); x= [1, 2]; end;		# will not survive: object x becomes a new container

julia> function n2(x); x[1]= 1; x[2]= 2; end;	## will survive: object is unchanged, but contents are

julia> function n3(x); x.= [1, 2]; end;		## will survive: '.=' is assign contents to existing object

julia> function n4(x); x[:]= [1, 2]; end;	## will survive: object is unchanged

julia> a= [99,99];  n1( a );  a
2-element Array{Int64,1}:
 –99
  99

julia> a= [99,99];  n2( a );  a
2-element Array{Int64,1}:
 1
 2

julia> a= [99,99];  n3( a );  a
2-element Array{Int64,1}:
 1
 2

julia> a= [99,99];  n4( a );  a
2-element Array{Int64,1}:
 1
 2
  • The biggest problems arise when the caller has not written the library function. In this case, the library function or an even deeper function can wreak havoc on the caller.
  • Julia recommends a “collaborative” model, where functions that modify their arguments should be named with a trailing '!'. Functions without it may or may not modify their passed arguments—well, unless the passed arguments are native primitives and not objects.
  • The collaborative model is an inferior alternative to allowing the caller to force-designate the called function not to have permission to alter the parameter.
  • Unfortunately, this aspect can play a role in many variations. For example, in the Optim package, one can provide gradients. An inadvertent function passed to the optimizer, as in g!(G,x) G=myfungradients(x) messes this up and in a most difficult way to debug. Instead, it needs to be g!(G,x) G.=myfungradients(x).

Can your function modify the (object content of its) arguments?

This depends on what your code is expected to do—and you have to know.

`<:Type` vs `Type` as Argument

snippet.juliarepl
julia> f( x::Vector{AbstractFloat} )= "ok";

julia> f( [1.0, 2.0] )
ERROR: MethodError: no method matching f(::Array{Float64,1})
Closest candidates are:
  f(!Matched::Array{AbstractFloat,1}) at none:1
Stacktrace:
 [1] top-level scope at none:0

julia> f( x::Vector{<:AbstractFloat} )= "ok"	## probably what was meant
f (generic function with 2 methods)

julia> f( [1.0, 2.0] )
"ok"

FIXME—give an example of what the first definition would catch.

Varargs Naming (Passthrough of Named Arguments)

snippet.juliarepl
julia> g(a, b; catchme)= "ok $catchme";

julia> f(a, b, x... ) = g(a, b, x...);

julia> f(1,2; catchme="caught")
ERROR: function f does not accept keyword arguments
Stacktrace:
 [1] kwfunc(::Any) at ./boot.jl:321
 [2] top-level scope at none:0

and the call does not work, because the function definition has to be clear in the semicolon vs comma distinction. So, use the following:

snippet.juliarepl
julia> g(a, b; catchme)= "ok $catchme";

julia> f(a, b; x... ) = g(a, b; x...);		## watch the semicolons

julia> f(1,2; catchme="caught")
"ok caught"

Delayed Eval (inside Function)

snippet.julianoeval
[download only julia statements]
julia> function tryeval(); @eval (newfun() = 1);  return newfun(); end;#function tryeval#
 
julia> tryeval()
ERROR: MethodError: no method matching newfun()
The applicable method may be too new: running in world age 25040, while current world is 25041.
Closest candidates are:
  newfun() at REPL[1]:1 (method too new to be called from this world context.)
Stacktrace:
 [1] tryeval() at ./REPL[1]:1
 [2] top-level scope at none:0
 
julia> tryeval()
1
  • The first time, newfun() has not yet been defined at the call time. The second time it has.

Dispatching to Vector of Vector of *Abstract* Floats

This works:

snippet.juliarepl
julia> twist( manyx::Vector{ Vector{Float64} } )= @info("matched")
twist (generic function with 1 method)

julia> twist( [ [1.0, 2.0], [3.0, 4.0] ] )
[ Info: matched

but this does not:

snippet.juliarepl
julia> twist( [ [1.0, 2.0], [3.0, 4.0] ] )
ERROR: UndefVarError: twist not defined
Stacktrace:
 [1] top-level scope at none:0

Passing Functions as Argument

snippet.juliarepl
julia> f(g::Function; x::Float64=10)= g(x);

julia> f(sqrt, 10.0)
ERROR: MethodError: no method matching f(::typeof(sqrt), ::Float64)
Closest candidates are:
  f(::Function; x) at none:1
Stacktrace:
 [1] top-level scope at none:0

Objects as Fill Values

snippet.juliarepl
julia> mutable struct s; v::Int; end

julia> nada= s(1)
s(1)

julia> nadavec= fill( nada, 3 )
3-element Array{s,1}:
 s(1)
 s(1)
 s(1)

julia> nadavec[1].v= 20
20

julia> nadavec
3-element Array{s,1}:
 s(20)
 s(20)
 s(20)

Because the fill does not create new nadas everytime, but just assigns the pointer to nada into the vec, so changing one changes the others.

Hygiene in Macros

Macros should not accidentally modify the global variables, but use good local temporary variables. See Hygiene

Arrays (Vectors and Matrices)

Flattened vs. Unflattened Vectors for R users

Unlike in R, vectors do not autofold. Therefore

snippet.juliarepl
julia> [ [ 1, 2 ], 3 ]		## result: a nested structure of different things 
2-element Array{Any,1}:
  [1, 2]
 3

julia> vcat( [ 1, 2 ], 3 )	## use this one if you want one array
3-element Array{Int64,1}:
 1
 2
 3
 

Vectors of Vectors: Pushing onto Vector of Vectors

snippet.juliarepl
julia> v= Vector{ Vector{Float64} }(undef,5)	# a 5-vector of Undef Float Vectors
5-element Array{Array{Float64,1},1}:
 #undef
 #undef
 #undef
 #undef
 #undef

julia> push!( v[1], 1.0 )
ERROR: UndefRefError: access to undefined reference
Stacktrace:
 [1] getindex(::Array{Array{Float64,1},1}, ::Int64) at ./array.jl:731
 [2] top-level scope at none:0

Initializing Vectors of Vectors

snippet.juliarepl
julia> v= Vector{ Vector{Float64} }(5)
ERROR: MethodError: no method matching Array{Array{Float64,1},1}(::Int64)
Closest candidates are:
  Array{Array{Float64,1},1}() where T at boot.jl:413
  Array{Array{Float64,1},1}(!Matched::UndefInitializer, !Matched::Int64) where T at boot.jl:394
  Array{Array{Float64,1},1}(!Matched::UndefInitializer, !Matched::Int64...) where {T, N} at boot.jl:400
  ...
Stacktrace:
 [1] top-level scope at none:0

julia> push!( v[1], 1.0 )
ERROR: UndefVarError: v not defined
Stacktrace:
 [1] top-level scope at none:0

FIXME show how to do this.

  • is this a vector type or an instantiated vector?

Matrix Access

snippet.juliarepl
julia> m= [ 1 2; 3 4 ]
2×2 Array{Int64,2}:
 1  2
 3  4

julia> m[1,:]= m[1,:] .+ 100	## this is vector access
2-element Array{Int64,1}:
 101
 102

julia> m
2×2 Array{Int64,2}:
 101  102
   3    4

But the following is not:

snippet.juliarepl
julia> m= [ 1 2; 3 4 ]
2×2 Array{Int64,2}:
 1  2
 3  4

julia> m[1,]= m[1,] .+ 100	## this is scalar access, probably unintended
101

julia> m
2×2 Array{Int64,2}:
 101  2
   3  4

Assigning Different Matrix Types

snippet.juliarepl
julia> msz= 4;

julia> bvector= reshape( collect(1:(msz*msz)), (msz,msz) )
4×4 Array{Int64,2}:
 1  5   9  13
 2  6  10  14
 3  7  11  15
 4  8  12  16

julia> using LinearAlgebra

julia> bvector[1:2,1:2] = first( qr( bvector[ 1:2, 1:2 ] ) )
ERROR: InexactError: Int64(Int64,0.44721359549995787)
Stacktrace:
 [1] Type at ./float.jl:700 [inlined]
 [2] convert at ./number.jl:7 [inlined]
 [3] setindex! at ./array.jl:771 [inlined]
 [4] macro expansion at ./multidimensional.jl:648 [inlined]
 [5] macro expansion at ./cartesian.jl:64 [inlined]
 [6] macro expansion at ./multidimensional.jl:643 [inlined]
 [7] _unsafe_setindex!(::IndexLinear, ::Array{Int64,2}, ::LinearAlgebra.QRCompactWYQ{Float64,Array{Float64,2}}, ::UnitRange{Int64}, ::UnitRange{Int64}) at ./multidimensional.jl:636
 [8] _setindex! at ./multidimensional.jl:631 [inlined]
 [9] setindex!(::Array{Int64,2}, ::LinearAlgebra.QRCompactWYQ{Float64,Array{Float64,2}}, ::UnitRange{Int64}, ::UnitRange{Int64}) at ./abstractarray.jl:998
 [10] top-level scope at none:0

julia> bvector = first( qr( bvector[ 1:2, 1:2 ] ) )
2×2 LinearAlgebra.QRCompactWYQ{Float64,Array{Float64,2}}:
 –0.4472140.8944270.894427   0.447214
  • bvector is an Int64 matrix. Thus, you cannot assign the Float64 matrix over its existing contents.

Strings

Inadvertent String Interpolation of Module-Named Variables

snippet.julianoeval
[download only julia statements]
julia> struct FFF; fv::Float64; end#function
 
julia> Base.show(io::IO, f::FFF) = println(io, "Your FFF type is $f.fv")
 
julia> f= FFF(2.0)
Error showing value of type FFF:
ERROR: StackOverflowError:
Stacktrace:
  • You probably meant a version in which $f.fv is not $f\.fv, but $(f.fv):
snippet.juliarepl
julia> struct FFF; fv::Float64; end#function

julia> Base.show(io::IO, f::FFF) = println(io, "Your FFF type is $(f.fv)")

julia> f= FFF(2.0)
Your FFF type is 2.0

really nasty. instead, you need to quote this as $(f.fv).

String-Interpolated Names of Exclamation Variables

snippet.juliarepl
julia> op= 2.0
2.0

julia> println("Thank you for $op!")
ERROR: UndefVarError: op! not defined
Stacktrace:
 [1] top-level scope at none:0
  • Variable names can contain '!', so this asks for $op! not for $op continues.
  • Recall also that in $op.x, you get $(op).x.

String vs. Byte Representations

snippet.juliarepl
julia> x= 7310302560386184563;			## (thanks steveng)

julia> write(stdout, x); println("\n");		## it's the byte representation!!!
surprise

You probably wanted println(x), not write(x).

Eval of Function Name Stored in String vs Eval of Function Name in Symbol

snippet.juliarepl
julia> d1= Dict{String,Int}()
Dict{String,Int64} with 0 entries

julia> d2= Dict{String,Int}()
Dict{String,Int64} with 0 entries

julia> (eval("d1"))["first"]= 1		## the eval'ed string is not d1
ERROR: MethodError: no method matching setindex!(::String, ::Int64, ::String)
Stacktrace:
 [1] top-level scope at none:0

julia> (eval(:d1))["first"]= 1		## the eval'ed symbol is d1
1

Tuples

Tuples vs Sequential Statements

snippet.juliarepl
julia> f(x)= ( x=x+1, x*2 )  ## returns (x+1,x*2)
ERROR: syntax: invalid named tuple element "(x * 2)"

is not

snippet.juliarepl
julia> f(x)= ( x=x+1; x*2 )  ## returns 2*(x+1)
f (generic function with 1 method)

Tuples are not Lists

snippet.juliarepl
julia> (1,2) == (1,2)
true

julia> (1,2) == (1;2)
false

julia> (1,) == (1)
false

Wrong Tuple or Array Naming: Parens instead Curlies

snippet.juliarepl
julia> function f()::Tuple{Int,Int}; x=(1,2); @info(typeof(x)); x end
f (generic function with 1 method)

julia> f()
[ Info: Tuple{Int64,Int64}
(1, 2)

The following is a bug:

snippet.juliarepl
julia> function f()::Tuple(Int,Int); x=(1,2); @info(typeof(x)); x end
f (generic function with 1 method)

julia> f()
ERROR: MethodError: no method matching Tuple(::Type{Int64}, ::Type{Int64})
Closest candidates are:
  Tuple(::Any) where T<:Tuple at tuple.jl:243
Stacktrace:
 [1] f() at ./REPL[8]:1
 [2] top-level scope at none:0

Maybe this is clearer:

snippet.juliarepl
julia> Tuple([Int,Int])
(Int64, Int64)

julia> Tuple(Int,Int)
ERROR: MethodError: no method matching Tuple(::Type{Int64}, ::Type{Int64})
Closest candidates are:
  Tuple(::Any) where T<:Tuple at tuple.jl:243
Stacktrace:
 [1] top-level scope at none:0

Search for “potential failure” scenarios in Julia Module, e.g., for precompiling to fail.

Structs

All Structs Must Be Global.

Functions Cannot Define Structs (Compile Types). Ergo, constructors always need to be outside functions. Fortunately, this give a good and clear error:

snippet.juliarepl
julia> function f();
	mutable struct Point; x::Float64; f::Float64; end;
	println("ok");
        end##function##
ERROR: syntax: "struct" expression not at top level

Extract All Fields from Vector of Structs

snippet.juliarepl
julia> struct S; x::Int; f::String; end#struct

julia> s1= S(1, "a"); s2= S(20, "b"); s3= S(300, "c")
S(300, "c")

julia> svec= [ s1, s2, s3 ]
3-element Array{S,1}:
 S(1, "a")
 S(20, "b")
 S(300, "c")

julia> [ y->svec[u].x for y in svec ]	## or [ i->svec[i].x for i=1:length(svec) ]
3-element Array{getfield(Main, Symbol("##4#6")),1}:
 getfield(Main, Symbol("##4#6"))()
 getfield(Main, Symbol("##4#6"))()
 getfield(Main, Symbol("##4#6"))()

julia> [ y.x for y in svec ]		## or [ svec[i].x for i=1:length(svec) ]
3-element Array{Int64,1}:
   1
  20
 300

Dicts

Initialization of Dict

snippet.juliarepl
julia> d= Dict{String,Int}
Dict{String,Int64}

julia> d["first"]= 1
ERROR: MethodError: no method matching setindex!(::Type{Dict{String,Int64}}, ::Int64, ::String)
Stacktrace:
 [1] top-level scope at none:0

julia> d= Dict{String,Int}(0)
ERROR: BoundsError: attempt to access 0
  at index [2]
Stacktrace:
 [1] indexed_iterate(::Int64, ::Int64, ::Nothing) at ./tuple.jl:69
 [2] Dict{String,Int64}(::Int64) at ./dict.jl:103
 [3] top-level scope at none:0

julia> d["first"]= 1
ERROR: MethodError: no method matching setindex!(::Type{Dict{String,Int64}}, ::Int64, ::String)
Stacktrace:
 [1] top-level scope at none:0

Instead, you need

snippet.juliarepl
julia> d= Dict{String,Int}()
Dict{String,Int64} with 0 entries

julia> d["first"]= 1
1

Parallel Processing

addprocs() must be defined BEFORE everywhere.

Plotting

Timing of Redraw

snippet.juliarepl
julia> using Plots

julia> p= plot( [1,10], [1,10], color=Gray(0.5), w=30, title="gray scale" )

julia> for i=0:10; vline!( [i], color=Gray(i/10.0), w=10 ) end
  • The for loop is one statement, and therefore the final statement's output is a nothing, which does not lead to a redraw. To redraw, type “p” or “display(p)”.

Benchmarking: @time and @btime

I think that symbols cannot be escaped with '$' in @btime, only numbers

or it could be constants pulled out from list?

Backmatter

Commonly Useful Packages on Julia Repository

Notes

baffling.txt · Last modified: 2018/11/20 13:44 (external edit)