Saturday, October 6, 2012

An example use of Haskell Views

In this post I'm going to show how I'm using Haskell Views in a small Scheme implementation to deal with syntactic sugar.

One example of syntactic sugar in Scheme is the way to define functions. You can do it the following way:


(define square (lambda (x) (* x x))
But you can also use the following shortcut:

(define (square x) (* x x))

I'm going to take advantage of Haskell Views to simplify the implementation of this feature. The following code shows the data type representing Scheme code.


       data Expr a where
               ScSymbol :: String -> Expr a
               ScString :: String -> Expr a
               ScNumber :: Integer -> Expr a
               ScDouble :: Double -> Expr a 
               ScCons :: (Expr a) -> (Expr a) -> (Expr a)
               ScNil :: Expr a
               ScBool :: Bool -> Expr a
               ScQuote :: (Expr a) -> Expr a
               ScEnv ::  (Expr a)
               ScChar :: Char -> Expr a
               ScPort :: Handle -> Expr a
               ScClosure :: ScExecutable a =>  [String] -> a -> (Env a) -> Expr a
               ScPrimitive ::  ([Expr a] -> ScInterpreterMonad (Expr a))  -> Expr a

As a side note,I'm using ScCons/ScNil to represent lists which is different from the way "Write Yourself a Scheme in 48 Hours" defines them (a wrapper around a Haskell list). This decision turned out to be incorrect since manipulating a Haskell list is easier.

Given Scheme datatype definition we can represent the following function definition:

(define (id x) x)

As the following structure:


  (ScCons
     (ScSymbol "define")
     (ScCons
        (ScCons
           (ScSymbol "id")   
           (ScCons
              (ScSymbol "x")
              ScNil))
        (ScCons
           (ScSymbol "x")
           ScNil)))

And the following code:

(define id (lambda (x) x))

Using the following structure


  (ScCons
     (ScSymbol "define")
     (ScCons
        (ScSymbol "id")
        (ScCons
           (ScCons
              (ScSymbol "lambda")
              (ScCons
                 (ScCons
                    (ScSymbol "x")
                    ScNil)
                 (ScCons
                    (ScSymbol "x")
                    ScNil)))
           ScNil)))

Using Haskell Views we can create a view that returns the desired parts:


defineView :: (Expr a) -> Maybe (String, Expr a)
defineView  (ScCons 
             (ScSymbol "define")
             (ScCons 
                 name@(ScSymbol symbolToDefine)
                 (ScCons
                    value
                    ScNil))) = Just (symbolToDefine, value)
defineView  (ScCons 
                (ScSymbol "define")
                (ScCons 
                   (ScCons 
                      name@(ScSymbol symbolToDefine)
                      args)
                   (ScCons
                       value
                       ScNil))) = Just (symbolToDefine, 
                                        (ScCons (ScSymbol "lambda") 
                                                (ScCons args 
                                                        (ScCons value ScNil))))
defineView _ = Nothing

As shown above, this view will extract the parts of the original expression or create the necessary elements in the case of the function definition. Also it will fail in case of non supported syntax.

This view is used in the following code:


prepare (defineView -> Just (symbolToDefine, value)) = 
         do
            preparedValue <- prepare value
            return $ ScSeqDefine symbolToDefine preparedValue 

Code for this post is available in the ToyScheme repo.

No comments: