Skip to content

Instantly share code, notes, and snippets.

@pao
Created February 11, 2013 19:31
Show Gist options
  • Save pao/4756915 to your computer and use it in GitHub Desktop.
Save pao/4756915 to your computer and use it in GitHub Desktop.
Former contents of Julia wiki page on code patterns.

Code Patterns in Julia

Climbing the type hierarchy

The base library of Julia contains a method which finds the element type of an array. This dispatch is fine for instances of AbstractArray or any of its children.

eltype{T,n}(::AbstractArray{T,n}) = T

But what about if you supply a type instead of an instance? Naively, you could do this, and duck type the children:

eltype{T<:AbstractArray}(::Type{T}) = T.parameters[1] # BAD

But that relies on all the concrete implementations working that way, so isn't particularly generic.

Instead, provide an implementation for the highest type in the hierarchy:

eltype{T,n}(::Type{AbstractArray{T,n}}) = T

And for children, go one step up the type chain. If you haven't reached the highest type in the chain yet, this will recursively call itself until eventually the first dispatch matches.

eltype{T<:AbstractArray}(::Type{T}) = eltype(super(T))

Pattern matching on sum types

In languages with algebraic data types, a common technique is to create a simple sum type—a type that is exactly one of its children, but contains no fields—and perform a pattern match to run different code depending on which type you get. It is possible to perform a similar operation with multiple dispatch.

The Options framework uses this to distinguish different ways of dealing with potentially unused options to a function.

First, declare the types:

abstract OptionsChecking
type CheckNone <: OptionsChecking; end
type CheckWarn <: OptionsChecking; end
type CheckError <: OptionsChecking; end

You can then write multiple methods to match on the type when it is appropriate to do so.

function docheck(o::Options{CheckNone},checkflag::Vector{Bool})
    clearcheck(o,checkflag)
end
function docheck(o::Options{CheckWarn},checkflag::Vector{Bool})
    unused, msg = docheck_common(o,checkflag)
    if any(unused)
        println("Warning: ",msg)
    end
    clearcheck(o,checkflag)
end
function docheck(o::Options{CheckError},checkflag::Vector{Bool})
    unused, msg = docheck_common(o,checkflag)
    clearcheck(o,checkflag) # in case it's in a try/catch block...
    if any(unused)
        error(msg)
    end
end

eval recipes

eval (and @eval) are powerful, but sometimes one has to take care to achieve the desired aim. For example, one can define functions programmatically like this:

for (fname, val) in
    ((:plus2, 2),
     (:plus3, 3))
    @eval begin
        function ($fname)(x)
            x + $val
        end
    end
end

julia> plus2(4)
6

julia> plus3(4.1)
7.1

However, what if you want to retain a variable as a symbol? Consider this example:

julia> fname = :ddot_
ddot_

julia> dlsym(Base.libblas, fname)
Ptr{Void} @0x00007f4b6a7da720

julia> @eval begin
           dlsym(Base.libblas, $fname)
       end
ddot_ not defined

Why did this happen? The problem is that $fname leads to an attempt to evaluate the symbol, and there is no value assigned to a variable named ddot_. A way around this is to use the $(expr(:quote, varname)) syntax:

julia> @eval begin
           dlsym(Base.libblas, $(expr(:quote, fname)))
       end
Ptr{Void} @0x00007f4b6a7da720

Another problem can arise in creating tuple expressions. For example, consider this:

julia> argtypes = (Int32, FileOffset, Int32)
(Int32,Int64,Int32)

julia> @eval begin
         function mylseek(fd, offset, ref)
           cpos = ccall(:lseek, FileOffset, $argtypes, fd, offset, ref)
         end
       end
syntax error: ccall argument types must be a tuple; try (T,)

The compiler is not recognizing argtypes as a tuple. The trick is to create a tuple expression: substitute ($(argtypes...),) in place of $argtypes above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment