[Haskell-cafe] "import" functionality in DSLs

Nikhil A. Patil patil.nikhil at gmail.com
Sat Apr 16 15:29:30 CEST 2011


Hi,

I am planning a simple monadic DSL (frankly, calling it a DSL is a bit
of a stretch; it's just a somewhat glorified state monad), and I wish to
implement some kind of "import" functionality for it.

The DSL code looks something like this:

> doit :: DSL Term
> doit = do (+) <- define_function "+" 2
>           n0  <- define_constant "0"
>           k   <- define_constant "k"
>           -- begin beautiful DSL code
>           let x = k + n0
>           return $ x + x

The code above adds identifiers "+", "0", "k" to the DSL monad and
conveniently exposes haskell identifiers (+), n0, k for the user to use
in code that follows. (Note that these define_* functions make state
updates in the underlying state monad.)

Needless to say, most functions like doit have very similar define_*
calls in the beginning. Thus, I want to implement some kind of import
functionality. Ideally, the code would look like this:

> module DSLPrelude where
>
> prelude :: DSL ()
> prelude = do (+) <- define_function "+" 2
>              n0  <- define_constant "0"
>              k   <- define_constant "k"
>              return ()

> module Main where
> import DSLPrelude
>
> doit :: DSL Term
> doit = do prelude
>           -- begin beautiful DSL code
>           let x = k + n0
>           return $ x + x

...but of course that won't work since (+), n0, k are not in scope.

I can think of two solutions, both of which I dislike:

Solution 1:

> module DSLPrelude where
>
> prelude :: DSL (Term -> Term -> Term, Term, Term)
> prelude = do (+) <- define_function "+" 2
>              n0  <- define_constant "0"
>              k   <- define_constant "k"
>              return ((+), n0, k)

> module Main where
> import DSLPrelude
>
> doit :: DSL Term
> doit = do ((+), k, n0) <- prelude
>           -- begin beautiful DSL code
>           let x = k + n0
>           return $ x + x

This is quite unsafe: I have mistakenly swapped k and n0 in doit,
without failing typecheck.

Solution 2:

> module DSLPrelude where
>
> (+) :: DSL (Term -> Term -> Term)
> n0  :: DSL Term
> k   :: DSL Term
> (+) = define_function "+" 2
> n0  = define_constant "0"
> k   = define_constant "k"

> module Main where
> import DSLPrelude
>
> doit :: DSL Term
> doit = do (+) <- (+)
>           n0  <- n0
>           k   <- k
>           -- begin beautiful DSL code
>           let x = k + n0
>           return $ x + x

...which works, but still has quite a bit of boilerplate crap.

I feel this would be a common problem with a lot of DSLs, so I am
curious to know how others solve it. Any pointers and suggestions are
most welcome and greatly appreciated.

Thanks!

nikhil



More information about the Haskell-Cafe mailing list