I’m playing around with Haskell and thought I’d try to create a simple programming language with it. Ignoring the concrete syntax for now; I am concentrating on the abstract syntax and the semantics.
The language currently should consist of integers, integer addition, variable names, and variable binding blocks.
If a variable is used that doesn’t exist in the scope it is used, an error is raised.
The following is my current progress:
module ProgLang where
import Data.Map as Map
-- Classes
class Runnable d where
run :: (Runnable a) => d -> Map String a -> Either [String] Integer
-- Data
data Name = Name String
deriving (Eq, Ord, Show)
data Add a b = Add a b
deriving (Eq, Ord, Show)
data Block a = Block (Map String a) a
deriving (Eq, Ord, Show)
-- Instances
-- Integers resolve to Right Integer
instance Runnable Integer where
run v _ = Right v
-- For Names
-- look up their expression in the scope, then evaluate
-- if name is out of scope, raise an error
instance Runnable Name where
run (Name n) s = which (Map.lookup n s) where
which Nothing = Left ["Variable not in scope: " ++ n]
which (Just v) = run v s
-- For Addition
-- Run a, Run b, Add their results
-- Raise appropriate errors where necessary
instance (Runnable a, Show a, Runnable b, Show b) => Runnable (Add a b) where
run (Add a b) s = geta (run a s) where
geta (Left es) = Left (es ++ ["In lhs of expression: " ++ show (Add a b)])
geta (Right a') = getb a' (run b s)
getb _ (Left es) = Left (es ++ ["In rhs of expression: " ++ show (Add a b)])
getb a' (Right b') = Right (a' + b')
-- For Blocks
-- Run the block's expression under a new scope
-- (merging the current with the block's scope definition)
instance Runnable a => Runnable (Block a) where
run (Block s' e) s = result $ run e (Map.union s' s) where
result (Left es) = Left (es ++ ["In block: " ++ show (Block s' e)])
result (Right v) = Right v
I am using (Runnable a) => Either [String] a as the outcome of run. Left for errors and Right for a valid result.
The following are example expressions and their expected outcome:
-- run 5 Map.empty
-- => Right 5
-- run (Name "a") Map.empty
-- => Left ["Variable not in scope: a"]
-- run (Name "a") (fromList [("a", 6)])
-- => Right 6
-- run (Add 6 3) Map.empty
-- => Right 9
-- run (Add (Name "a") 7) (fromList [("a", 10)])
-- => Right 17
-- run (Block (fromList [("a", 10)]) (Name "a")) Map.empty
-- => Right 10
I am getting the following error from GHCI (version 7.4.1):
progLang.hs:45:53:
Could not deduce (a1 ~ a)
from the context (Runnable a)
bound by the instance declaration at progLang.hs:44:10-41
or from (Runnable a1)
bound by the type signature for
run :: Runnable a1 =>
Block a -> Map String a1 -> Either [String] Integer
at progLang.hs:(45,3)-(47,30)
`a1' is a rigid type variable bound by
the type signature for
run :: Runnable a1 =>
Block a -> Map String a1 -> Either [String] Integer
at progLang.hs:45:3
`a' is a rigid type variable bound by
the instance declaration at progLang.hs:44:19
Expected type: Map String a1
Actual type: Map String a
In the second argument of `union', namely `s'
In the second argument of `run', namely `(union s' s)'
Failed, modules loaded: none.
This error (as far as I can tell) is due to the Block’s run function. It doesn’t seem to like the call to Map.union.
I’m unsure what I’m doing wrong. Any ideas? Should I be attempting a completely different approach to this project?
Thanks in advance.
The problem is the way that
runis declared.What you probably intended is that the second argument is a
MapfromStringto any runnables, mingled together in the same map. But what this actually means is that the second argument is aMapfromStringto one particular kind of runnable (it’s just not known which it is).Rather than using a typeclass and disparate types, try using a single type instead.
The only change I made to this code was the type declarations and reorganization of the
runfunction.If you add a dummy
Numinstance withfromInteger = Ithen you can also use integer literals asRunnables. Here’s a test run with the test cases you supplied, looks like all the expected outputs match: http://ideone.com/9UbC5.