[Haskell-cafe] Re: I love purity, but it's killing me.

Heinrich Apfelmus apfelmus at quantentunnel.de
Fri May 29 04:08:19 EDT 2009

Conal Elliott wrote:
> Sittampalam, Ganesh wrote
>> In my experience [1], observable sharing using GHC's stable names is a
>> pretty effective solution to this problem.
> Plus unsafePerformIO and weak references as in *Stretching the storage
> manager: weak pointers and stable names in
> Haskell<http://citeseer.ist.psu.edu/peytonjones99stretching.html>
> *?
> Lacking a more elegant alternative, that's what I'll probably do again, as
> in Pan, Vertigo, and Pajama.

Note that the lack of a more elegant alternative, i.e. one that avoids
unsafePerformIO  for observing sharing, is not a random inconvenience
but an unavoidable consequence of embedding a DSL into a host language.
In other words, there is a fundamental problem here and unsafePerformIO
is but the usual duct tape for inadequately fixing fundamental problems.

The problem is that there are two types of  let  expressions, the one of
the host language and the one of the embedded language. For instance,
consider the example

    let a = Add 2 3  :: Expr
        b = Add a a  :: Expr

Replacing equals with equals, this is the same as

    Add (Add 2 3) (Add 2 3)  :: Expr

and there is no sharing in the embedded language. But I argue that this
is a good thing, for sharing in the embedded language should be done
with  let  expressions of the embedded language, like for example

    Let ['a' := Add 2 3,
         'b' := Add (Var 'a') (Var 'a') ] :: Expr

Clearly, these are two different expressions of the embedded language,
even if one is an optimization of the other!

One could say that the  let  of the host language is a shorthand
notation for constructing large  Expr  and only the  Let  of the
embedded language can express sharing inside the embedded language. As
soon as we try to make the former synonymous to the latter, our ability
to use it as a shorthand notation is gone and it now becomes
*impossible* to represent

   Add (Add 2 3) (Add 2 3)  :: Expr


   let a = Add 2 3  :: Expr
       b = Add a a  :: Expr

Whether this is desirable or not is irrelevant; the host language
Haskell will rebel at this.

An analogous example would be the two fixed points of a  MonadFix ,
namely the "internal" (embedded) fixed point

   fixInternal :: (a -> m a) -> m a
   fixInternal = mfix

and the "external" (host) fixed point

   fixExternal :: (a -> m a) -> m a
   fixExternal = \f -> fix (>>= f)

(Perhaps not surprisingly, MonadFix was first discovered/used when
designing the DSL Lava, if I am informed correctly.)



More information about the Haskell-Cafe mailing list