[Haskell-cafe] Difficulties with tagless - create "primitives" or
compose them
Bruno Oliveira
bruno at ropas.snu.ac.kr
Sun Jun 13 00:29:51 EDT 2010
Hi Gunther,
The "finally tagless" approach can be viewed as one instance of the
typecase pattern,
which myself and others have investigated the past:
http://ropas.snu.ac.kr/~bruno/papers/Typecase.pdf
Your problem is related to a problem that is found when designing
generic programming
libraries. Namely the existence of what we call "ad-hoc" cases. These
are cases that
most of the times have a default, but sometimes we would like to
override that default.
This is essentially the source of your confusion. What is the best
thing todo: shall I provide
a default by using composition by creating a definition for "mul"
outside the class; or
shall I create a primitive. There is a trade-off between the two.
Fortunatelly it is possible
to avoid such trade-off. This is one of the things that is described
in the EMGM paper,
which uses the same variation of the typecase pattern that is used in
the "finally tagless"
approach:
http://ropas.snu.ac.kr/~bruno/papers/ExtensibleGM.pdf
Now, for your particular example, this amounts to the following:
type Arr exp a b = exp a -> exp b
class EDSL exp where
lam :: (exp a -> exp b) -> exp (Arr exp a b)
app :: exp (Arr exp a b) -> exp a -> exp b
int :: Int -> exp Int -- Integer literal
add :: exp Int -> exp Int -> exp Int
sub :: exp Int -> exp Int -> exp Int
class EDSL exp => EDSLMul exp where
mul :: exp Int -> exp Int -> exp Int
mul e1 e2 = -- a default definition in terms of the primitives in
EDSL
By creating a subclass and defining "mul" as a default method you get
the advantages
of the primitive approach ( you can define your own interpretations)
with the advantages
of the compositional approach (you can modularly add as many as you
want without having
to touch EDSL).
There is a little extra work that you need to do though, because now
you'll need to say:
instance EDSLMul MyInterpretation
to get an instance for mul at a particular interpretation. You can
actually avoid this, if you
use overlapping instances:
instance EDSLMul a
then, only when you you really want to override an interpretation, you
will need an instance.
More generally this approach tells you how you can modularize your
EDSLs. Or, put another
way, if offers a solution for the expression problem:
http://www.daimi.au.dk/~madst/tool/papers/expression.txt
Hope this helps!
Cheers,
Bruno Oliveira
On Jun 13, 2010, at 10:58 AM, Günther Schmidt wrote:
> Hi list,
>
> there is one thing about the "Finally tagless" EDSL approach that
> really confuses me: (Well more than one actually but this one more so)
>
> When to decide to introduce a term as a primitive in the syntax
> class and when to define it from primitives already defined.
>
> For example this one here:
>
> type Arr exp a b = exp a -> exp b
>
> class EDSL exp where
> lam :: (exp a -> exp b) -> exp (Arr exp a b)
> app :: exp (Arr exp a b) -> exp a -> exp b
>
> int :: Int -> exp Int -- Integer literal
> add :: exp Int -> exp Int -> exp Int
> sub :: exp Int -> exp Int -> exp Int
> mul :: exp Int -> exp Int -> exp Int
>
> Let's take "mul" here, defined as a "primitive", in other words
> defined in the EDSL class.
>
> Technically, with lam, app and add already defined, I could have
> defined "mul" outside the EDSL class, just built from the 3
> primitive operators.
>
> Of course doing so then does not give me the possibility to choose
> alternative evaluation strategies for "mul" itself, only for lam,
> app and add.
>
> So what is a good measure for deciding when to define a term
> primitive or not?
>
> Günther
>
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
More information about the Haskell-Cafe
mailing list