Re: [Haskell-cafe] CBN, CBV, Lazy in the same final tagless framework

2009-10-15 Thread Jacques Carette
Just a short note to show how the 3 evaluation orders can be written in 
a very symmetric manner:


o...@okmij.org wrote these as:

In call-by-name, we have
lam f   = S . return $ (unS . f . S)

In call-by-value, we have
lam f   = S . return $ (\x - x = unS . f . S . return)

In call-by-need, we have
lam f   = S . return $ (\x - share x = unS . f . S)
  

These can be rewritten as
call-by-name (eta-expanded and application made visible):
   lam f = S . return $ (\x - unS . f . S  $ x)
call-by-value (flip)
   lam f = S . return $ (\x - unS . f . S . return = x)
call-by-need (flip)
   lam f = S . return $ (\x - unS . f . S = share x )

This pushes us to write two helper functions:
execS  :: (IO a - IO b) - IO a - IO b
execS g x = g = share x

execM :: Monad m = (m a - m b) - m a - m b
execM g x = g . return = x

And now, with the magic of slices, we can truly display those 3 in 
highly symmetric fashion:

call-by-name:
   lam f = S . return $ ((unS . f . S) $)
call-by-value (flip)
   lam f = S . return $ ((unS . f . S) `execM`)
call-by-need (flip)
   lam f = S . return $ ((unS . f . S ) `execS`)
(the redundant $ is left in to make the symmetry explicit)

And now we see the pattern:
   lam f = wrap . lift $ ((unwrap . f . wrap) `apply`)
where the names above are meant to be suggestive rather than 'actual' names.

Jacques
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] CBN, CBV, Lazy in the same final tagless framework

2009-10-09 Thread Felipe Lessa
On Thu, Oct 08, 2009 at 12:54:14AM -0700, o...@okmij.org wrote:
 Actually it is possible to implement all three evaluation orders
 within the same final tagless framework, using the same interpretation
 of types and reusing most of the code save the semantics of lam. That
 is where the three orders differ, by their own definition.

That's really nice, Oleg, thanks!  I just wanted to comment that
I'd prefer to write

share :: IO a - IO (IO a)
share m = mdo r - newIORef (do x - m
writeIORef r (return x)
return x)
  return (readIORef r = id)

which unfortunately needs {-# LANGUAGE RecursiveDo #-} or
some ugliness from mfix

share :: IO a - IO (IO a)
share m = do r - mfix $ \r - newIORef (do x - m
writeIORef r (return x)
return x)
 return (readIORef r = id)

Thanks,

--
Felipe.
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] CBN, CBV, Lazy in the same final tagless framework

2009-10-09 Thread David Menendez
On Fri, Oct 9, 2009 at 11:12 AM, Felipe Lessa felipe.le...@gmail.com wrote:
 On Thu, Oct 08, 2009 at 12:54:14AM -0700, o...@okmij.org wrote:
 Actually it is possible to implement all three evaluation orders
 within the same final tagless framework, using the same interpretation
 of types and reusing most of the code save the semantics of lam. That
 is where the three orders differ, by their own definition.

 That's really nice, Oleg, thanks!  I just wanted to comment that
 I'd prefer to write

 share :: IO a - IO (IO a)
 share m = mdo r - newIORef (do x - m
                                writeIORef r (return x)
                                return x)
              return (readIORef r = id)

 which unfortunately needs {-# LANGUAGE RecursiveDo #-} or
 some ugliness from mfix

 share :: IO a - IO (IO a)
 share m = do r - mfix $ \r - newIORef (do x - m
                                            writeIORef r (return x)
                                            return x)
             return (readIORef r = id)


Alternatively,

share m = do
r - newIORef undefined
writeIORef r $ do
x - m
writeIORef r (return x)
return x
return $ readIORef r = id

Which is basically the same as your version, but only needs one IORef.

-- 
Dave Menendez d...@zednenem.com
http://www.eyrie.org/~zednenem/
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] CBN, CBV, Lazy in the same final tagless framework

2009-10-09 Thread Felipe Lessa
On Fri, Oct 09, 2009 at 01:27:57PM -0400, David Menendez wrote:
 On Fri, Oct 9, 2009 at 11:12 AM, Felipe Lessa felipe.le...@gmail.com wrote:
  That's really nice, Oleg, thanks!  I just wanted to comment that
  I'd prefer to write
 
  share :: IO a - IO (IO a)
  share m = mdo r - newIORef (do x - m
                                 writeIORef r (return x)
                                 return x)
               return (readIORef r = id)
 
  which unfortunately needs {-# LANGUAGE RecursiveDo #-} or
  some ugliness from mfix
 
  share :: IO a - IO (IO a)
  share m = do r - mfix $ \r - newIORef (do x - m
                                             writeIORef r (return x)
                                             return x)
              return (readIORef r = id)
 

 Alternatively,

 share m = do
 r - newIORef undefined
 writeIORef r $ do
 x - m
 writeIORef r (return x)
 return x
 return $ readIORef r = id

 Which is basically the same as your version, but only needs one IORef.

Hmmm, but my version also needs only one IORef, right?  In fact I
first wrote the same code as yours but I've frowned upon the need
of having that 'undefined' and an extra 'writeIORef'.

Thanks,

--
Felipe.
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] CBN, CBV, Lazy in the same final tagless framework

