What's wrong with this idea for representing hot dogs?
data HotDog = Meat MeatType
| Bun BunType
| Spicy Bool
| Length Double
This data declaration implies that a HotDog is either just Meat OR a Bun OR Spicy Or a specific Length. (Note how this differs from class data members in an object-oriented programming language like Java. In Haskell, can have multiple pieces of data at once using a tuple. Haskell actually supports tuples with named fields, known as record types, but we have not used them in this class.)
Wiki Extra: Datatypes Quiz
Describe all the parts of this declaration...
data GrutorFood = Thai ThaiPlace [ThaiDish]
| Burger BurgerPlace Int Bool Bool
| Pizza PizzaPlace Int [Topping]
| Spicy GrutorFood
| Vegan GrutorFood
deriving (Show, Eq, Ord)
Thai, Burger, Pizza, Spicy, and Vegan are all Value Constructors or Case Labels or Tags
Show, Eq, and Ord are examples of Type Classes
ThaiPlace [ThaiDish], BurgerPlace Int Bool Bool, PizzaPlace Int [Topping], GrutorFood are all Types
data is a reserved keyword
More Data Declarations
Electrical Circuit
-- Represents an electric circuit. Assume the circuit is closed
-- (the end is connected to the beginning). Ground, Input, and
-- Output are exceptions.
data Circuit = -- A plain wire, equivalent to (R 0).
Epsilon
-- The wire is grounded and the circuit is not closed.
| Ground
-- A voltage source determined by external input.
| Input (a -> Double)
-- A voltage sink which determines an external output.
| Output (Double -> a)
-- A component facing the other way.
| Backward Circuit
-- Two components in series.
| Series Circuit Circuit
-- Two components sharing a beginning and end.
| Parallel Circuit Circuit
-- A battery. The Double determines voltage.
| Battery Double
-- An AC battery. The Doubles are voltage and frequency.
| ACBattery Double Double
-- A resistor.
| R Double
-- An inductor.
| L Double
-- A capacitor.
| C Double
-- A switch. The Bool is its state (On/Off).
| Switch Bool
deriving (Show, Eq, Ord)
This is ugly, so when testing our code in class, instead of typing this in directly we have been using parsers made by Prof. Melissa.
To parse is to go from concrete syntax to abstract syntax.
In the real world, it is possible to use tools to help write parsers. We won't use these tools in this class, instead, for educational purposes, we'll build our own from scratch.
Abstract Syntax for the Lambda Calculus
type VarName = String
data LambdaExpr = Var VarName
| Lambda VarName LambdaExpr
| Apply LambdaExpr LambdaExpr
deriving (Show, Eq, Ord)
Examples: Converting from Concrete to Abstract by Hand
Concrete Syntax:
(λx.(x y))
Abstract Syntax:
Lambda "x" (Apply (Var "x") (Var "y"))
What are some ways to specify what people can say? (i.e. how do we specify Concrete Syntax for Lambda Calculus?)
Ans: We need a grammar.
A Grammar for the Lambda Calculus
Here's our (required parentheses) grammar for the lambda calculus.
Note:
isSpace:: Char -> Bool
isAlpha:: Char -> Bool
span:: (Char->Bool) -> String -> (String, String)
takes in a function f (that takes in a character and returns a boolean) and a string x, and returns (a,b)
where a is the first part of x that satisfies f, and b is the rest of the string x
Demo
*LambdaLexer> tokenize ". ( ) λ x y z abc"
[DotTok,LParenTok,RParenTok,LambdaTok,IdentTok "x",IdentTok "y",IdentTok "z",IdentTok "abc"]
*LambdaLexer> tokenize ".()λxyzabc"
[DotTok,LParenTok,RParenTok,LambdaTok,IdentTok "xyzabc"]
*LambdaLexer> tokenize "(λx.(λy.(x y)))"
[LParenTok,LambdaTok,IdentTok "x",DotTok,LParenTok,LambdaTok,IdentTok "y",DotTok,LParenTok,IdentTok "x",IdentTok "y",RParenTok,RParenTok,RParenTok]
Parsing
parse is a function that takes in a list of lambda tokens L, returns the first lambda expression, and the rest of the tokens in L
parse :: [LambdaToken] -> (LambdaExpr, [LambdaToken])
parse (IdentTok vname : rest) = (Var vname, rest)
parse (LParenTok : rest) =
let (result, remainder) =
case rest of
LambdaTok : IdentTok vname : DotTok : rest2 ->
let (body, rest3) = parse rest2
in (Lambda vname body, rest3)
_ ->
let (func, rest2) = parse rest
(arg, rest3) = parse rest2
in (Apply func arg, rest3)
in case remainder of
RParenTok : toe -> (result, toe)
_ -> error "')' expected"
parse _ = error "Lambda expression expected"
We have completed Tokenization and Parsing only using recursion! Next time we will do the same thing in an easier way but with better infrastructure.
There is a runnable repl.it demo of this code.
Run