
I seem to have run into an instance of the expression problem [1], or something very similar, when experimenting with ``finally tagless'' EDSLs, and don't see a good way to work around it. I have been experimenting with embedded DSLs, using the techniques described in a couple recent papers [2,3]. The idea is this: implement an embedded DSL using type classes, rather than ADTs or GADTs. This allows one to define analyses, queries, and manipulations of EDSL programs independently, as class instances. Furthermore, by using type classes rather than data types, there is no interpretive overhead in the analyses, queries, and manipulations on the EDSL programs. Finally, using type classes permits greater modularity, as an EDSL can be defined as the combination of several simpler EDSLs [3]. Suppose we have a type class for simple integer arithmetic expressions:
class IntArithExpr exp where integer :: Integer -> exp Integer add :: exp Integer -> exp Integer -> exp Integer
We can write an evaluator for these expressions like this:
newtype E a = E { eval :: a }
instance IntArithExpr E where integer = E add e1 e2 = E (eval e1 + eval e2)
-- eval $ add (integer 20) (integer 22) <==> 42
The trouble comes in when defining a more general arithmetic expression type class. Suppose we want polymorphic arithmetic expressions:
class PolyArithExpr exp where constant :: a -> exp a addP :: exp a -> exp a -> exp a
We then try to define an evaluator:
-- instance PolyArithExpr E where -- constant = E -- addP e1 e2 = E (eval e1 + eval e2) -- bzzt!
The instance definition for `addP' is not type correct: Could not deduce (Num a) from the context () arising from a use of `+' at /home/blarsen/mail.lhs:42:20-36 One way to remedy this is to change the class definition of PolyArithExpr so that `addP' has a Num constraint on `a':
class PolyArithExprFixed exp where pae_constant :: a -> exp a pae_add :: Num a => exp a -> exp a -> exp a
which allows us to define an evaluator:
instance PolyArithExprFixed E where pae_constant = E pae_add e1 e2 = E (eval e1 + eval e2)
I find this ``fix'' lacking, however: to define a new interpretation of the EDSL, we may be forced to change the DSL definition. This is non-modular, and seems like an instance of the expression problem. (There may be a multiparameter type class solution for this.) How can one define the polymorphic arithmetic EDSL without cluttering up the class definitions with interpretation-specific constraints, and still write the desired interpretations? Sincerely, Bradford Larsen References: [1] Philip Wadler. The Expression Problem. November 12, 1998. http://www.daimi.au.dk/~madst/tool/papers/expression.txt. [2] Jacques Carette, Oleg Kiselyov, and Chung-chieh Shan. Finally Tagless, Partially Evaluated: Tagless Staged Interpreters for Simpler Typed Languages. APLAS 2007. [3] Robert Atkey, Sam Lindley, and Jeremy Yallop. Unembedding Domain-Specific Languages. ICFP Haskell Symposium 2009.