mikrokosmos 0.1.0 → 0.2.0
raw patch · 7 files changed
+354/−231 lines, 7 files
Files
- Interpreter.hs +197/−0
- Lambda.hs +5/−5
- Main.hs +64/−206
- MultiBimap.hs +2/−2
- NamedLambda.hs +55/−0
- README.md +25/−12
- mikrokosmos.cabal +6/−6
+ Interpreter.hs view
@@ -0,0 +1,197 @@+{-|+Module: Interpreter+Description: Internal logic of the interpreter+License: GPL-3++This module contains auxiliary logic, types and representations of+the internal state of the interpreter.+-}+module Interpreter+ ( Context+ , emptyContext+ , InterpreterOptions (InterpreterOptions)+ , defaultOptions+ , changeVerbose+ , changeColor+ , getVerbose+ , getColor+ , InterpreterAction (..)+ , interpreteractionParser+ , act+ , multipleAct+ , Action (..)+ , actionParser+ )+where++import Control.Applicative ((<$>), (<*>))+import Control.Monad.State.Lazy +import Text.ParserCombinators.Parsec hiding (State)+import Data.Char+import Data.List+import MultiBimap+import NamedLambda+import Format+import Lambda++-- | A context is an application between expressions and the names+-- they may have.+type Context = MultiBimap Exp String++-- | Empty context without any bindings+emptyContext :: Context+emptyContext = MultiBimap.empty+++-- Interpreter options+-- | Configuration options for the interpreter. They can be changed dinamically.+data InterpreterOptions = InterpreterOptions+ { verbose :: Bool -- ^ true to produce verbose output+ , color :: Bool -- ^ true to color the output+ }++-- | Default configuration options for the interpreter.+defaultOptions :: InterpreterOptions+defaultOptions = InterpreterOptions+ { verbose = False+ , color = True+ }++-- | Gets the verbose configuration+getVerbose :: InterpreterOptions -> Bool+getVerbose = verbose++-- | Gets the color configuration+getColor :: InterpreterOptions -> Bool+getColor = color++-- | Sets the verbose configuration on/off.+changeVerbose :: InterpreterOptions -> InterpreterOptions+changeVerbose options = options {verbose = not $ verbose options}++-- | Sets the color configuration on/off+changeColor :: InterpreterOptions -> InterpreterOptions+changeColor options = options {color = not $ color options}++++++-- | Interpreter action. It can be a language action (binding and evaluation)+-- or an interpreter specific one, such as "quit". +data InterpreterAction = Interpret Action -- ^ Language action+ | EmptyLine -- ^ Empty line, it will be ignored+ | Error -- ^ Error on the interpreter+ | Quit -- ^ Close the interpreter+ | Load String -- ^ Load the given file+ | SetVerbose -- ^ Changes verbosity+ | SetColors -- ^ Changes colors+ | Help -- ^ Shows help++-- | Language action. The language has a number of possible valid statements;+-- all on the following possible forms.+data Action = Bind (String, NamedLambda) -- ^ bind a name to an expression+ | EvalBind (String, NamedLambda) -- ^ bind a name to an expression and simplify it+ | Execute NamedLambda -- ^ execute an expression+ | Comment -- ^ comment+++-- | Executes a language action. Given a context and an action, returns+-- the new context after the action and a text output.+act :: Action -> State Context [String]+act Comment = return [""]+act (Bind (s,le)) =+ do modify (\ctx -> MultiBimap.insert (toBruijn ctx le) s ctx)+ return [""]+act (EvalBind (s,le)) =+ do modify (\ctx -> MultiBimap.insert (simplifyAll $ toBruijn ctx le) s ctx)+ return [""]+act (Execute le) =+ do context <- get+ return [unlines $+ [ show le ] +++ [ unlines $ map showReduction $ simplifySteps $ toBruijn context le ] +++ [ showCompleteExp context $ simplifyAll $ toBruijn context le ]+ ]+++-- TODO: Use Text instead of String for efficiency+-- | Executes multiple actions. Given a context and a set of actions, returns+-- the new context after the sequence of actions and a text output.+multipleAct :: [Action] -> State Context [String]+multipleAct actions = concat <$> mapM act actions++++-- | Shows an expression and the name that is bound to the expression+-- in the current context+showCompleteExp :: Context -> Exp -> String+showCompleteExp context expr = case getExpressionName context expr of+ Nothing -> show (nameExp expr)+ Just expName -> show (nameExp expr) ++ formatName ++ " ⇒ " ++ expName ++ end+++-- | Given an expression, returns its name if it is bounded to any.+getExpressionName :: Context -> Exp -> Maybe String+getExpressionName context expr = case MultiBimap.lookup expr context of+ [] -> Nothing+ xs -> Just $ intercalate ", " xs+++++-- Parsing of interpreter command line commands.+-- | Parses an interpreter action.+interpreteractionParser :: Parser InterpreterAction+interpreteractionParser = choice+ [ try interpretParser+ , try quitParser+ , try loadParser+ , try verboseParser+ , try helpParser+ ]++-- | Parses a language action as an interpreter action.+interpretParser :: Parser InterpreterAction+interpretParser = Interpret <$> actionParser++-- | Parses a language action.+actionParser :: Parser Action+actionParser = choice+ [ try bindParser+ , try evalbindParser+ , try executeParser+ , try commentParser+ ]++-- | Parses a binding between a variable an its representation.+bindParser :: Parser Action+bindParser = fmap Bind $ (,) <$> many1 alphaNum <*> (spaces >> string "!=" >> spaces >> lambdaexp)++-- | Parses a binding and evaluation expression between a variable an its representation+evalbindParser :: Parser Action+evalbindParser = fmap EvalBind $ (,) <$> many1 alphaNum <*> (spaces >> string "=" >> spaces >> lambdaexp)++-- | Parses an expression in order to execute it.+executeParser :: Parser Action+executeParser = Execute <$> lambdaexp++-- | Parses comments.+commentParser :: Parser Action+commentParser = string "#" >> many anyChar >> return Comment++-- | Parses a "quit" command.+quitParser :: Parser InterpreterAction+quitParser = string ":quit" >> return Quit++-- | Parses a "help" command.+helpParser :: Parser InterpreterAction+helpParser = string ":help" >> return Help++-- | Parses a change in verbosity.+verboseParser :: Parser InterpreterAction+verboseParser = string ":verbose" >> return SetVerbose++-- | Parses a "load-file" command.+loadParser :: Parser InterpreterAction+loadParser = Load <$> (string ":load" >> between spaces spaces (many1 (satisfy (not . isSpace))))
Lambda.hs view
@@ -3,9 +3,12 @@ Description: DeBruijn lambda expressions. License: GPL-3 -This package deals with the parsing, reduction and printing of lambda-expressions using DeBruijn notation.+This module deals with the parsing, reduction and printing of lambda+expressions using DeBruijn notation. The interpreter uses DeBruijn+notation as an internal representation and as output format. This is because+it is easier to do beta reduction with DeBruijn indexes. -}+ module Lambda ( Exp (Var, Lambda, App) , simplifyAll@@ -17,9 +20,6 @@ import Format -- DeBruijn Expressions--- The interpreter uses DeBruijn notation as an internal representation and--- as output format. It is easier to do beta reduction with DeBruijn indexes.- -- | A lambda expression using DeBruijn indexes. data Exp = Var Integer -- ^ integer indexing the variable. | Lambda Exp -- ^ lambda abstraction
Main.hs view
@@ -1,56 +1,21 @@ module Main where -import Control.Applicative ((<$>), (<*>)) import Control.Monad.Trans-import Data.Char-import Data.List-import qualified Data.Map.Strict as Map-import Data.Maybe+import Control.Monad.State+import Control.Exception import System.Environment import System.Console.Haskeline-import Text.ParserCombinators.Parsec+import Text.ParserCombinators.Parsec hiding (try) import Format-import MultiBimap-import NamedLambda-import Lambda+import Interpreter +-- | A filename is a string containing the directory path and+-- the real name of the file. type Filename = String-type Context = MultiBimap Exp String ----- | Translates a named variable expression into a DeBruijn one.--- Uses a dictionary of already binded numbers and variables.-tobruijn :: Map.Map String Integer -- ^ dictionary of the names of the variables used- -> Context -- ^ dictionary of the names already binded on the scope- -> NamedLambda -- ^ initial expression- -> Exp--- Every lambda abstraction is inserted in the variable dictionary,--- and every number in the dictionary increases to reflect we are entering--- into a deeper context.-tobruijn d context (LambdaAbstraction c e) = Lambda $ tobruijn newdict context e- where newdict = Map.insert c 1 (Map.map succ d)--- Translation of applications is trivial.-tobruijn d context (LambdaApplication f g) = App (tobruijn d context f) (tobruijn d context g)--- Every variable is checked on the variable dictionary and in the current scope.-tobruijn d context (LambdaVariable c) =- case Map.lookup c d of- Just n -> Var n- Nothing -> fromMaybe (Var 0) (MultiBimap.lookupR c context)---- | Transforms a lambda expression with named variables to a deBruijn index expression.--- Uses only the dictionary of the variables in the current context.-toBruijn :: Context -- ^ Variable context- -> NamedLambda -- ^ Initial lambda expression with named variables- -> Exp-toBruijn = tobruijn Map.empty---- -- Lambda interpreter--- The logic of the interpreter is written here. It allows to execute normal+-- The actions of the interpreter are written here. It allows to execute normal -- actions (bindings and evaluation), and interpreter specific actions, as -- "quit" or "load". @@ -59,99 +24,13 @@ main = do args <- getArgs case args of- [] -> runInputT defaultSettings (outputStrLn initialText- >> interpreterLoop defaultOptions emptyContext)+ [] -> runInputT defaultSettings ( outputStrLn initialText+ >> interpreterLoop defaultOptions emptyContext+ ) [filename] -> executeFile filename _ -> putStrLn "Wrong number of arguments" --- | Executes the commands inside a file. A .mkr file can contain a sequence of--- expressions and variable bindings, and it is interpreted sequentially.-executeFile :: Filename -- Input file- -> IO ()-executeFile filename = do- maybeloadfile <- loadFile filename- case maybeloadfile of- Nothing -> putStrLn "Error loading file"- Just actions -> case multipleAct emptyContext actions of- (_, outputs) -> mapM_ (putStr . format) outputs- where- format :: String -> String- format "" = ""- format s = (++"\n") . last . lines $ s- ---- | Empty context without any bindings-emptyContext :: Context-emptyContext = MultiBimap.empty----- | Configuration options for the interpreter. They can be changed dinamically.-data InterpreterOptions = InterpreterOptions { verbose :: Bool -- ^ true if produces verbose output- , color :: Bool -- ^ true if colors the output- }---- | Default config options-defaultOptions :: InterpreterOptions-defaultOptions = InterpreterOptions { verbose = False- , color = True- }---- | Interpreter action. It can be a language action (binding and evaluation)--- or an interpreter specific one, such as "quit". -data InterpreterAction = Interpret Action -- ^ Language action- | EmptyLine -- ^ Empty line, it will be ignored- | Error -- ^ Error on the interpreter- | Quit -- ^ Close the interpreter- | Load String -- ^ Load the given file- | SetVerbose -- ^ Changes verbosity- | SetColors -- ^ Changes colors- | Help -- ^ Shows help---- | Language action. The language has a number of possible valid statements;--- all on the following possible forms.-data Action = Bind (String, NamedLambda) -- ^ bind a name to an expression- | EvalBind (String, NamedLambda) -- ^ bind a name to an expression and simplify it- | Execute NamedLambda -- ^ execute an expression- | Comment -- ^ comment----- | Executes a language action. Given a context and an action, returns--- the new context after the action and a text output.-act :: Context -> Action -> (Context, String)-act context Comment = (context,"")-act context (Bind (s,le)) = (MultiBimap.insert (toBruijn context le) s context, "")-act context (EvalBind (s,le)) = (MultiBimap.insert (simplifyAll $ toBruijn context le) s context, "")-act context (Execute le) = (context,- unlines $- [ show le ] ++- [ unlines $ map showReduction $ simplifySteps $ toBruijn context le ] ++- [ showCompleteExp context $ simplifyAll $ toBruijn context le ]- )--showCompleteExp :: Context -> Exp -> String-showCompleteExp context expr = case getExpressionName context expr of- Nothing -> show expr- Just expName -> show expr ++ formatName ++ " ⇒ " ++ expName ++ end--getExpressionName :: Context -> Exp -> Maybe String-getExpressionName context expr = case MultiBimap.lookup expr context of- [] -> Nothing- xs -> Just $ intercalate ", " xs---- TODO: Writer monad--- TODO: Use Text instead of String for efficiency--- TODO: Lists of string are inefficient--- | Executes multiple actions. Given a context and a set of actions, returns--- the new context after the sequence of actions and a text output.-multipleAct :: Context -> [Action] -> (Context, [String])-multipleAct context = foldl (\(ccontext,text) action ->- (fst $ act ccontext action, text ++ [snd (act ccontext action)]))- (context,[])------ TODO: State Monad -- | Interpreter awaiting for an instruction. interpreterLoop :: InterpreterOptions -> Context -> InputT IO () interpreterLoop options context = do@@ -166,101 +45,80 @@ case interpreteraction of EmptyLine -> interpreterLoop options context Quit -> return ()- Error -> outputStrLn "Error"+ Error -> do+ outputStr formatFormula+ outputStrLn "Unknown command"+ outputStr end+ interpreterLoop options context SetVerbose -> do- outputStrLn $ "verbose mode: " ++ if verbose options then "off" else "on"- interpreterLoop (options {verbose = not $ verbose options}) context- SetColors -> interpreterLoop (options {color = not $ color options}) context+ outputStrLn $+ formatFormula +++ "verbose mode: " ++ if getVerbose options then "off" else "on" +++ end+ interpreterLoop (changeVerbose options) context+ SetColors -> interpreterLoop (changeColor options) context Help -> outputStr helpText >> interpreterLoop options context Load filename -> do maybeloadfile <- lift $ loadFile filename case maybeloadfile of- Nothing -> outputStrLn "Error loading file"- Just actions -> case multipleAct context actions of- (ccontext, outputs) -> do- outputStr formatFormula- outputActions options outputs- outputStr end+ Nothing -> do+ outputStrLn "Error loading file"+ interpreterLoop options context+ Just actions -> case runState (multipleAct actions) context of+ (output, ccontext) -> do+ outputActions options output interpreterLoop options ccontext- Interpret action -> case act context action of- (ccontext, output) -> do- outputStr formatFormula- outputActions options [output]- outputStr end+ Interpret action -> case runState (act action) context of+ (output, ccontext) -> do+ outputActions options output interpreterLoop options ccontext +++ -- | Outputs results from actions. Given a list of options and outputs, -- formats and prints them in console. outputActions :: InterpreterOptions -> [String] -> InputT IO ()-outputActions options = mapM_ (outputStr . format)+outputActions options output = do+ outputStr formatFormula+ mapM_ (outputStr . format) output+ outputStr end where format :: String -> String format "" = "" format s- | not (verbose options) = (++"\n") . last . lines $ s+ | not (getVerbose options) = (++"\n") . last . lines $ s | otherwise = s ++++-- Loading and reading files -- | Loads the given filename and returns the complete list of actions. -- Returns Nothing if there is an error reading or parsing the file. loadFile :: String -> IO (Maybe [Action]) loadFile filename = do putStrLn filename- input <- readFile filename- let parsing = map (parse actionParser "") $ filter (/="") $ lines input- let actions = map (\x -> case x of- Left _ -> Nothing- Right a -> Just a) parsing- return $ sequence actions---- | Parses an interpreter action.-interpreteractionParser :: Parser InterpreterAction-interpreteractionParser = choice [ try interpretParser- , try quitParser- , try loadParser- , try verboseParser- , try helpParser- ]---- | Parses a language action as an interpreter action.-interpretParser :: Parser InterpreterAction-interpretParser = Interpret <$> actionParser---- | Parses a language action.-actionParser :: Parser Action-actionParser = choice [ try bindParser- , try evalbindParser- , try executeParser- , try commentParser- ]---- | Parses a binding between a variable an its representation.-bindParser :: Parser Action-bindParser = fmap Bind $ (,) <$> many1 alphaNum <*> (spaces >> string "!=" >> spaces >> lambdaexp)---- | Parses a binding and evaluation expression between a variable an its representation-evalbindParser :: Parser Action-evalbindParser = fmap EvalBind $ (,) <$> many1 alphaNum <*> (spaces >> string "=" >> spaces >> lambdaexp)---- | Parses an expression in order to execute it.-executeParser :: Parser Action-executeParser = Execute <$> lambdaexp---- | Parses comments.-commentParser :: Parser Action-commentParser = string "#" >> many anyChar >> return Comment---- | Parses a "quit" command.-quitParser :: Parser InterpreterAction-quitParser = string ":quit" >> return Quit---- | Parses a "help" command.-helpParser :: Parser InterpreterAction-helpParser = string ":help" >> return Help---- | Parses a change in verbosity.-verboseParser :: Parser InterpreterAction-verboseParser = string ":verbose" >> return SetVerbose+ input <- try $ (readFile filename) :: IO (Either IOException String)+ case input of+ Left _ -> return Nothing+ Right inputs -> do+ let parsing = map (parse actionParser "") $ filter (/="") $ lines inputs+ let actions = map (\x -> case x of+ Left _ -> Nothing+ Right a -> Just a) parsing+ return $ sequence actions --- | Parses a "load-file" command.-loadParser :: Parser InterpreterAction-loadParser = Load <$> (string ":load" >> between spaces spaces (many1 (satisfy (not . isSpace))))+-- | Executes the commands inside a file. A .mkr file can contain a sequence of+-- expressions and variable bindings, and it is interpreted sequentially.+executeFile :: Filename -> IO ()+executeFile filename = do+ maybeloadfile <- loadFile filename+ case maybeloadfile of+ Nothing -> putStrLn "Error loading file"+ Just actions -> case runState (multipleAct actions) emptyContext of+ (outputs, _) -> mapM_ (putStr . format) outputs+ where+ format :: String -> String+ format "" = ""+ format s = (++"\n") . last . lines $ s
MultiBimap.hs view
@@ -44,9 +44,9 @@ -- | Lookup a key in the multi-bimap, returning the list of -- associated values.-lookup :: (Ord k, Ord v) => k -> MultiBimap k v -> [v]+lookup :: (Ord k) => k -> MultiBimap k v -> [v] lookup k (MkMultiBimap left _) = MM.lookup k left -- | Lookup a right value in the multi-bimap, returning the associated key.-lookupR :: (Ord k, Ord v) => v -> MultiBimap k v -> Maybe k+lookupR :: (Ord v) => v -> MultiBimap k v -> Maybe k lookupR v (MkMultiBimap _ right) = M.lookup v right
NamedLambda.hs view
@@ -10,12 +10,21 @@ module NamedLambda ( NamedLambda (LambdaVariable, LambdaAbstraction, LambdaApplication) , lambdaexp+ , toBruijn+ , nameExp ) where import Text.ParserCombinators.Parsec import Control.Applicative ((<$>), (<*>))+import qualified Data.Map.Strict as Map+import Lambda+import MultiBimap+import Data.Maybe+import Control.Monad +type Context = MultiBimap Exp String+ -- Parsing of Lambda Expressions. -- The user can input a lambda expression with named variables, of -- the form of "\x.x" or "(\a.(\b.a b))". The interpreter will parse@@ -76,3 +85,49 @@ show = showNamedLambda +++-- | Translates a named variable expression into a DeBruijn one.+-- Uses a dictionary of already binded numbers and variables.+tobruijn :: Map.Map String Integer -- ^ dictionary of the names of the variables used+ -> Context -- ^ dictionary of the names already binded on the scope+ -> NamedLambda -- ^ initial expression+ -> Exp+-- Every lambda abstraction is inserted in the variable dictionary,+-- and every number in the dictionary increases to reflect we are entering+-- into a deeper context.+tobruijn d context (LambdaAbstraction c e) = Lambda $ tobruijn newdict context e+ where newdict = Map.insert c 1 (Map.map succ d)+-- Translation of applications is trivial.+tobruijn d context (LambdaApplication f g) = App (tobruijn d context f) (tobruijn d context g)+-- Every variable is checked on the variable dictionary and in the current scope.+tobruijn d context (LambdaVariable c) =+ case Map.lookup c d of+ Just n -> Var n+ Nothing -> fromMaybe (Var 0) (MultiBimap.lookupR c context)++-- | Transforms a lambda expression with named variables to a deBruijn index expression.+-- Uses only the dictionary of the variables in the current context. +toBruijn :: Context -- ^ Variable context+ -> NamedLambda -- ^ Initial lambda expression with named variables+ -> Exp+toBruijn = tobruijn Map.empty++++-- | Translates a deBruijn expression into a lambda expression+-- with named variables, given a list of used and unused variable names.+nameIndexes :: [String] -> [String] -> Exp -> NamedLambda+nameIndexes _ _ (Var 0) = LambdaVariable "undefined"+nameIndexes used _ (Var n) = LambdaVariable (used !! pred (fromInteger n))+nameIndexes used new (Lambda e) = LambdaAbstraction (head new) (nameIndexes (head new:used) (tail new) e)+nameIndexes used new (App f g) = LambdaApplication (nameIndexes used new f) (nameIndexes used new g)++-- | Gives names to every variable in a deBruijn expression using+-- alphabetic order.+nameExp :: Exp -> NamedLambda+nameExp = nameIndexes [] variableNames++-- | A list of all possible variable names in lexicographical order.+variableNames :: [String]+variableNames = concatMap (`replicateM` ['a'..'z']) [1..]
README.md view
@@ -12,37 +12,48 @@ ## Installation -Mikrokosmos will be soon installable from [Hackage](http://hackage.haskell.org/). Meanwhile, you can install it -cloning the repository and using [cabal](https://www.haskell.org/cabal/):+Mikrokosmos is installable from [Hackage](http://hackage.haskell.org/); you can install it directly from `cabal`: +```+cabal update+cabal install mikrokosmos+``` +You can also install it by cloning the git repository and using [cabal](https://www.haskell.org/cabal/):+ ``` bash git clone https://github.com/M42/mikrokosmos.git cd mikrokosmos cabal install ``` +If you have `ghc` version 8 or greater you can also compile it directly using:++``` bash+git clone https://github.com/M42/mikrokosmos.git+cd mikrokosmos+ghc Main.hs+```+ ## First steps Once installed, you can open the interpreter typing `mikrokosmos` in your terminal. It will show you a prompt where you can write lambda expressions to evaluate them: -+ You can write expressions using `\var.` to denote a lambda abstraction on the `var` variable and-you can bind names to expressions using `=`. *But why am I getting this weird output?* Well, the interpreter-outputs the lambda expressions in [De Bruijn notation](https://en.wikipedia.org/wiki/De_Bruijn_notation); it is more-compact and the interpreter works internally with it. However, as you can see in the image, whenever the interpreter finds a known constant, it labels the expression with its name.+you can bind names to expressions using `=`. As you can see in the image, whenever the interpreter finds a known constant, it labels the expression with its name. If you need help at any moment, you can type `:help` into the prompt to get a summary of the available options: -+ ## The standard library -Mikrokosmos comes bundled with a standard library. It allows you to experiment with Church encoding of booleans,-integers and much more. You can load it with `:load std.mkr`; after that, you can use a lot of new constants:+Mikrokosmos comes bundled with a standard library in a file called `std.mkr`; if it was not the case for you, you can download the [library](https://raw.githubusercontent.com/M42/mikrokosmos/master/std.mkr) from the git repository. It allows you to experiment with [Church encoding](https://en.wikipedia.org/wiki/Church_encoding) of booleans,+integers and much more. You can load it with `:load std.mkr`, given the file is in your working directory; after that, you can use a lot of new constants: -+ All this is written in lambda calculus! You can check the definitions on the `std.mkr` file. @@ -52,14 +63,16 @@ It can be activated and deactivated writing `:verbose`, and it will show you every step on the reduction of the expression, coloring the substitution at every step. -+ +It uses [DeBruijn notation](https://en.wikipedia.org/wiki/De_Bruijn_notation) to show the substitutions, because this is the internal representation used by the interpreter. The term in red is being substituted by the term in yellow. + ## Advanced data structures There are representations of structures such as linked lists or trees in the standard library. You can use them to do a bit of your usual functional programming: -+ Oh! And you can insert comments with `#`, both in the interpreter and in the files the interpreter can load.
mikrokosmos.cabal view
@@ -1,5 +1,5 @@ name: mikrokosmos-version: 0.1.0+version: 0.2.0 synopsis: Lambda calculus interpreter description: A didactic untyped lambda calculus interpreter. homepage: https://github.com/M42/mikrokosmos@@ -12,13 +12,15 @@ build-type: Simple extra-source-files: README.md cabal-version: >=1.10-tested-with: GHC == 7.8.3+tested-with: GHC == 8.0.1 extra-source-files: std.mkr source-repository head type: git location: git://github.com/M42/mikrokosmos.git- +++ executable mikrokosmos main-is: Main.hs build-depends: base >=4.7 && <5,@@ -34,9 +36,7 @@ Lambda NamedLambda MultiBimap+ Interpreter default-language: Haskell2010 ghc-options: -Wall---