[<<][meta][>>][..]
Fri Oct 28 20:46:19 EDT 2011

Pure functions vs. embedded syntax

Hmm... looks like we're at a dead end.

termFun is made for compiling pure functions, not HOS functions.

It looks like overall it's a better strategy to stick to compilation
of HOS functions only, and write wrappers for pure functions.

This means the whole Compile.hs needs to be adjusted for this too.
Let's dupe it and make a variant.  ( Tried, gave up..  Can't do it now. )

Oh man, this isn't easy...

So what about doing this again.  Maybe it's even simpler than the
Compile.hs approach.  Really, we only need to provide an input, apply
it to the body of the function and we have an expression.  Take it out
of the monad and wrap it.

In order for this to work, we probably need to stay in the monad.
_lambda can't be a pure function, maybe that's the trick?

WRONG:
_lambda :: Args r as ras => (ras -> m (r t)) -> r (as -> m t)

RIGHT:
_lambda :: Args r as ras => (ras -> m (r t)) -> m (r (as -> m t))

This indeed seems to be necessary.

So, doing the eval purely for the type checking gives this:

  _lambda = lambda where
    lambda :: forall as ras t. Args Code as ras => 
              (ras -> MCode (Code t)) -> MCode (Code (as -> MCode t))
    lambda f = term where
      inTermTree :: ras
      inVarList :: [Var]
      inTermTree = undefined -- FIXME
      inVarList  = undefined -- FIXME: from inTermTree

      term = do
        (Code bodyTerm) <- f inTermTree
        return $ Code $ Lambda inVarList bodyTerm
        
Now I have to fill in that "undefined" with a bunch of variables that
have the right form, and translate them to a representation as a list.

One remark: the generated variable names need to come from the monad,
to avoid any possibility of capture, and also to make later
implementation simpler; meaning we can use global variables for
implementation without fear of clashing.

Maybe that's actually a better way of programming.  Write down the
types, use undefined for everything, and then fill in the blanks.

Forget about name generation for now, this can easily be added later.
Let's focus on the construction of proper variable trees.

class TreeVars ras where
  treeVars      :: ras -> ras
  treeVarNames  :: ras -> [Var]

instance Args r as ras => TreeVars ras where
  treeVars = undefined
  treeVarNames = undefined

instance Loop MCode' Code where
  _lambda = lambda where
    lambda :: forall as ras t. Args Code as ras => 
              (ras -> MCode (Code t)) -> MCode (Code (as -> MCode t))
    lambda f = term where
      inTermTree = treeVars (undefined :: ras)
      inVarList  = treeVarNames inTermTree      
      term = do
        (Code bodyTerm) <- f inTermTree
        return $ Code $ Lambda inVarList bodyTerm


Now, how to fill in the blanks?  The fact that there is no base case
for Args is probably still not a good thing..

Little tweaking, with proper varList interface:




class TreeVars ras where
  treeSize :: ras -> Int
  treeVars :: [Var] -> ras -> ras

instance Args r as ras => TreeVars ras where
  treeVars = undefined
  treeSize = undefined

instance Loop MCode' Code where      
  _lambda = lambda where
    lambda :: forall as ras t. Args Code as ras => 
              (ras -> MCode (Code t)) -> MCode (Code (as -> MCode t))
    lambda f = term where
      n = treeSize (undefined :: ras)
      term = do
        inVarList <- varList n
        let inTermTree = treeVars inVarList (undefined :: ras)
          in do
           (Code bodyTerm) <- f inTermTree
           return $ Code $ Lambda inVarList bodyTerm
        

So, what about putting this TreeVars straight into Args interface?
This means a little leakage to the Value implementation, but would
make things more straightforward.  Or.. can it go in the type
signature for lambda above?

If it goes into Args, then Args needs an extra type, which is the
compilation result type.  This is a bit ad-hoc..

Trouble is that the ability to call these methods on ras really needs
to be encoded in the context of _lambda, which means Args.  This
bleeds through to the Loop class, which needs a mention of the type it
will eventually be compiled to.

Let's just do it like that.  Will probably solve the problem.

Indeed:

termCompile m = runMCode m k where
  k _ (Code t) = t

f5 = _lambda $ \a -> _ret a

type C1 = MCode (Code (Tint -> MCode Tint))

> termCompile (f5 :: C1)
Lambda [Var i "t0" 0] (Ret [Var i "t0" 0])


But this one's no good:


f6 = _lambda $ \a -> do
  square <- mul a a
  _ret square

> termCompile (f6 :: C1)
Let (Var i "t1" 0) (Op i "mul" [Ref (Var i "t0" 0),Ref (Var i "t0" 0)]) (Lambda [Var i "t0" 0] (Ret [Var i "t1" 0]))

The Lambda needs to insert itself higher up.  Needs some monad
support, like makeVar.



[Reply][About]
[<<][meta][>>][..]