<p>Haskell has a <code>do</code> notation for writing code that executes in a monad. I've seen that this is sometimes (ab?)used for creating embedded internal domain-specific languages, where code written in the language is put inside a <code>do</code> block within the Haskell source, but I'm not sure how that works.</p>
<p>Do I use an existing monad, or create my own? Does it need to really <em>be</em> a monad, or is just a Monad instance enough? What do I actually need to do to build a DSL this way?</p>

<haskell><monads><domain-specific-languages><internal-dsl>
How do I create an internal DSL with Haskell's do notation?
CC BY-SA 4.0
2023-05-20T03:09:38.457Z
Haskell's llvm bindings are a pretty good example of this. They use `mdo` to build SSA blocks. Example: https://github.com/llvm-hs/llvm-hs-examples/blob/master/irbuilder/Main.hs
Silvio Mayolo
CC BY-SA 4.0
2023-05-20T03:11:34.883Z
is this really a language design question? Or is it just a programming question? I can't tell.
starball
CC BY-SA 4.0
2023-05-20T03:13:03.903Z
@starball In theory, it's about writing DSLs. I guess it's an open question of this site whether DSL design is in scope.
Silvio Mayolo
CC BY-SA 4.0
2023-05-20T03:13:27.613Z
@starball It's a language **implementation** question, I think, though I expect answers may be coloured by language design constraints imposed by the notation/host language.
Michael Homer
CC BY-SA 4.0
2023-05-20T03:14:26.857Z
@SilvioMayolo This is a real question, I don't have an answer lined up, so please do post one if you have it!
Michael Homer
CC BY-SA 4.0
2023-05-20T03:16:17.123Z
I can't think why domain-specific languages would *not* be on topic, nor what rule could distinguish them definitively. The mooted scope of the site did include tools used for building languages, but we'll see how that goes in practice.
Michael Homer
<p>You'll make a Monad instance, but it doesn't need to &quot;be&quot; a monad satisfying the laws (though when you interact with other functions they may expect it to be). There are a couple of direct routes.</p>
<p>In libraries like HaTeX and Blaze, the <code>do</code> notation is used to construct a syntax tree for a document, which can be processed later on. Something like this:</p>
<pre class="lang-haskell prettyprint-override"><code>aDocument = do
  section &quot;Top-level heading&quot; $ do
    text &quot;Multiple items can appear inside. &quot;
    text &quot;These can be nested&quot;
    list $ do
      item $ text &quot;Creating a tree&quot;
      item $ text &quot;Of values&quot;
    section &quot;A subsection&quot; $ do
      text &quot;Recursively&quot;
