Are there idioms in Julia for fast Algebraic Data Types (ADT)?

Hello @thautwarm,

thanks for point to tagless final style - intriguing! … I have tried to read into the topic given your example, as well as from this Haskell introduction. I have tried to follow the latter introduction in Julia language and came across a problem when trying to extend an existing SYM. As extensibility is one of the features of the tagless final approach, I was wondering how to go about it in Julia?

On the top of my head, I don’t see how to solve the issue - have you thought about this yet?

Here is my code:

# http://okmij.org/ftp/tagless-final/course/index.html#lecture

## Intro2.hs
struct ExpSYM{FLIT, FNEG, FADD}
    lit :: FLIT
    neg :: FNEG
    add :: FADD
end

function lit(v)
    function (sym::ExpSYM)
        sym.lit(v)
    end
end

function neg(v)
    function (sym::ExpSYM)
        sym.neg(v(sym))
    end
end

function add(term1, term2)
    function (sym::ExpSYM)
        sym.add(term1(sym), term2(sym))
    end
end

tf1 = add(lit(8), neg(add(lit(1), lit(2))))

## Implement evaluation for repr = Int
evaluate = 
    let lit(v::Int) = v,
        neg(v::Int) = -v,
        add(e1::Int, e2::Int) = e1 + e2
        ExpSYM(lit, neg, add)
    end

@show tf1(evaluate)
@code_warntype tf1(evaluate)

## Implement view for repr = String
view = 
    let lit(v::Int) = "$v",
        neg(v::String) = "(-$v)",
        add(e1::String, e2::String) = "($e1 + $e2)"
        ExpSYM(lit, neg, add)
    end

@show tf1(view)


## ExtF.hs
struct MulSYM{FMUL}
    mul::FMUL
end

function mul(term1, term2)
    function (sym::MulSYM)
        sym.mul(term1(sym), term2(sym))
    end
end

tfm1 = add(lit(7), neg(mul(lit(1), lit(2))))

## Implement evaluation for repr = Int
evaluate =
    let mul(e1::Int, e2::Int) = e1 * e2
        MulSYM(mul)
    end

tfm1(evaluate) ## FAILS
# ERROR: LoadError: MethodError: no method matching (::var"#9#10"{var"#5#6"{Int64},var"#7#8"{var"#17#18"{var"#5#6"{Int64},var"#5#6"{Int64}}}})(::MulSYM{var"#mul#19"})
# Closest candidates are:
# #9(::ExpSYM) at /Users/Markus/.julia/dev/myplayground/playground/finaltagless_lecture.jl:24
# Stacktrace:
# [1] top-level scope at /Users/Markus/.julia/dev/myplayground/playground/finaltagless_lecture.jl:72
# [2] include_string(::Function, ::Module, ::String, ::String) at /Applications/Julia-1.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
# in expression starting at /Users/Markus/.julia/dev/myplayground/playground/finaltagless_lecture.jl:72

Typeclasses should be interpreted as interfaces here, but you’re using Julia structs.
So far there is no way to give a type indicating the existence of specific fields.