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.
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
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?
Haskell-Cafe mailing list