Re: [Haskell-cafe] Beginning of a meta-Haskell [was: An issue with the ``finally tagless'' tradition]

2009-09-24 Thread Peter Gammie

Thanks Oleg!

Brad:

On 24/09/2009, at 3:54 PM, o...@okmij.org wrote:


and interpret it several times, as an integer


-- testpw :: Int
testpw = (unR power) (unR 2) ((unR 7)::Int)
-- 128


My type function allows one to remove the '::Int' annotation, which is  
especially useful in situations where you cannot give an annotation  
due to 'show . read'-style ambiguity. Conversely one gives up some  
useful polymorphism (in my case, sometimes it would be nice to have  
multiple boolean types).


Another nit with Oleg's code is that you can only interpret Bool with  
Bool, in the interpreter:



class QBool repr where
   true, false :: repr Bool
   if_ :: repr Bool - repr w - repr w - repr w


If 'repr' is merely a Haskell98-style type constructor, it cannot  
analyse its argument. Hence there are two choices: use the argument  
(Bool) or don't (see the pretty printer). I doubt you could implement  
a very pleasant interpreter using the latter option, but see


http://web.cecs.pdx.edu/~brianh/talks/talk20081010.pdf

if you want to try. Again, using a type function here allows you to  
choose an arbitrary type to represent Bool (and so forth). Trying to  
do this with fundeps is possible but syntactically heavy.


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


[Haskell-cafe] Beginning of a meta-Haskell [was: An issue with the ``finally tagless'' tradition]

2009-09-23 Thread oleg

The topic of an extensible, modular interpreter in the tagless final
style has come up before. A bit more than a year ago, on a flight from
Frankfurt to San Francisco I wrote two interpreters for a trivial
subset of Haskell or ML (PCF actually), just big enough for Power,
Fibonacci and other classic functions. The following code is a
fragment of meta-Haskell. It defines the object language and two
interpreters: one is the typed meta-circular interpreter, and the
other is a non-too-pretty printer. We can write the expression once:

 power =
   fix $ \self -
   lam $ \x - lam $ \n -
 if_ (n = 0) 1
 (x * ((self $$ x) $$ (n - 1)))

and interpret it several times, as an integer

 -- testpw :: Int
 testpw = (unR power) (unR 2) ((unR 7)::Int)
 -- 128

or as a string

 -- testpwc :: P.String
 testpwc = showQC power

{-
 (let self0 = (\\t1 - (\\t2 - (if (t2 = 0) then 1 else (t1 * ((self0  t1)  
(t2 - 1)) in self0)
-}

The code follows. It is essentially Haskell98, with the exception of
multi-parameter type classes (but no functional dependencies, let
alone overlapping instances).

{-# LANGUAGE NoMonomorphismRestriction, NoImplicitPrelude #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

-- A trivial introduction to `meta-Haskell', just enough to give a taste
-- Please see the tests at the end of the file

module Intro where

import qualified Prelude as P
import Prelude (Monad(..), (.), putStrLn, IO, Integer, Int, ($), (++),
(=), Bool(..))
import Control.Monad (ap)
import qualified Control.Monad.State as S

-- Definition of our object language
-- Unlike that in the tagless final paper, the definition here is spread
-- across several type classes for modularity

class QNum repr a where
(+) :: repr a - repr a - repr a
(-) :: repr a - repr a - repr a
(*) :: repr a - repr a - repr a
negate :: repr a - repr a
fromInteger :: Integer - repr a
infixl 6 +, -
infixl 7 *

class QBool repr where
true, false :: repr Bool
if_ :: repr Bool - repr w - repr w - repr w

class QBool repr = QLeq repr a where
(=) :: repr a - repr a - repr Bool
infix 4 =

-- Higher-order fragment of the language

class QHO repr  where
lam  :: (repr a - repr r) - repr (a - r)
($$) :: repr (a - r) - (repr a - repr r)
fix  :: (repr a - repr a) - repr a
infixr 0 $$

-- The first interpreter R -- which embeds the object language in
-- Haskell. It is a meta-circular interpreter, and so is trivial.
-- It still could be useful if we wish just to see the result
-- of our expressions, quickly
newtype R a = R{unR :: a}

instance P.Num a = QNum R a where
R x + R y = R $ x P.+ y
R x - R y = R $ x P.- y
R x * R y = R $ x P.* y
negate  = R . P.negate . unR
fromInteger = R . P.fromInteger

instance QBool R where
true  = R True
false = R False
if_ (R True)  x y = x
if_ (R False) x y = y

instance QLeq R Int where
R x = R y = R $ x P.= y

instance QHO R where
lam f  = R $ unR . f . R
R f $$ R x = R $ f x
fix f  = f (fix f)

-- The second interpreter: pretty-printer
-- Actually, it is not pretty, but sufficient

newtype S a = S{unS :: S.State Int P.String}

instance QNum S a where
S x + S y = S $ app_infix + x y
S x - S y = S $ app_infix - x y
S x * S y = S $ app_infix * x y
negate (S x) = S $ (return $ \xc - (negate  ++ xc ++ )) `ap` x
fromInteger = S . return . P.show

app_infix op x y = do
  xc - x
  yc - y
  return $ ( ++ xc ++   ++ op ++   ++ yc ++ )

instance QBool S where
true  = S $ return True
false = S $ return False
if_ (S b) (S x) (S y) = S $ do
bc - b
xc - x
yc - y
return $ (if  ++ bc ++  then  ++ xc ++ 
  else  ++ yc ++ )
instance QLeq S a where
S x = S y = S $ app_infix = x y

newName stem = do
  cnt - S.get
  S.put (P.succ cnt)
  return $ stem ++ P.show cnt
  
instance QHO S where
 S x $$ S y = S $ app_infix  x y

 lam f = S $ do
 name - newName t
 let xc = name
 bc - unS . f . S $ return xc
 return $ (\\ ++ xc ++  -  ++ bc ++ )

 fix f = S $ do
 self - newName self
 let sc = self
 bc - unS . f . S $ return sc
 return $ (let  ++ self ++  =  ++ bc ++  in  ++ sc ++ )

showQC :: S a - P.String
showQC (S m) = S.evalState m (unR 0)

-- 
--   Tests

-- Perhaps the first test should be the power function...
-- The following code can be interpreted and compiled just as it is...

power =
  fix $ \self -
  lam $ \x - lam $ \n -
if_ (n = 0) 1
(x * ((self $$ x) $$ (n - 1)))

-- The interpreted result
-- testpw :: Int
testpw = (unR power) (unR 2) ((unR 7)::Int)
-- 128

-- The result of compilation. 
-- testpwc :: P.String
testpwc = showQC