</code></pre>
<p>is built out of functions like <code>text :: String -&gt; Doc () = Doc [Text s] ()</code>. We could imagine implementing it something like this:</p>
<pre class="lang-haskell prettyprint-override"><code>data DocDSL = Text String | Section String (Doc ()) | List (Doc ()) | Item (Doc ())
data Doc v = Doc [DocDSL] v
-- Keywords of the DSL:
text s = Doc [Text s] ()
section title body = Doc [Section title body] ()
list body = Doc [List body] ()
item body = Doc [Item body] ()
-- ... Functor &amp; Applicative instances elided here ...
instance Monad Doc where
  return v = Doc [] v
  (Doc l v) &gt;&gt;= f = case f v of (Doc l2 v2) -&gt; Doc (l ++ l2) v2```
</code></pre>
<p>The bind operation <code>&gt;&gt;=</code> that's inserted between every line just builds up the syntax tree by joining consecutive items together into a list of sibling nodes, while constructs like <code>section</code> and <code>list</code> are given their own <code>do</code> block that produces another Doc to hold inside, creating a recursively-nested structure.</p>
<p>The resulting value can be processed like any other algebraic data type later on, to evaluate, transform, or optimise it. Here that value is</p>
<pre class="lang-haskell prettyprint-override"><code>Doc [Section &quot;Top-level heading&quot; (Doc [Text &quot;Multiple items can appear inside. &quot;,Text &quot;These can be nested&quot;,List (Doc [Item (Doc [Text &quot;Creating a tree&quot;] ()),Item (Doc [Text &quot;Of values&quot;] ())] ()),Section &quot;A subsection&quot; (Doc [Text &quot;Recursively&quot;] ())] ())] ()
</code></pre>
<p>and a typical syntax tree structure. A more complete language could do some more work here than purely building up lists, but you can see the general structure in play.</p>
<p>In some of these libraries the DSL is parameterised further by the evaluator in use later, uses overloaded strings, etc, but those are advanced elements. This same structure could be used for any tree structure, not just a document, so a programming language or other output format is viable too. A programming language might use some more features of the <code>do</code> notation, like variable bindings.</p>
<hr />
<p>The case above is really just leveraging the <code>do</code> notation, and otherwise has essentially no connection to being a monad. A more complex DSL could make better use of that, and in particular <code>do</code> provides syntactic support for variables.</p>
<pre class="lang-haskell prettyprint-override"><code>do
  ...
  x &lt;- currentDepth
  if x &gt; 10 then text &quot;Deep&quot; else text &quot;Shallow&quot;
</code></pre>
<p>These are regular Haskell variables, and can hold any type of data, be passed to other functions, used in conditionals or combined with other values, and so on.</p>
<p>The DSL might build on the state monad for storing and retrieving values, but any of your DSL functions can return a value to be put in a variable (going where <code>()</code> was written in the version above), with whatever meaning makes sense for the language you're building. There's still no requirement to adhere to the monad laws or to do anything beyond satisfying the typechecker.</p>
<hr />
<p>There is also <a href="https://hackage.haskell.org/package/control-dsl-0.2.1.3/docs/Control-Dsl.html" rel="nofollow noreferrer">Control.Dsl</a>, a set of modules specifically for making DSLs with <code>do</code>, but relying on the <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/rebindable_syntax.html" rel="nofollow noreferrer">RebindableSyntax</a> GHC extension and so outside of standard Haskell. They sidestep monads entirely and add additional functionality around <code>do</code>, so they are able to go a bit further with the language.</p>
<hr />
<p><a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/applicative_do.html" rel="nofollow noreferrer">ApplicativeDo</a> is another GHC option that uses the applicative operations in translating <code>do</code> instead of the monadic ones when possible. This is another way not to need a Monad instance, but limits your DSL to mostly a list of steps. This could work for simple cases of document trees, but not with variables.</p>

CC BY-SA 4.0
2023-06-22T01:58:23.260Z
2023-06-22T01:58:23.260Z
Michael Homer
# How do I create an internal DSL with Haskell's do notation?
Posted by: username_0 on 2023-05-20
Haskell has a `do` notation for writing code that executes in a monad. I've seen that this is sometimes (ab?)used for creating embedded internal domain-specific languages, where code written in the language is put inside a `do` block within the Haskell source, but I'm not sure how that works.

Do I use an existing monad, or create my own? Does it need to really *be* a monad, or is just a Monad instance enough? What do I actually need to do to build a DSL this way?
**Tags:** haskell monads domain-specific-languages internal-dsl
> **Comments:**
> **username_1**: Haskell's llvm bindings are a pretty good example of this. They use `mdo` to build SSA blocks. Example: https://github.com/llvm-hs/llvm-hs-examples/blob/master/irbuilder/Main.hs  
> **username_2**: is this really a language design question? Or is it just a programming question? I can't tell.  
> **username_1**: @starball In theory, it's about writing DSLs. I guess it's an open question of this site whether DSL design is in scope.  
> **username_0**: @starball It's a language **implementation** question, I think, though I expect answers may be coloured by language design constraints imposed by the notation/host language.  
> **username_0**: @SilvioMayolo This is a real question, I don't have an answer lined up, so please do post one if you have it!  
> **username_0**: I can't think why domain-specific languages would *not* be on topic, nor what rule could distinguish them definitively. The mooted scope of the site did include tools used for building languages, but we'll see how that goes in practice.  
---
## 1 answers
### Answer by username_0 on 2023-06-22. Score: 3
You'll make a Monad instance, but it doesn't need to "be" a monad satisfying the laws (though when you interact with other functions they may expect it to be). There are a couple of direct routes.
In libraries like HaTeX and Blaze, the do notation is used to construct a syntax tree for a document, which can be processed later on. Something like this:
aDocument = do
  section "Top-level heading" $ do
    text "Multiple items can appear inside. "
    text "These can be nested"
    list $ do
      item $ text "Creating a tree"
      item $ text "Of values"
    section "A subsection" $ do
      text "Recursively"

is built out of functions like text :: String -> Doc () = Doc [Text s] (). We could imagine implementing it something like this:
data DocDSL = Text String | Section String (Doc ()) | List (Doc ()) | Item (Doc ())
data Doc v = Doc [DocDSL] v
-- Keywords of the DSL:
text s = Doc [Text s] ()
section title body = Doc [Section title body] ()
list body = Doc [List body] ()
item body = Doc [Item body] ()
-- ... Functor & Applicative instances elided here ...
instance Monad Doc where
  return v = Doc [] v
  (Doc l v) >>= f = case f v of (Doc l2 v2) -> Doc (l ++ l2) v2```

The bind operation >>= that's inserted between every line just builds up the syntax tree by joining consecutive items together into a list of sibling nodes, while constructs like section and list are given their own do block that produces another Doc to hold inside, creating a recursively-nested structure.
The resulting value can be processed like any other algebraic data type later on, to evaluate, transform, or optimise it. Here that value is
Doc [Section "Top-level heading" (Doc [Text "Multiple items can appear inside. ",Text "These can be nested",List (Doc [Item (Doc [Text "Creating a tree"] ()),Item (Doc [Text "Of values"] ())] ()),Section "A subsection" (Doc [Text "Recursively"] ())] ())] ()

and a typical syntax tree structure. A more complete language could do some more work here than purely building up lists, but you can see the general structure in play.
In some of these libraries the DSL is parameterised further by the evaluator in use later, uses overloaded strings, etc, but those are advanced elements. This same structure could be used for any tree structure, not just a document, so a programming language or other output format is viable too. A programming language might use some more features of the do notation, like variable bindings.

The case above is really just leveraging the do notation, and otherwise has essentially no connection to being a monad. A more complex DSL could make better use of that, and in particular do provides syntactic support for variables.
do
  ...
  x <- currentDepth
  if x > 10 then text "Deep" else text "Shallow"

These are regular Haskell variables, and can hold any type of data, be passed to other functions, used in conditionals or combined with other values, and so on.
The DSL might build on the state monad for storing and retrieving values, but any of your DSL functions can return a value to be put in a variable (going where () was written in the version above), with whatever meaning makes sense for the language you're building. There's still no requirement to adhere to the monad laws or to do anything beyond satisfying the typechecker.

There is also Control.Dsl, a set of modules specifically for making DSLs with do, but relying on the RebindableSyntax GHC extension and so outside of standard Haskell. They sidestep monads entirely and add additional functionality around do, so they are able to go a bit further with the language.

ApplicativeDo is another GHC option that uses the applicative operations in translating do instead of the monadic ones when possible. This is another way not to need a Monad instance, but limits your DSL to mostly a list of steps. This could work for simple cases of document trees, but not with variables.
---
