Cleanup: fix warnings, fix indentation, upgrade dependencies.

master
James T. Martin 2021-03-05 19:04:06 -08:00
parent 59a55acdc6
commit f73e78fcdb
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
11 changed files with 195 additions and 140 deletions

View File

@ -3,9 +3,6 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 4 indent_size = 2
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.yml]
indent_size = 2

View File

@ -10,8 +10,8 @@ jobs:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Install latest Haskell Stack - name: Install Haskell toolchain
uses: actions/setup-haskell@v1.1 uses: haskell/actions/setup@v1
with: with:
enable-stack: true enable-stack: true

View File

@ -1,4 +1,3 @@
{-# LANGUAGE LambdaCase #-}
module Main where module Main where
import Control.Monad (forever) import Control.Monad (forever)

View File

@ -15,18 +15,38 @@ extra-source-files:
default-extensions: default-extensions:
- BlockArguments - BlockArguments
- ImportQualifiedPost - ImportQualifiedPost
- LambdaCase
- OverloadedStrings - OverloadedStrings
- PatternSynonyms
- ViewPatterns - ViewPatterns
dependencies: dependencies:
- base >= 4.13 && < 5 - base >= 4.14 && < 5
- parsec >= 3.1 && < 4 - parsec >= 3.1 && < 4
- text >= 1.2 && < 2 - text >= 1.2 && < 2
- text-show >= 3.8 && < 4 - text-show >= 3.9 && < 4
- unordered-containers >= 0.2.10 && < 0.3 - unordered-containers >= 0.2.13 && < 0.3
library: library:
source-dirs: src source-dirs: src
ghc-options:
- -Weverything
# Useless Safe Haskell warnings
- -Wno-missing-safe-haskell-mode
- -Wno-unsafe
- -Wno-safe
# Other stupid warnings
- -Wno-implicit-prelude
- -Wno-missing-deriving-strategies
# Less stupid warnings, but I still don't care
- -Wno-unused-do-bind
- -Wno-all-missed-specialisations
# Explicit import lists are generally a good thing, but I don't want them
# in certain cases (e.g. importing my own modules, task-specific modules like the parser).
- -Wno-missing-import-lists
# I intentionally include unused top-level bindings
# as a way of documenting and explaining concepts.
- -Wno-unused-top-binds
executables: executables:
jtm-lambda-calculus: jtm-lambda-calculus:
@ -50,7 +70,7 @@ tests:
dependencies: dependencies:
- jtm-lambda-calculus - jtm-lambda-calculus
- generic-random >= 1.2 && < 2 - generic-random >= 1.2 && < 2
- QuickCheck >= 2.13 && < 3 - QuickCheck >= 2.14 && < 3
- tasty >= 1.2 && < 2 - tasty >= 1.2 && < 2
- tasty-hunit >= 0.10 && < 0.11 - tasty-hunit >= 0.10 && < 0.11
- tasty-quickcheck >= 0.10.1 && < 0.11 - tasty-quickcheck >= 0.10.1 && < 0.11

View File

@ -1,7 +1,7 @@
module LambdaCalculus module LambdaCalculus
( module LambdaCalculus.Expression ( module LambdaCalculus.Expression
, eagerEval, lazyEval , eagerEval, lazyEval
) where ) where
import Data.List (elemIndex, find) import Data.List (elemIndex, find)
import Data.Maybe (fromJust) import Data.Maybe (fromJust)
@ -38,47 +38,51 @@ closed = HS.null . freeVariables
-- i.e. one can be converted to the other using only alpha-conversion. -- i.e. one can be converted to the other using only alpha-conversion.
alphaEquivalent :: Expression -> Expression -> Bool alphaEquivalent :: Expression -> Expression -> Bool
alphaEquivalent = alphaEquivalent' [] [] alphaEquivalent = alphaEquivalent' [] []
where alphaEquivalent' :: [Text] -> [Text] -> Expression -> Expression -> Bool where
alphaEquivalent' ctx1 ctx2 (Variable v1) (Variable v2) alphaEquivalent' :: [Text] -> [Text] -> Expression -> Expression -> Bool
-- Two variables are alpha-equivalent if they are bound in the same location. alphaEquivalent' ctx1 ctx2 (Variable v1) (Variable v2)
= bindingSite ctx1 v1 == bindingSite ctx2 v2 -- Two variables are alpha-equivalent if they are bound in the same location.
alphaEquivalent' ctx1 ctx2 (Application ef1 ex1) (Application ef2 ex2) = bindingSite ctx1 v1 == bindingSite ctx2 v2
-- Two applications are alpha-equivalent if their components are alpha-equivalent. alphaEquivalent' ctx1 ctx2 (Application ef1 ex1) (Application ef2 ex2)
= alphaEquivalent' ctx1 ctx2 ef1 ef2 -- Two applications are alpha-equivalent if their components are alpha-equivalent.
&& alphaEquivalent' ctx1 ctx2 ex1 ex2 = alphaEquivalent' ctx1 ctx2 ef1 ef2
alphaEquivalent' ctx1 ctx2 (Abstraction v1 b1) (Abstraction v2 b2) && alphaEquivalent' ctx1 ctx2 ex1 ex2
-- Two abstractions are alpha-equivalent if their bodies are alpha-equivalent. alphaEquivalent' ctx1 ctx2 (Abstraction v1 b1) (Abstraction v2 b2)
= alphaEquivalent' (v1 : ctx1) (v2 : ctx2) b1 b2 -- Two abstractions are alpha-equivalent if their bodies are alpha-equivalent.
= alphaEquivalent' (v1 : ctx1) (v2 : ctx2) b1 b2
alphaEquivalent' _ _ _ _ = False
-- | The binding site of a variable is either the index of its binder -- | The binding site of a variable is either the index of its binder
-- or, if it is unbound, the name of the free variable. -- or, if it is unbound, the name of the free variable.
bindingSite :: [Text] -> Text -> Either Text Int bindingSite :: [Text] -> Text -> Either Text Int
bindingSite ctx var = maybeToRight var $ var `elemIndex` ctx bindingSite ctx var = maybeToRight var $ var `elemIndex` ctx
where maybeToRight :: b -> Maybe a -> Either b a where maybeToRight :: b -> Maybe a -> Either b a
maybeToRight default_ = maybe (Left default_) Right maybeToRight default_ = maybe (Left default_) Right
-- | Substitution is the process of replacing all free occurrences of a variable in one expression with another expression. -- | Substitution is the process of replacing all free occurrences of a variable in one expression with another expression.
substitute :: Text -> Expression -> Expression -> Expression substitute :: Text -> Expression -> Expression -> Expression
substitute var1 value unmodified@(Variable var2) substitute var1 value unmodified@(Variable var2)
| var1 == var2 = value | var1 == var2 = value
| otherwise = unmodified | otherwise = unmodified
substitute var value (Application ef ex) substitute var value (Application ef ex)
= Application (substitute var value ef) (substitute var value ex) = Application (substitute var value ef) (substitute var value ex)
substitute var1 value unmodified@(Abstraction var2 body) substitute var1 value unmodified@(Abstraction var2 body)
| var1 == var2 = unmodified | var1 == var2 = unmodified
| otherwise = Abstraction var2' $ substitute var1 value $ alphaConvert var2 var2' body | otherwise = Abstraction var2' $ substitute var1 value $ alphaConvert var2 var2' body
where var2' :: Text where
var2' = escapeName (freeVariables value) var2 var2' :: Text
var2' = escapeName (freeVariables value) var2
alphaConvert :: Text -> Text -> Expression -> Expression alphaConvert :: Text -> Text -> Expression -> Expression
alphaConvert oldName newName expr = substitute oldName (Variable newName) expr alphaConvert oldName newName expr = substitute oldName (Variable newName) expr
-- | Generate a new name which isn't present in the set, based on the old name. -- | Generate a new name which isn't present in the set, based on the old name.
escapeName :: HashSet Text -> Text -> Text escapeName :: HashSet Text -> Text -> Text
escapeName env name = fromJust $ find (not . free) names escapeName env name = fromJust $ find (not . free) names
where names :: [Text] where names :: [Text]
names = name : map (`T.snoc` '\'') names names = name : map (`T.snoc` '\'') names
free :: Text -> Bool
free = (`HS.member` env) free :: Text -> Bool
free = (`HS.member` env)
-- | Returns True if the top-level expression is reducible by beta-reduction. -- | Returns True if the top-level expression is reducible by beta-reduction.
betaRedex :: Expression -> Bool betaRedex :: Expression -> Bool
@ -110,22 +114,25 @@ whnf (Application (Abstraction _ _) _) = False
whnf (Abstraction var1 (Application fe (Variable var2))) whnf (Abstraction var1 (Application fe (Variable var2)))
= var1 /= var2 || var1 `freeIn` fe = var1 /= var2 || var1 `freeIn` fe
whnf (Application ef _) = whnf ef whnf (Application ef _) = whnf ef
whnf _ = True
eval :: (Expression -> Expression) -> Expression -> Expression eval :: (Expression -> Expression) -> Expression -> Expression
eval strategy = eval' eval strategy = eval'
where eval' :: Expression -> Expression where
eval' (Application ef ex) = eval' :: Expression -> Expression
case ef' of eval' (Application ef ex) =
-- Beta-reduction case ef' of
Abstraction var body -> eval' $ substitute var ex' body -- Beta-reduction
_ -> Application ef' ex' Abstraction var body -> eval' $ substitute var ex' body
where ef' = eval' ef _ -> Application ef' ex'
ex' = strategy ex where
eval' unmodified@(Abstraction var1 (Application ef (Variable var2))) ef' = eval' ef
-- Eta-reduction ex' = strategy ex
| var1 == var2 && not (var1 `freeIn` ef) = eval' ef eval' unmodified@(Abstraction var1 (Application ef (Variable var2)))
| otherwise = unmodified -- Eta-reduction
eval' x = x | var1 == var2 && not (var1 `freeIn` ef) = eval' ef
| otherwise = unmodified
eval' x = x
-- | Reduce an expression to normal form. -- | Reduce an expression to normal form.
eagerEval :: Expression -> Expression eagerEval :: Expression -> Expression

View File

@ -1,5 +1,11 @@
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveGeneric #-}
module LambdaCalculus.Expression where module LambdaCalculus.Expression
( Expression (Variable, Application, Abstraction)
, ast2expr, expr2ast
, pattern Lets, pattern Abstractions, pattern Applications
, viewLet, viewAbstraction, viewApplication
, basicShow
) where
-- The definition of Expression is in its own file because: -- The definition of Expression is in its own file because:
-- * Expression and AbstractSyntax should not be in the same file -- * Expression and AbstractSyntax should not be in the same file
@ -10,18 +16,21 @@ module LambdaCalculus.Expression where
-- with all of these irrelevant conversion operators. -- with all of these irrelevant conversion operators.
import Data.Bifunctor (first, second) import Data.Bifunctor (first, second)
import Data.List.NonEmpty (NonEmpty ((:|)), fromList, toList)
import Data.Text (Text) import Data.Text (Text)
import Data.Text qualified as T import Data.Text qualified as T
import GHC.Generics (Generic) import GHC.Generics (Generic)
import LambdaCalculus.Parser.AbstractSyntax (AbstractSyntax) import LambdaCalculus.Parser.AbstractSyntax (AbstractSyntax)
import LambdaCalculus.Parser.AbstractSyntax qualified as AST import LambdaCalculus.Parser.AbstractSyntax qualified as AST
import TextShow import TextShow (Builder, fromText, TextShow, showb, showt)
data Expression data Expression
= Variable Text = Variable Text
| Application Expression Expression -- | Function application: `(f x)`.
| Abstraction Text Expression | Application Expression Expression
deriving (Eq, Generic) -- | Lambda abstraction: `(λx. e)`.
| Abstraction Text Expression
deriving (Eq, Generic)
-- | A naive implementation of 'show', which does not take advantage of any syntactic sugar -- | A naive implementation of 'show', which does not take advantage of any syntactic sugar
-- and always emits optional parentheses. -- and always emits optional parentheses.
@ -33,26 +42,36 @@ basicShow (Abstraction var body) = "(λ" <> fromText var <> ". " <> showb body <
-- | Convert from an abstract syntax tree to an expression. -- | Convert from an abstract syntax tree to an expression.
ast2expr :: AbstractSyntax -> Expression ast2expr :: AbstractSyntax -> Expression
ast2expr (AST.Variable name) = Variable name ast2expr (AST.Variable name) = Variable name
ast2expr (AST.Application []) = Abstraction "x" (Variable "x") ast2expr (AST.Application (x :| [])) = ast2expr x
ast2expr (AST.Application [x]) = ast2expr x ast2expr (AST.Application xs) = foldl1 Application $ map ast2expr (toList xs)
ast2expr (AST.Application xs) = foldl1 Application $ map ast2expr xs
ast2expr (AST.Abstraction [] body) = ast2expr body
ast2expr (AST.Abstraction names body) = foldr Abstraction (ast2expr body) names ast2expr (AST.Abstraction names body) = foldr Abstraction (ast2expr body) names
ast2expr (AST.Let defs body) = foldr (uncurry letExpr . second ast2expr) (ast2expr body) defs ast2expr (AST.Let defs body) = foldr (uncurry letExpr . second ast2expr) (ast2expr body) defs
where letExpr :: Text -> Expression -> Expression -> Expression where
letExpr name val body = Application (Abstraction name body) val letExpr :: Text -> Expression -> Expression -> Expression
letExpr name val body' = Application (Abstraction name body') val
-- | View nested applications of abstractions as a list. -- | View nested applications of abstractions as a list.
pattern Lets :: [(Text, Expression)] -> Expression -> Expression
pattern Lets defs body <- (viewLet -> (defs@(_:_), body))
viewLet :: Expression -> ([(Text, Expression)], Expression) viewLet :: Expression -> ([(Text, Expression)], Expression)
viewLet (Application (Abstraction var body) x) = first ((var, x) :) (viewLet body) viewLet (Application (Abstraction var body) x) = first ((var, x) :) (viewLet body)
viewLet x = ([], x) viewLet x = ([], x)
-- | View nested abstractions as a list. -- | View nested abstractions as a list.
pattern Abstractions :: [Text] -> Expression -> Expression
pattern Abstractions names body <- (viewAbstraction -> (names@(_:_), body))
viewAbstraction :: Expression -> ([Text], Expression) viewAbstraction :: Expression -> ([Text], Expression)
viewAbstraction (Abstraction name body) = first (name :) (viewAbstraction body) viewAbstraction (Abstraction name body) = first (name :) (viewAbstraction body)
viewAbstraction x = ([], x) viewAbstraction x = ([], x)
-- | View left-nested applications as a list. -- | View left-nested applications as a list.
pattern Applications :: [Expression] -> Expression
pattern Applications exprs <- (viewApplication -> (exprs@(_:_:_)))
{-# COMPLETE Abstractions, Applications, Variable :: Expression #-}
viewApplication :: Expression -> [Expression] viewApplication :: Expression -> [Expression]
viewApplication (Application ef ex) = ex : viewApplication ef viewApplication (Application ef ex) = ex : viewApplication ef
viewApplication x = [x] viewApplication x = [x]
@ -61,9 +80,9 @@ viewApplication x = [x]
-- --
-- This function will use let, and applications and abstractions of multiple values when possible. -- This function will use let, and applications and abstractions of multiple values when possible.
expr2ast :: Expression -> AbstractSyntax expr2ast :: Expression -> AbstractSyntax
expr2ast (viewLet -> (defs@(_:_), body)) = AST.Let (map (second expr2ast) defs) $ expr2ast body expr2ast (Lets defs body) = AST.Let (fromList $ map (second expr2ast) defs) $ expr2ast body
expr2ast (viewAbstraction -> (names@(_:_), body)) = AST.Abstraction names $ expr2ast body expr2ast (Abstractions names body) = AST.Abstraction (fromList names) $ expr2ast body
expr2ast (viewApplication -> es@(_:_:_)) = AST.Application $ map expr2ast $ reverse es expr2ast (Applications exprs) = AST.Application $ fromList $ map expr2ast $ reverse exprs
expr2ast (Variable name) = AST.Variable name expr2ast (Variable name) = AST.Variable name
instance TextShow Expression where instance TextShow Expression where

View File

@ -1,18 +1,13 @@
module LambdaCalculus.Parser module LambdaCalculus.Parser (parseAST, parseExpression) where
( parseAST, parseExpression
) where
import Control.Applicative ((*>)) import Data.List.NonEmpty (fromList)
import Control.Monad (void)
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import Data.Text qualified as T
import LambdaCalculus.Expression (Expression, ast2expr) import LambdaCalculus.Expression (Expression, ast2expr)
import qualified LambdaCalculus.Expression as Expr
import LambdaCalculus.Parser.AbstractSyntax import LambdaCalculus.Parser.AbstractSyntax
import Text.Parsec hiding (label, token) import Text.Parsec hiding (label, token)
import Text.Parsec qualified import Text.Parsec qualified
import Text.Parsec.Text (Parser) import Text.Parsec.Text (Parser)
import TextShow
label :: String -> Parser a -> Parser a label :: String -> Parser a -> Parser a
label = flip Text.Parsec.label label = flip Text.Parsec.label
@ -26,10 +21,10 @@ keywords = ["let", "in"]
-- | A keyword is an exact string which is not part of an identifier. -- | A keyword is an exact string which is not part of an identifier.
keyword :: Text -> Parser () keyword :: Text -> Parser ()
keyword kwd = label (T.unpack kwd) $ do keyword kwd = label (T.unpack kwd) $ do
try do try do
string $ T.unpack kwd string $ T.unpack kwd
notFollowedBy letter notFollowedBy letter
spaces spaces
-- | An identifier is a sequence of letters which is not a keyword. -- | An identifier is a sequence of letters which is not a keyword.
identifier :: Parser Identifier identifier :: Parser Identifier
@ -48,22 +43,22 @@ grouping :: Parser AbstractSyntax
grouping = label "grouping" $ between (token '(') (token ')') expression grouping = label "grouping" $ between (token '(') (token ')') expression
application :: Parser AbstractSyntax application :: Parser AbstractSyntax
application = Application <$> many2 applicationTerm application = Application . fromList <$> many2 applicationTerm
where applicationTerm :: Parser AbstractSyntax where applicationTerm = abstraction <|> let_ <|> grouping <|> variable
applicationTerm = abstraction <|> let_ <|> grouping <|> variable
abstraction :: Parser AbstractSyntax abstraction :: Parser AbstractSyntax
abstraction = label "lambda abstraction" $ Abstraction <$> between lambda (token '.') (many1 identifier) <*> expression abstraction = label "lambda abstraction" $ Abstraction <$> between lambda (token '.') (fromList <$> many1 identifier) <*> expression
where lambda = label "lambda" $ (char '\\' <|> char 'λ') *> spaces where lambda = label "lambda" $ (char '\\' <|> char 'λ') *> spaces
let_ :: Parser AbstractSyntax let_ :: Parser AbstractSyntax
let_ = Let <$> between (keyword "let") (keyword "in") definitions <*> expression let_ = Let <$> between (keyword "let") (keyword "in") (fromList <$> definitions) <*> expression
where definitions :: Parser [Definition] where
definitions = flip sepBy1 (token ';') do definitions :: Parser [Definition]
name <- identifier definitions = flip sepBy1 (token ';') do
token '=' name <- identifier
value <- expression token '='
pure (name, value) value <- expression
pure (name, value)
expression :: Parser AbstractSyntax expression :: Parser AbstractSyntax
expression = label "expression" $ abstraction <|> let_ <|> try application <|> grouping <|> variable expression = label "expression" $ abstraction <|> let_ <|> try application <|> grouping <|> variable

View File

@ -1,11 +1,12 @@
module LambdaCalculus.Parser.AbstractSyntax module LambdaCalculus.Parser.AbstractSyntax
( AbstractSyntax (..), Definition, Identifier ( AbstractSyntax (..), Definition, Identifier
) where ) where
import Data.Bifunctor (first) import Data.Bifunctor (first)
import Data.List.NonEmpty (NonEmpty ((:|)), fromList, toList)
import Data.Text (Text) import Data.Text (Text)
import Data.Text qualified as T import Data.Text qualified as T
import TextShow import TextShow (Builder, TextShow, showb, showt, toText, fromText)
-- | The abstract syntax tree reflects the structure of the externally-visible syntax. -- | The abstract syntax tree reflects the structure of the externally-visible syntax.
-- --
@ -14,42 +15,59 @@ import TextShow
-- would become unnecessarily complicated, because the same expression -- would become unnecessarily complicated, because the same expression
-- can be represented in terms of multiple abstract syntax trees. -- can be represented in terms of multiple abstract syntax trees.
data AbstractSyntax data AbstractSyntax
= Variable Identifier = Variable Identifier
| Application [AbstractSyntax] -- There is no technical reason for the AST to forbid nullary applications and so forth.
| Abstraction [Identifier] AbstractSyntax -- However the parser rejects them to avoid confusing edge cases like `let x=in`,
| Let [Definition] AbstractSyntax -- so they're forbidden here too so that the syntax tree can't contain data
-- that the parser would refuse to accept.
--
-- As a matter of curiosity, here's why `let x=in` was syntactically valid:
-- 1. Parentheses in `let` statements are optional, infer them: `let x=()in()`.
-- 2. Insert optional whitespace: `let x = () in ()`.
-- 3. Nullary application expands to the identity function because
-- the identity function is the left identity of function application:
-- `let x=(\x.x) in \x.x`.
--
-- | n-ary function application: `(f x_1 x_2 ... x_n)`.
| Application (NonEmpty AbstractSyntax)
-- | Lambda abstraction over n variables: `(λx_1 x_2 ... x_n. e)`
| Abstraction (NonEmpty Identifier) AbstractSyntax
-- | Let expressions (syntactic sugar) binding `n` variables:
-- `let x_1 = e_1; x_2 = e_2; ... x_n = e_n`.
| Let (NonEmpty Definition) AbstractSyntax
type Definition = (Identifier, AbstractSyntax) type Definition = (Identifier, AbstractSyntax)
type Identifier = Text type Identifier = Text
-- I'm surprised this isn't in base somewhere. -- I'm surprised this isn't in base somewhere.
unsnoc :: [a] -> ([a], a) unsnoc :: NonEmpty a -> ([a], a)
unsnoc [x] = ([], x) unsnoc (x :| []) = ([], x)
unsnoc (x : xs) = first (x :) (unsnoc xs) unsnoc (x :| xs) = first (x :) (unsnoc (fromList xs))
instance TextShow AbstractSyntax where instance TextShow AbstractSyntax where
showb = unambiguous showb = unambiguous
where where
unambiguous, ambiguous :: AbstractSyntax -> Builder -- Parentheses are often optional to the parser, but not in every context.
unambiguous (Variable name) = fromText name -- The `unambigous` printer is used in contexts where parentheses are optional, and does not include them;
-- There's no technical reason for the AST to forbid nullary applications and so forth. -- the `ambiguous` printer is used when omitting parentheses could result in an incorrect parse.
-- However the parser rejects them to avoid confusing edge cases like `let x=in`, unambiguous, ambiguous :: AbstractSyntax -> Builder
-- so they're forbidden here too so that `show` will never print anything the parser would refuse to accept. unambiguous (Variable name) = fromText name
unambiguous (Application []) = error "Empty applications are currently disallowed." unambiguous (Application (unsnoc -> (es, final))) = foldr (\e es' -> ambiguous e <> " " <> es') final' es
unambiguous (Application (unsnoc -> (es, final))) = foldr (\e es' -> ambiguous e <> " " <> es') final' es where
where final' = case final of final' = case final of
Application _ -> ambiguous final Application _ -> ambiguous final
_ -> unambiguous final _ -> unambiguous final
unambiguous (Abstraction [] _) = error "Empty lambdas are currently disallowed." unambiguous (Abstraction names body) = "λ" <> names' <> ". " <> unambiguous body
unambiguous (Abstraction names body) = "λ" <> fromText (T.intercalate " " names) <> ". " <> unambiguous body where names' = fromText (T.intercalate " " $ toList names)
unambiguous (Let [] body) = error "Empty lets are currently disallowed." unambiguous (Let defs body) = "let " <> defs' <> " in " <> unambiguous body
unambiguous (Let defs body) = "let " <> fromText (T.intercalate "; " $ map (toText . showDef) defs) <> " in " <> unambiguous body where
where showDef :: Definition -> Builder defs' = fromText (T.intercalate "; " $ map (toText . showDef) $ toList defs)
showDef (name, val) = fromText name <> " = " <> unambiguous val
-- | Adds a grouper if omitting it could result in ambiguous syntax. showDef :: Definition -> Builder
-- (Which is to say, the parser would parse it wrong because a different parse has a higher priority.) showDef (name, val) = fromText name <> " = " <> unambiguous val
ambiguous e@(Variable _) = unambiguous e
ambiguous e = "(" <> unambiguous e <> ")" -- | Adds a grouper if omitting it could result in ambiguous syntax.)
ambiguous e@(Variable _) = unambiguous e
ambiguous e = "(" <> unambiguous e <> ")"
instance Show AbstractSyntax where instance Show AbstractSyntax where
show = T.unpack . showt show = T.unpack . showt