2009-10-09 Thread David Menendez
On Fri, Oct 9, 2009 at 1:39 PM, Felipe Lessa felipe.le...@gmail.com wrote:
 On Fri, Oct 09, 2009 at 01:27:57PM -0400, David Menendez wrote:
 On Fri, Oct 9, 2009 at 11:12 AM, Felipe Lessa felipe.le...@gmail.com wrote:
  That's really nice, Oleg, thanks!  I just wanted to comment that
  I'd prefer to write
 
  share :: IO a - IO (IO a)
  share m = mdo r - newIORef (do x - m
                                 writeIORef r (return x)
                                 return x)
               return (readIORef r = id)
 
  which unfortunately needs {-# LANGUAGE RecursiveDo #-} or
  some ugliness from mfix
 
  share :: IO a - IO (IO a)
  share m = do r - mfix $ \r - newIORef (do x - m
                                             writeIORef r (return x)
                                             return x)
              return (readIORef r = id)
 

 Alternatively,

 share m = do
     r - newIORef undefined
     writeIORef r $ do
         x - m
         writeIORef r (return x)
         return x
     return $ readIORef r = id

 Which is basically the same as your version, but only needs one IORef.

 Hmmm, but my version also needs only one IORef, right?  In fact I
 first wrote the same code as yours but I've frowned upon the need
 of having that 'undefined' and an extra 'writeIORef'.

It's in the implementation of mfix for IO. From System.IO,

fixIO :: (a - IO a) - IO a
fixIO k = do
ref - newIORef (throw NonTermination)
ans - unsafeInterleaveIO (readIORef ref)
result - k ans
writeIORef ref result
return result

If we inline that into your definition, we get

share m = do
ref - newIORef (throw NonTermination)
ans - unsafeInterleaveIO (readIORef ref)
r - newIORef $ do { x - m; writeIORef ans (return x); return x }
writeIORef ref r
return (readIORef r = id)

So behind the scenes, the mfix version still creates an IORef with
undefined and has an extra writeIORef.

It also has that unsafeInterleaveIO, but I don't think there's any way
that can cause a problem.


Incidentally, none of the versions of share discussed so far are
thread-safe. Specifically, if a second thread starts to evaluate the
result of share m while the first thread is still evaluating m, we end
up with the effects of m happening twice. Here's a version that avoids
this by using a semaphore.

share m = do
r - newIORef undefined
s - newMVar False
writeIORef r $ do
b - takeMVar s
if b
then do
putMVar s True
readIORef r = id
else do
x - m
writeIORef r (return x)
putMVar s True
return x
return $ readIORef r = id

In the worst case, MVar will get read at most once per thread, so the
overhead is limited. Under normal circumstances, the MVar will be read
once and then discarded.

-- 
Dave Menendez d...@zednenem.com
http://www.eyrie.org/~zednenem/
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] CBN, CBV, Lazy in the same final tagless framework

2009-10-09 Thread Martijn van Steenbergen

Felipe Lessa wrote:

which unfortunately needs {-# LANGUAGE RecursiveDo #-} or
some ugliness from mfix


But mdo/mfix is awesome! :-(

Martijn.

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] CBN, CBV, Lazy in the same final tagless framework

2009-10-08 Thread oleg

Actually it is possible to implement all three evaluation orders
within the same final tagless framework, using the same interpretation
of types and reusing most of the code save the semantics of lam. That
is where the three orders differ, by their own definition.

In call-by-name, we have
lam f   = S . return $ (unS . f . S)

In call-by-value, we have
lam f   = S . return $ (\x - x = unS . f . S . return)

In call-by-need, we have
lam f   = S . return $ (\x - share x = unS . f . S)

In CBV, the function first evaluates its argument, whether the value
will be needed or not. In call-by-need, we arrange for the sharing of
the result should the computation be evaluated.

Here is an illustrative test

t2 :: HOAS exp = exp IntT
t2 = (lam $ \z - lam $ \x - let_ (x `add` x)
  $ \y - y `add` y) `app` (int 100 `sub` int 10)
 `app` (int 5 `add` int 5)

t2SN = runName t2 = print
{-
*CB t2SN
Adding
Adding
Adding
Adding
Adding
Adding
Adding
40
-}
In CBN, the result of subtraction was not needed, and so it was not performed
OTH, (int 5 `add` int 5) was computed four times.


t2SV = runValue t2 = print
{-
*CB t2SV
Subtracting
Adding
Adding
Adding
40
-}
In CBV, although the result of subtraction was not needed, it was
still performed. OTH, (int 5 `add` int 5) was computed only once.

t2SL = runLazy t2 = print
{-
*CB t2SL
Adding
Adding
Adding
40
-}
Now, Lazy is better than both CBN and CBV: subtraction was not needed,
and it was not performed.  All other expressions were needed, and
evaluated once.

The complete code is at
http://okmij.org/ftp/tagless-final/CB.hs


___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe