Search code examples
haskellfunctional-programmingabstract-syntax-treedslgadt

How to define types in a DSL in Haskell?


I've been writing an AST to build a DSL in Haskell and, for that, I'm using GADTs to define expressions such as:

data Expr a where
    I   :: Int  -> Expr Int
    B   :: Bool -> Expr Bool
    Add :: Expr Int -> Expr Int -> Expr Int
    Mul :: Expr Int -> Expr Int -> Expr Int
    Eq  :: Expr Int -> Expr Int -> Expr Bool

However, I would like for expressions like Add and Mul to also work with other numeric values, from types Float and Double. How would I be able to achieve such results?


Solution

  • You could generalize Expr a bit and use

    data Expr a where
      Lit :: a -> Expr a                           -- why not just let anything in?
      Add :: Num a => Expr a -> Expr a -> Expr a   -- only `a` w/ `Num a` can be added
      Mul :: Num a => Expr a -> Expr a -> Expr a   -- only `a` w/ `Num a` can be multiplied
      Eq  :: Eq a => Expr a -> Expr a -> Expr Bool -- only `a` w/ `Eq a` can be added
    

    Then again, the question really is: what are you trying to do with it? If you just want to explicitly construct an AST that type-checks and then be able to evaluate it, the above works just fine.

    eval :: Expr a -> a
    eval (Lit x) = x
    eval (Add x y) = eval x + eval y
    eval (Mul x y) = eval x * eval y
    eval (Eq x y) = eval x == eval y