Skip to content
This repository has been archived by the owner on May 20, 2018. It is now read-only.

[WIP] Typechecking #4

Open
wants to merge 109 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
c0e8134
Move unification into its own section.
robrix Jan 10, 2016
78bb942
Stub in a heading for typechecking.
robrix Jan 10, 2016
670fe15
Add a type synonym for contexts.
robrix Jan 10, 2016
a237756
Add a type synonym for inferers.
robrix Jan 10, 2016
af492e6
Add a type synonym for checkers.
robrix Jan 10, 2016
2e7cd74
Remove the Inferer/Checker synonyms.
robrix Jan 10, 2016
941e1e9
Add an Error case to Unification.
robrix Jan 10, 2016
c9ec209
Errors don’t have a unified term.
robrix Jan 10, 2016
8a7d3a8
Errors can equate.
robrix Jan 10, 2016
484486b
Errors can be shown.
robrix Jan 10, 2016
965b819
Stub in typechecking.
robrix Jan 10, 2016
4fb4753
Inference.
robrix Jan 10, 2016
e7e3a33
Infer the type of type.
robrix Jan 10, 2016
a2f8931
Infer the types of variables.
robrix Jan 10, 2016
56d4507
Parameterize Unification by the type of conflicts.
robrix Jan 10, 2016
d76d4fb
Unification is a Functor.
robrix Jan 10, 2016
d1fe642
Revert "Unification is a Functor."
robrix Jan 10, 2016
d657a6d
Revert "Parameterize Unification by the type of conflicts."
robrix Jan 10, 2016
63001ed
Add a Roll typeclass.
robrix Jan 10, 2016
67bd56c
Add a Roll instance for Term.
robrix Jan 10, 2016
cd2a935
Add a Roll instance for Unification.
robrix Jan 10, 2016
3a6312d
Generalize type' to Roll instances.
robrix Jan 10, 2016
e2ef41a
Generalize implicit to Roll instances.
robrix Jan 10, 2016
d9cac9a
Generalize variable to Roll instances.
robrix Jan 10, 2016
382f4ed
Change the type variable to r.
robrix Jan 10, 2016
8418a71
Generalize local.
robrix Jan 10, 2016
5f33d83
Generalize global.
robrix Jan 10, 2016
c03c36a
Generalize apply.
robrix Jan 10, 2016
0037f1e
Add a Catamorphable typeclass with an instance for Term.
robrix Jan 10, 2016
71d47de
Add an Unroll typeclass.
robrix Jan 10, 2016
1037360
Add a `para` function w/default implementation for Unrollable types.
robrix Jan 10, 2016
da516ed
Generalize maxBoundVariable to Catamorphable terms.
robrix Jan 10, 2016
1622693
Add an Unroll instance for Term.
robrix Jan 10, 2016
efc1bd5
Replace the Catamorphable instance for Term with one for Unroll.
robrix Jan 10, 2016
d5e7cec
Move the `para` implementation into the `Unroll` instance of `Catamor…
robrix Jan 10, 2016
2d95bc6
Move the Catamorphable instance to the bottom.
robrix Jan 10, 2016
3658b6c
Add a PartialUnroll typeclass.
robrix Jan 10, 2016
adebe6e
Add a PartialUnroll instance for Unification.
robrix Jan 10, 2016
ae64762
Move the typeclasses down into their own section.
robrix Jan 10, 2016
466d112
Use the `local` constructor for the variable.
robrix Jan 10, 2016
d83a75c
`roll`.
robrix Jan 10, 2016
c645a1f
Handle errors in flipUnification.
robrix Jan 10, 2016
0d3f3d3
Annotate a bunch of term types.
robrix Jan 10, 2016
4d04400
Remove the recursion schemes heading.
robrix Jan 10, 2016
50140a3
Total Unroll instances can be lowered to PartialUnroll instances.
robrix Jan 10, 2016
357e957
Generalize maxBoundVariable to PartialUnroll.
robrix Jan 10, 2016
0cb5baa
Generalize lambda to Roll/PartialUnroll r.
robrix Jan 10, 2016
74ba982
Generalize --> to Roll/PartialUnroll.
robrix Jan 10, 2016
4460e60
Add an explicit instance of PartialUnroll for Term.
robrix Jan 10, 2016
e439086
Generalize substitution to Roll/PartialUnroll.
robrix Jan 10, 2016
5492dcf
Infer the types of lambdas.
robrix Jan 10, 2016
100f99e
Use the constructors for unifying variables and applications.
robrix Jan 10, 2016
cffca38
Generalize renaming to Roll/PartialUnroll.
robrix Jan 10, 2016
199dfbb
Stub in unification of arbitrary lambdas.
robrix Jan 11, 2016
86878e7
Arbitrary lambda unification subsumes no-renaming unification.
robrix Jan 11, 2016
17613a4
Define freeVariables with a lambda expression.
robrix Jan 11, 2016
83ef087
Add a function to increment a name.
robrix Jan 11, 2016
dfcd1a1
Add a function to compute a fresh name given some predicate.
robrix Jan 11, 2016
3343543
Add a function to compute a fresh name given a set of names.
robrix Jan 11, 2016
8d33b00
Pick a sensible fresh name given a set of names.
robrix Jan 11, 2016
0818975
Generalize renaming to Names.
robrix Jan 11, 2016
4b6a1f5
Select a fresh name.
robrix Jan 11, 2016
3e59da4
Test that shadowed variables are not renamed.
robrix Jan 11, 2016
f92cb88
Test that prime is injective.
robrix Jan 11, 2016
997dac9
Test that renaming is injective.
robrix Jan 11, 2016
c3de2e5
Generalize the test of renaming with shadowed variables to arbitrary …
robrix Jan 11, 2016
bbde0cc
Factor out the priming.
robrix Jan 11, 2016
cd4d342
Reconstruct the expected term from Unifications.
robrix Jan 11, 2016
36a72ca
Reconstruct the actual term from Unifications.
robrix Jan 11, 2016
5783908
Stub in a test helper for replacing terms deeply.
robrix Jan 11, 2016
e17b619
Test that the expected term is recoverable.
robrix Jan 11, 2016
4def13c
Test that the actual term is recoverable.
robrix Jan 11, 2016
7ad1a93
Section header for naming.
robrix Jan 11, 2016
5fef46e
Alpha equivalence.
robrix Jan 11, 2016
d28d51f
Test recovery of expected/actual terms up to renaming.
robrix Jan 11, 2016
21d4919
Test actual/expected terms by replacing.
robrix Jan 11, 2016
b45dd28
Test that identical terms are alpha-equivalent.
robrix Jan 11, 2016
cab0752
Test that free variables are alpha-equivalent.
robrix Jan 11, 2016
142c054
Variables are alpha equivalent.
robrix Jan 11, 2016
0e80009
Generate lambdas more often.
robrix Jan 11, 2016
804bd99
Fix a test name.
robrix Jan 11, 2016
3620273
Unify free variables.
robrix Jan 11, 2016
d9d121b
Unify lambdas up to renaming.
robrix Jan 11, 2016
0c0d58d
Alpha-equivalence is defined up to renaming.
robrix Jan 11, 2016
f66ea99
Remove some unused terms.
robrix Jan 11, 2016
a5e5a2a
As an experiment, represent errors with conflicts.
robrix Jan 12, 2016
d00104f
Remove the Error case from Unification.
robrix Jan 12, 2016
bb2052a
Typecheck applications in suspension.
robrix Jan 12, 2016
e384d66
Typecheck applications of functions in suspension.
robrix Jan 12, 2016
0df897a
Stub in an applySubstitution function.
robrix Jan 12, 2016
d12d486
Substitute inside lambdas.
robrix Jan 12, 2016
740901b
Remove overlapped patterns.
robrix Jan 12, 2016
9229ceb
Substitute in application types.
robrix Jan 12, 2016
e3e85a4
Test the equivalence of checking against implicit & inference.
robrix Jan 12, 2016
ed623e6
Check lambda types against Type, rather than unifying against it.
robrix Jan 12, 2016
2ede00c
Test that free variables do not typecheck.
robrix Jan 12, 2016
912e8a7
Test the inference of bound variables.
robrix Jan 12, 2016
19b097a
Test that inferred types are checked.
robrix Jan 12, 2016
0d0358c
Test the inference of simple function types.
robrix Jan 12, 2016
57ad2a5
Simplify the test of simple function type inference.
robrix Jan 12, 2016
7c70635
Typecheck the bodies of lambdas.
robrix Jan 12, 2016
b3ed734
Add another test of simple function type inference.
robrix Jan 12, 2016
0e8a928
Test the inference of `identity`’s type.
robrix Jan 12, 2016
5ff72a4
Test the inference of `constant`’s type.
robrix Jan 12, 2016
4bb7cc4
Use the parameter type, and not its type, as the domain.
robrix Jan 12, 2016
318fea5
Shadow >> infix.
robrix Jan 12, 2016
90d1f24
Check lambdas against types.
robrix Jan 12, 2016
180c8fe
Make the >> definition available to all cases.
robrix Jan 12, 2016
c3bb9be
Strip some leading indentation.
robrix Jan 12, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 157 additions & 57 deletions src/Curve.hs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable, FlexibleInstances #-}
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable, FlexibleInstances, UndecidableInstances #-}
module Curve where

import Data.Functor.Classes
import qualified Data.List as List
import qualified Data.Set as Set
import qualified Data.Map as Map

data Name
= Local Int
Expand All @@ -27,36 +28,36 @@ type Unification' = Unification Expression

-- DSL for constructing terms

type' :: Term'
type' = Term Type
type' :: Roll r => r Expression
type' = roll Type

implicit :: Term'
implicit = Term Implicit
implicit :: Roll r => r Expression
implicit = roll Implicit

variable :: Name -> Term'
variable = Term . Variable
variable :: Roll r => Name -> r Expression
variable = roll . Variable

local :: Int -> Term'
local :: Roll r => Int -> r Expression
local = variable . Local

global :: String -> Term'
global :: Roll r => String -> r Expression
global = variable . Global

infixl 9 `apply`

apply :: Term' -> Term' -> Term'
apply a = Term . Application a
apply :: Roll r => r Expression -> r Expression -> r Expression
apply a = roll . Application a

infixr `lambda`

lambda :: Term' -> (Term' -> Term') -> Term'
lambda t f = Term $ Lambda i t body
lambda :: (Roll r, PartialUnroll r) => r Expression -> (r Expression -> r Expression) -> r Expression
lambda t f = roll $ Lambda i t body
where i = maybe 0 succ $ maxBoundVariable body
body = f (Term $ Variable $ Local i)
body = f (local i)

infixr -->

(-->) :: Term' -> Term' -> Term'
(-->) :: (Roll r, PartialUnroll r) => r Expression -> r Expression -> r Expression
a --> b = a `lambda` const b

infixr `pi`
Expand All @@ -72,67 +73,127 @@ into term = Unification $ into <$> out term

unified :: Unification' -> Maybe Term'
unified (Unification expression) = Term <$> traverse unified expression
unified (Conflict _ _) = Nothing
unified _ = Nothing

expected :: Unification' -> Term'
expected (Unification out) = Term $ expected <$> out
expected (Conflict a _) = a

actual :: Unification' -> Term'
actual (Unification out) = Term $ actual <$> out
actual (Conflict _ b) = b


-- Binding

rename :: Int -> Int -> Term' -> Term'
rename :: (Roll r, PartialUnroll r) => Name -> Name -> r Expression -> r Expression
rename old new term | old == new = term
rename old new term = Term $ case out term of
Variable (Local name) | name == old -> Variable $ Local new
Lambda name t b -> if name == old
rename old new term = case unrollMaybe term of
Just (Variable name) | name == old -> variable new
Just (Lambda name t b) -> roll $ if Local name == old
then Lambda name (rename old new t) b
else Lambda name (rename old new t) (rename old new b)
Application a b -> Application (rename old new a) (rename old new b)
other -> other

substitute :: Int -> Term' -> Term' -> Term'
substitute name withTerm inScope = case out inScope of
Variable (Local n) | n == name -> withTerm
Lambda n inType inBody -> if n == name
then Term $ Lambda n (substitute name withTerm inType) inBody
else Term $ Lambda n (substitute name withTerm inType) (substitute name withTerm inBody)
Application inA inB -> Term $ Application (substitute name withTerm inA) (substitute name withTerm inB)
Just (Application a b) -> rename old new a `apply` rename old new b
_ -> term

substitute :: (Roll r, PartialUnroll r) => Int -> r Expression -> r Expression -> r Expression
substitute name withTerm inScope = case unrollMaybe inScope of
Just (Variable (Local n)) | n == name -> withTerm
Just (Lambda n inType inBody) -> if n == name
then roll $ Lambda n (substitute name withTerm inType) inBody
else roll $ Lambda n (substitute name withTerm inType) (substitute name withTerm inBody)
Just (Application inA inB) -> roll $ Application (substitute name withTerm inA) (substitute name withTerm inB)
_ -> inScope

unify :: Term' -> Term' -> Unification'
unify expected actual = case (out expected, out actual) of
(_, Implicit) -> into expected
(Implicit, _) -> into actual
applySubstitution :: (Roll r, PartialUnroll r) => r Expression -> r Expression -> r Expression
applySubstitution withTerm body = case unrollMaybe body of
Just (Lambda i _ inScope) -> substitute i withTerm inScope
_ -> body

(Type, Type) -> into expected

(Variable n1, Variable n2) | n1 == n2 -> Unification $ Variable n2
(Application a1 b1, Application a2 b2) -> Unification $ Application (unify a1 a2) (unify b1 b2)
(Lambda i1 a1 b1, Lambda i2 a2 b2) | i1 == i2 -> Unification $ Lambda i2 (unify a1 a2) (unify b1 b2)
freeVariables :: Term' -> Set.Set Name
freeVariables = cata $ \ expression -> case expression of
Variable name -> Set.singleton name
Lambda i t b -> Set.delete (Local i) b `Set.union` t
Application a b -> a `Set.union` b
_ -> mempty

_ -> Conflict expected actual
maxBoundVariable :: PartialUnroll r => r Expression -> Maybe Int
maxBoundVariable = foldl maximal Nothing . unrollMaybe
where maximal into (Lambda i t _) = max into $ max (Just i) (maxBoundVariable t)
maximal into (Application a b) = max into $ max (maxBoundVariable a) (maxBoundVariable b)
maximal into _ = into


freeVariables :: Term' -> Set.Set Name
freeVariables = cata inExpression
where inExpression expression = case expression of
Variable name -> Set.singleton name
Lambda i t b -> Set.delete (Local i) b `Set.union` t
Application a b -> a `Set.union` b
_ -> mempty
alphaEquivalent :: Term' -> Term' -> Bool
alphaEquivalent a b | a == b = True
alphaEquivalent (Term (Variable _)) (Term (Variable _)) = True
alphaEquivalent (Term (Application a1 b1)) (Term (Application a2 b2)) = alphaEquivalent a1 a2 && alphaEquivalent b1 b2
alphaEquivalent (Term (Lambda _ t1 b1)) (Term (Lambda _ t2 b2)) = alphaEquivalent t1 t2 && alphaEquivalent b1 b2
alphaEquivalent _ _ = False


-- Naming

freshBy :: (Name -> Bool) -> Name -> Name
freshBy isUsed name = if isUsed name then freshBy isUsed (prime name) else name

freshIn :: Set.Set Name -> Name -> Name
freshIn names = freshBy (`Set.member` names)

prime :: Name -> Name
prime (Local i) = Local $ succ i
prime (Global s) = Global $ s ++ "ʹ"

pick :: Set.Set Name -> Name
pick names = freshIn names (maximum $ Set.insert (Local 0) names)


-- Typechecking

type Context = Map.Map Name Term'

infer :: Context -> Term' -> Unification'
infer = check implicit

check :: Term' -> Context -> Term' -> Unification'
check expected context term = case (out term, out expected) of
(Type, Implicit) -> Unification Type

(Variable name, Implicit) -> maybe (Conflict implicit implicit) into (Map.lookup name context)

(Application a b, Implicit) -> let a' = infer context a in case a' of
Unification (Lambda _ from to) -> case unified from of
Just from -> applySubstitution (check from context b) to
_ -> a' `apply` infer context b
_ -> a' `apply` infer context b

(Lambda i t body, Implicit) -> (check type' context t >> into t) `lambda` \ v -> substitute i v (infer (Map.insert (Local i) t context) body)

maxBoundVariable :: Term' -> Maybe Int
maxBoundVariable = cata (\ expression -> case expression of
Lambda n t _ -> max (Just n) t
Application a b -> max a b
_ -> Nothing)
(_, Implicit) -> Conflict implicit implicit

(Lambda {}, Type) -> check (type' --> type') context term

-- Recursion schemes
(_, _) -> let unification = infer context term in
maybe unification (unify expected) $ unified unification
where Unification _ >> b = b
a >> _ = a

cata :: Functor f => (f a -> a) -> Term f -> a
cata f = f . fmap (cata f) . out

para :: Functor f => (f (Term f, a) -> a) -> Term f -> a
para f = f . fmap fanout . out
where fanout a = (a, para f a)
-- Equality

unify :: Term' -> Term' -> Unification'
unify expected actual = case (out expected, out actual) of
(_, Implicit) -> into expected
(Implicit, _) -> into actual

(Type, Type) -> into expected

(Variable _, Variable _) -> into expected
(Application a1 b1, Application a2 b2) -> unify a1 a2 `apply` unify b1 b2
(Lambda i a1 b1, Lambda _ a2 b2) -> Unification $ Lambda i (unify a1 a2) (unify b1 b2)

_ -> Conflict expected actual


-- Numerals
Expand All @@ -149,6 +210,23 @@ showNumeral "" _ = ""
showNumeral alphabet i = List.genericIndex alphabet <$> digits (List.genericLength alphabet) i


-- Classes

class Roll g where
roll :: f (g f) -> g f

class Unroll r where
unroll :: r f -> f (r f)

class PartialUnroll r where
unrollMaybe :: r f -> Maybe (f (r f))

class Catamorphable r where
cata :: Functor f => (f a -> a) -> r f -> a

para :: Functor f => (f (r f, a) -> a) -> r f -> a


-- Instances

instance Show Name where
Expand Down Expand Up @@ -193,3 +271,25 @@ instance Show Unification' where
show (Unification out) = show out
show (Conflict a b) = "Expected: " ++ show a ++ "\n"
++ " Actual: " ++ show b ++ "\n"

instance Roll Term where
roll = Term

instance Unroll Term where
unroll = out

instance PartialUnroll Term where
unrollMaybe = Just . unroll

instance Roll Unification where
roll = Unification

instance Unroll r => Catamorphable r where
cata f = f . fmap (cata f) . unroll

para f = f . fmap fanout . unroll
where fanout a = (a, para f a)

instance PartialUnroll Unification where
unrollMaybe (Unification expression) = Just expression
unrollMaybe _ = Nothing
Loading