While playing with embedded domain specific languages in Haskell I discovered the Num type class is a really neat tool. Take this simple example embedded language that can embed primitives from the output language and can do function application.

>data Expr :: * -> * where
>  Prim :: String -> Expr a
>  App  :: Expr (a -> b) -> Expr a -> Expr b

Take these two dummy types to represent things in the output language.

>data MyNum
>data MyBool

Now it is very easy to create an Num instance for this language:

>primPlus :: Expr (MyNum -> MyNum -> MyNum)
>primPlus = Prim "prim+"

>instance Num (Epxr MyNum) where
>  a + b = primPlus `App` a `App` b
>  fromInteger = Prim . show
>  ...

Which allows you to create very beautiful expression for your language embedded inside Haskell. The Haskell expression `10 * 5 + 2' produces a nice and well typed expression in your embedded domain.

But unfortunately, not everyone in the Prelude is as tolerant as the Num instance. Take the Eq and the Ord type classes for example, they require you to deliver real Haskell `Bool's. This makes it impossible make your DSL an instance of these two, because there are no `Bool's only `Expr Bool's.

Which brings me to the point that, for the sake of embedding other languages, Haskell's Prelude (or an alternative) can greatly benefit from (at least) a Boolean type class like this:

class Boolean a where
ifthenelse :: a -> b -> b -> b -- Not sure about this representation.

And one instance:

>instance Boolean (Expr MyBool) where
>  ifthenelse c a b = Prim "if-then-else" `App` c `App` a `App` b

Now we can change (for example) the Eq type class to this:

>class Eq a where
>  (==) :: Boolean b => a -> a -> b
>  (/=) :: Boolean b => a -> a -> b

For which we can give an implementation for our domain:

>primEq :: Epxr (a -> a -> MyBool)
>primEq = Prim "=="

>instance Eq (Expr a) where
>  a == b = primEq `App` a `App` b

And now we get all functionality from the Prelude that is based on Eq (like not, &&, ||, etc) for free in our domain specific language! Off course there are many, many more examples of things from the standard libraries that can be generalised in order to serve reuse in EDSLs.

Anyone already working on such a generalized Prelude? I can imagine much more domains can benefit from this than my example above. Any interesting thoughts or pointers related to this subject?


Sebastiaan Visser

Haskell-Cafe mailing list

Reply via email to