View File

@ -1,5 +1,3 @@
# Nightly is required for the ImportQualifiedPost extension from GHC 8.10. resolver: lts-17.5
# An LTS resolver will be used once GHC 8.10 is in an LTS.
resolver: nightly-2020-11-03
packages: packages:
- . - .

View File

@ -6,7 +6,7 @@
packages: [] packages: []
snapshots: snapshots:
- completed: - completed:
size: 544004 size: 565266
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2020/11/3.yaml url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/5.yaml
sha256: f6988e9a2c92219dc8ff0ebd2d420ede3425fa08cb6613ba47f1bc97c9925aa8 sha256: 78e8ebabf11406261abbc95b44f240acf71802630b368888f6d758de7fc3a2f7
original: nightly-2020-11-03 original: lts-17.5

View File

@ -23,16 +23,18 @@ instance Arbitrary T.Text where
-- This should evaluate to `y y`. -- This should evaluate to `y y`.
dfi :: Expression dfi :: Expression
dfi = Application d (Application f i) dfi = Application d (Application f i)
where d = Abstraction "x" $ Application (Variable "x") (Variable "x") where
f = Abstraction "f" $ Application (Variable "f") (Application (Variable "f") (Variable "y")) d = Abstraction "x" $ Application (Variable "x") (Variable "x")
i = Abstraction "x" $ Variable "x" f = Abstraction "f" $ Application (Variable "f") (Application (Variable "f") (Variable "y"))
i = Abstraction "x" $ Variable "x"
-- This should evalaute to `y`. -- This should evalaute to `y`.
ttttt :: Expression ttttt :: Expression
ttttt = Application (Application (Application f t) (Abstraction "x" (Variable "x"))) (Variable "y") ttttt = Application (Application (Application f t) (Abstraction "x" (Variable "x"))) (Variable "y")
where t = Abstraction "f" $ Abstraction "x" $ where
t = Abstraction "f" $ Abstraction "x" $
Application (Variable "f") (Application (Variable "f") (Variable "x")) Application (Variable "f") (Application (Variable "f") (Variable "x"))
f = Abstraction "T" $ Abstraction "f" $ Abstraction "x" $ f = Abstraction "T" $ Abstraction "f" $ Abstraction "x" $
Application (Application Application (Application
(Application (Variable "T") (Application (Variable "T")
(Application (Variable "T") (Application (Variable "T")