Skip to content

Instantly share code, notes, and snippets.

@c42f
Last active June 8, 2016 21:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save c42f/841ecdfb8af878b34e58 to your computer and use it in GitHub Desktop.
Save c42f/841ecdfb8af878b34e58 to your computer and use it in GitHub Desktop.
Experimenting with generated types
module GeneratedTypes
# First an example of a simple "generic" type
immutable GenericImmutable{FieldNames, FieldTypes<:Tuple}
fieldvalues::FieldTypes
end
@generated function getfield{FieldNames, FieldTypes, Name}(gi::GenericImmutable{FieldNames,FieldTypes},::Type{Val{Name}})
fieldnumber = findfirst(FieldNames, Name)
fieldnumber > 0 || error("Field $name not found")
quote
gi.fieldvalues[$fieldnumber]
end
end
# Here we should define a macro version of getfield. So far it is immutable, so setfield is redundant
# @getfield(x,a) -> getfield(x,Val{:a})
macro getfield(x,a)
:(getfield($x,Val{$(Expr(:quote, a))})) # Is there a better way to insert a quote ":" than this???
end
# An example:
x = GeneratedTypes.GenericImmutable{(:a,:b),Tuple{Int64,Float64}}((1,2.0))
@assert getfield(x,Val{:a}) == 1
@assert getfield(x,Val{:b}) == 2.0
# Next, we need to define a macro that takes modifies a type definition to create a generated type
# It will work like this:
macro generated_type(expr)
println(expr) # For now just show what the parser thinks of the mess below
1
end
@generated_type immutable Foo{N}
if N == 1
quote
A::Int
end
else
quote
B::(Float64,Float64)
C::Bool
end
end
end
# foo{1}(1) == foo{Tuple{:A},Tuple{Int}}(1)
# foo{2}((2.0,3.0),true) == foo{Tuple{:B,:C},Tuple{Tuple{Float64,Float64},Bool}}((2.0,3.0),true)
#
# The macro will also define a @getfield macro and getfield() function
# The inner constructors probably won't work, not sure?
# Outer constructors may or may not work, but they will have to call the "complicated" name of the inner constructor of what is wanted (but can be generated themselves)
end # module
@c42f
Copy link
Author

c42f commented Feb 4, 2016

Hey @andyferris, I stuck up your version in this gist, and did a little cleanup.

@c42f
Copy link
Author

c42f commented Feb 4, 2016

Hacked another couple of things. I guess we could probably do better than GenericImmutable - the @generated_type macro could expand to create an abstract type Foo{N} and some helper guff -

abstract Foo{N}

@generated function gen_Foo{N}(::Type{Val{N}})
    body = if N == 1
        quote
            A::Int
        end
    else
        quote
            B::Float64
            C::Bool
        end
    end
    name = "Foo{$N}"
    genname = gensym(name)
    @eval immutable $genname <: Foo{$N} # Yup, it's a hack alright
            $body
    end
    quote
        $genname
    end
end

# Add a gentype helper, and we have something somewhat resembling the real
# thing.
macro gentype(expr)
    @assert expr.head == :call && expr.args[1].head == :curly
    val_params = expr.args[2:end]
    curlyexpr = expr.args[1]
    func_name = symbol("gen_$(curlyexpr.args[1])")
    type_params = [:(Val{$p}) for p in curlyexpr.args[2:end]]
    quote
        $func_name($(type_params...))($(val_params...))
    end
end


@show @gentype Foo{1}(1)
@show @gentype Foo{2}(1.2,true)
@show @gentype Foo{3}(3.4,true)

@andyferris
Copy link

Just looking at this again. The line you commented "Yup, it's a hack alright" is the one that is currently forbidden in 0.5 master, since JuliaLang/julia#16040...

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