Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Pairs #10

Open
wants to merge 78 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
5417077
Stub in a Surface.Language module.
robrix Jan 7, 2016
b18aa6d
Add a type synonym for Term Expression.
robrix Jan 7, 2016
d106732
Export Surface.Language.
robrix Jan 7, 2016
98b6b9f
Move all of Surface to Surface.Language.
robrix Jan 7, 2016
79d199f
Stub in a Surface.Pair module.
robrix Jan 7, 2016
d94175e
Define a Pair type.
robrix Jan 7, 2016
a464292
Export Pair.
robrix Jan 7, 2016
ea1c0fb
Move checkIsType up a bit.
robrix Jan 7, 2016
b8beb6d
Remove a redundant import.
robrix Jan 7, 2016
7f40acd
Write a few lambdas inline.
robrix Jan 7, 2016
b6563ba
Check function types more or less the same as pi types.
robrix Jan 7, 2016
80e7d35
Test for unification against the return type, not its kind.
robrix Jan 7, 2016
9f6a92d
Check that the return type is a type.
robrix Jan 7, 2016
73d0bd5
Just check against type.
robrix Jan 7, 2016
82e1cc7
Unify the types.
robrix Jan 7, 2016
9261a7d
Move _type into the Language module.
robrix Jan 7, 2016
09a144d
Remove a redundant import.
robrix Jan 7, 2016
f3a9a12
Ignore the tags file.
robrix Jan 7, 2016
2c63002
Rename the parameters.
robrix Jan 8, 2016
4fe94bd
Test the type of _Pair.
robrix Jan 8, 2016
11b820e
Move the language tests into their own module.
robrix Jan 8, 2016
70b7600
Stub in a Pair module.
robrix Jan 8, 2016
82b2d08
Don’t import Surface.Pair.
robrix Jan 8, 2016
75fb47c
Stub in a Definition type.
robrix Jan 8, 2016
5f74131
Only represent the type of definitions for now.
robrix Jan 8, 2016
72e7f99
Modules are lists of definitions.
robrix Jan 8, 2016
3ac0257
Remove a redundant import.
robrix Jan 8, 2016
e7dcfeb
Remove a bogus $.
robrix Jan 8, 2016
f2add1e
Define Definition using record syntax.
robrix Jan 8, 2016
68a1a71
Add a lookup operator for Module.
robrix Jan 8, 2016
0fdb668
Export Data.Module.
robrix Jan 8, 2016
4562713
Definitions have types and values.
robrix Jan 8, 2016
c869d4d
Correct the type of Pair.
robrix Jan 8, 2016
c4ebecc
Add a function to typecheck a definition.
robrix Jan 8, 2016
46fe06f
Add a function to typecheck modules.
robrix Jan 8, 2016
c6977c8
Rename the test subject.
robrix Jan 8, 2016
966ed36
None of this “should” business.
robrix Jan 8, 2016
028a7fe
Test that the module typechecks.
robrix Jan 8, 2016
deba278
Correct the inference of function types.
robrix Jan 8, 2016
25cb9ff
Unification carries a typechecker.
robrix Jan 8, 2016
0e49f82
Check pi types like function types.
robrix Jan 8, 2016
3aeaedf
lts-4.
robrix Jan 9, 2016
5bd111b
Default compiler check.
robrix Jan 9, 2016
94b6b94
4.0, that is.
robrix Jan 9, 2016
6541127
Add a `checkFunctionType` function.
robrix Jan 9, 2016
b4f4b03
Use the Checker synonym.
robrix Jan 9, 2016
0b9d66a
Use checkFunctionType for lambdas, pi types, and function types.
robrix Jan 9, 2016
40ed0d2
Recur into abstractions.
robrix Jan 9, 2016
369ee1f
Add a type synonym for environments.
robrix Jan 9, 2016
c41e8bf
Stub in the reduction of terms to weak head normal form.
robrix Jan 9, 2016
12de9d8
Lookup variables for whnf.
robrix Jan 9, 2016
cda5fec
Substitute inside applications in whnf.
robrix Jan 9, 2016
b793b40
Reduce applications to whnf correctly.
robrix Jan 9, 2016
7d65f48
byUnifying recurs via expectUnifiable.
robrix Jan 9, 2016
05eb00b
Use the Term' shorthand everywhere we can.
robrix Jan 9, 2016
7a4651e
Module is defined in terms of Term'.
robrix Jan 9, 2016
d1af2bb
Abstract Definition over a term type.
robrix Jan 9, 2016
7c1a611
Abstract Definition over the expression type.
robrix Jan 9, 2016
0b1b6fc
Abstract Module over the expression type.
robrix Jan 9, 2016
74ac43f
Abstract Module and Definition over the term type.
robrix Jan 9, 2016
4329f99
Move the Module types into Data.Term.Types.
robrix Jan 9, 2016
1e98610
We’re no longer dependent on StandaloneDeriving.
robrix Jan 9, 2016
1a1a2d8
Equality of conflicts.
robrix Jan 9, 2016
c7aca05
Inequality of other comparisons.
robrix Jan 9, 2016
bc5c75f
Move the Unification module under Surface.
robrix Jan 9, 2016
bdd3daa
Move the Unifiable instance for Expression into Surface.Unification.
robrix Jan 9, 2016
444187a
Stub in a Surface.Renaming module.
robrix Jan 9, 2016
3efe2a7
Move the Unifiable typeclass into Data.Term.Types.
robrix Jan 9, 2016
a51b467
Resort the exports.
robrix Jan 9, 2016
c70f301
Export Surface.Renaming.
robrix Jan 9, 2016
91a9e19
Move renaming into Surface.Renaming.
robrix Jan 9, 2016
30af305
Move unification into Surface.Unification.
robrix Jan 9, 2016
17bfaf1
`annotation` typechecking doesn’t use expectUnifiable.
robrix Jan 9, 2016
0d97693
Minimize the set of constraints necessary for `annotation`.
robrix Jan 9, 2016
905dde2
Minimize the constraints necessary for `rename`.
robrix Jan 9, 2016
9f18d6a
Minimize the constraints necessary for `renameUnification`.
robrix Jan 9, 2016
a75a412
Remove a redundant import.
robrix Jan 9, 2016
6f8d0fd
Typecheck types without checkInferred.
robrix Jan 9, 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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
.DS_Store
.stack-work

tags
7 changes: 6 additions & 1 deletion Surface.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ library
, Data.Term
, Data.Term.Types
, Data.Typing
, Data.Unification
, Surface
, Surface.Language
, Surface.Pair
, Surface.Renaming
, Surface.Unification
build-depends: base >= 4.7 && < 5
, containers
default-language: Haskell2010
Expand All @@ -44,6 +47,8 @@ test-suite Surface-test
main-is: Spec.hs
other-modules: Data.Name.Internal.Spec
, Data.Term.Spec
, Surface.Language.Spec
, Surface.Pair.Spec
, Test.Assertions
build-depends: base
, Surface
Expand Down
8 changes: 0 additions & 8 deletions src/Data/Expression.hs
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
module Data.Expression where

import Data.Unification

data Expression recur
= Application recur recur
| Lambda recur recur
deriving (Functor, Show, Eq, Foldable, Traversable)

instance Unifiable Expression where
unifyBy f expected actual = case (expected, actual) of
(Application a1 b1, Application a2 b2) -> Just $ Application (f a1 a2) (f b1 b2)
(Lambda a1 b1, Lambda a2 b2) -> Just $ Lambda (f a1 a2) (f b1 b2)
_ -> Nothing
16 changes: 13 additions & 3 deletions src/Data/Module.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
{-# LANGUAGE FlexibleContexts #-}
module Data.Module where

import Data.Expression
import qualified Data.List as List
import Data.Term.Types
import qualified Data.Map as Map
import Surface.Language

data Module = Module (Map.Map String (Term Expression))
(!) :: Module term -> String -> Definition term
Module definitions ! key = case List.find ((== key) . symbol) definitions of
Just x -> x
_ -> error "Expected definition to exist"

checkModule :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Context (Term f) -> Module (Term f) -> Result [Term f]
checkModule context (Module definitions) = sequence $ checkDefinition context <$> definitions

checkDefinition :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Context (Term f) -> Definition (Term f) -> Result (Term f)
checkDefinition context (Definition symbol t v) = typeOf t _type' context >> typeOf v t context
69 changes: 6 additions & 63 deletions src/Data/Term.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import Data.Binding
import Data.Name
import Data.Typing
import Data.Term.Types
import Data.Unification
import Surface.Renaming
import Surface.Unification
import qualified Data.Map as Map
import qualified Data.Set as Set

Expand All @@ -21,9 +22,11 @@ abstract f = abstraction name scope
name = maybe (Local 0) prime $ maxBoundVariable (f $ variable (Local $ negate 1))

-- | Construct the annotation of a term by a type. The term will be checked against this type.
annotation :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Term f -> Term f -> Term f
annotation :: Foldable f => Term f -> Term f -> Term f
annotation term type' = checkedTyping typeChecker $ Annotation term type'
where typeChecker against context = typeOf term type' context >>= expectUnifiable against
where typeChecker against context = do
_ <- typeOf term type' context
typeOf term against context


checkedAbstraction :: Name -> Checker (Term f) -> Term f -> Term f
Expand All @@ -44,18 +47,9 @@ checkedBinding typeChecker = checkedTyping typeChecker . Binding
checkedExpression :: Foldable f => Checker (Term f) -> f (Term f) -> Term f
checkedExpression typeChecker = checkedBinding typeChecker . Expression

_type :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Int -> Term f
_type n = Term mempty (checkInferred $ const $ Right (_type (n + 1))) $ Type n

_type' :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Term f
_type' = _type 0

implicit :: Term f
implicit = Term mempty (const . Right) Implicit

expectUnifiable :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Term f -> Term f -> Result (Term f)
expectUnifiable expected actual = maybe (Left $ "error: Unification failed.\nExpected: '" ++ show expected ++ "'\n Actual: '" ++ show actual ++ "'.\n") Right $ unified $ unify expected actual

checkInferred :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Inferer (Term f) -> Checker (Term f)
checkInferred inferer expected context = do
actual <- inferer context
Expand All @@ -71,28 +65,6 @@ maxBoundVariable = cata $ \ t -> case t of
Binding (Expression e) -> maximum e
_ -> Nothing

rename :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Name -> Name -> Term f -> Term f
rename old new (Term freeVariables typeChecker typing) = Term (replace old new freeVariables) typeChecker $ renameTypingBy (rename old new) old new typing

replace :: Ord a => a -> a -> Set.Set a -> Set.Set a
replace old new set = if Set.member old set then Set.insert new $ Set.delete old set else set

renameUnification :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Name -> Name -> Unification f -> Unification f
renameUnification old new (Unification freeVariables out) = Unification (replace old new freeVariables) $ renameTypingBy (renameUnification old new) old new out
renameUnification old new (Conflict expected actual) = Conflict (rename old new expected) (rename old new actual)

renameTypingBy :: Functor f => (g f -> g f) -> Name -> Name -> Typing (Binding f) (g f) -> Typing (Binding f) (g f)
renameTypingBy _ old new typing | old == new = typing
renameTypingBy f old new typing = case typing of
Binding (Variable name) -> if name == old then Binding (Variable new) else typing
Binding (Abstraction name scope) -> if name == old then typing else Binding $ Abstraction name (f scope)
Binding (Expression body) -> Binding $ Expression $ f <$> body

Annotation a b -> Annotation (f a) (f b)

Type _ -> typing
Implicit -> typing

substitute :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Name -> Term f -> Term f -> Term f
substitute name with term | with == variable name = term
substitute name with term@(Term _ typeChecker binding) = case binding of
Expand Down Expand Up @@ -129,32 +101,3 @@ cata f = f . fmap (cata f) . out
para :: Functor f => (Typing (Binding f) (Term f, a) -> a) -> Term f -> a
para f = f . fmap fanout . out
where fanout a = (a, para f a)

byUnifying :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Checker (Term f) -> Checker (Term f) -> Checker (Term f)
byUnifying a b against context = do
a' <- a against context
b' <- b against context
maybe (Left "couldn’t unify") Right $ unified $ unify a' b'

unify :: (Show (Term f), Unifiable f, Traversable f, Eq (f (Term f))) => Term f -> Term f -> Unification f
unify expected actual = case (out expected, out actual) of
(a, b) | a == b -> into expected

(_, Implicit) -> into expected
(Implicit, _) -> into actual

(Type _, Type _) -> into expected
(Annotation term1 type1, Annotation term2 type2) -> Unification free (Annotation (unify term1 term2) (unify type1 type2))

(Binding (Abstraction name1 scope1), Binding (Abstraction name2 scope2)) | name1 == name2 -> Unification free (Binding $ Abstraction name1 (unify scope1 scope2))
(Binding (Abstraction name1 scope1), Binding (Abstraction name2 scope2)) -> Unification (Set.delete name1 free) (Binding $ Abstraction name1 (renameUnification name name1 (unify (rename name1 name scope1) (rename name2 name scope2))))
where name = pick free
free = freeVariables scope1 `Set.union` freeVariables scope2

(Binding (Abstraction name scope), _) | Set.notMember name (freeVariables scope) -> unify scope actual
(_, Binding (Abstraction name scope)) | Set.notMember name (freeVariables scope) -> unify expected scope

(Binding (Expression e1), Binding (Expression e2)) | Just e <- unifyBy unify e1 e2 -> Unification free $ Binding $ Expression e

_ -> Conflict expected actual
where free = freeVariables expected `Set.union` freeVariables actual
31 changes: 22 additions & 9 deletions src/Data/Term/Types.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{-# LANGUAGE StandaloneDeriving, UndecidableInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Term.Types where

import Data.Binding
Expand All @@ -9,35 +9,48 @@ import qualified Data.Set as Set

type Result a = Either String a

data Module term = Module [Definition term]
data Definition term = Definition { symbol :: String, getType :: term, getValue :: term }

type Context term = Map.Map Name term
type Environment term = Map.Map Name term

type Inferer term = Context term -> Result term
type Checker term = term -> Context term -> Result term

data Term f = Term { freeVariables :: Set.Set Name, typeOf :: Checker (Term f), out :: Typing (Binding f) (Term f) }

data Unification f = Unification (Set.Set Name) (Typing (Binding f) (Unification f)) | Conflict (Term f) (Term f)
data Unification f = Unification (Set.Set Name) (Checker (Term f)) (Typing (Binding f) (Unification f)) | Conflict (Term f) (Term f)

class Unifiable e where
unifyBy :: (f e -> f e -> Unification e) -> e (f e) -> e (f e) -> Maybe (e (Unification e))

expected :: Functor f => Unification f -> Term f
expected (Conflict expected _) = expected
expected (Unification freeVariables out) = Term freeVariables (const . const $ Left "Unification does not preserve typecheckers.\n") (expected <$> out)
expected (Unification freeVariables typeChecker out) = Term freeVariables typeChecker (expected <$> out)

actual :: Functor f => Unification f -> Term f
actual (Conflict _ actual) = actual
actual (Unification freeVariables out) = Term freeVariables (const . const $ Left "Unification does not preserve typecheckers.\n") (actual <$> out)
actual (Unification freeVariables typeChecker out) = Term freeVariables typeChecker (actual <$> out)

unified :: Traversable f => Unification f -> Maybe (Term f)
unified (Conflict _ _) = Nothing
unified (Unification freeVariables out) = do
unified (Unification freeVariables typeChecker out) = do
out <- mapM unified out
return $ Term freeVariables (const . const $ Left "Unification does not preserve typecheckers.\n") out
return $ Term freeVariables typeChecker out

into :: Functor f => Term f -> Unification f
into term = Unification (freeVariables term) $ into <$> out term
into term = Unification (freeVariables term) (typeOf term) $ into <$> out term


instance Eq (f (Term f)) => Eq (Term f) where
a == b = freeVariables a == freeVariables b && out a == out b

deriving instance (Eq (Term f), Eq (f (Unification f))) => Eq (Unification f)
deriving instance (Show (Term f), Show (f (Unification f))) => Show (Unification f)
instance (Eq (Term f), Eq (f (Unification f))) => Eq (Unification f) where
(Unification freeVariablesA _ outA) == (Unification freeVariablesB _ outB) = freeVariablesA == freeVariablesB && outA == outB
(Conflict a1 b1) == (Conflict a2 b2) = a1 == a2 && b1 == b2
_ == _ = False

instance (Functor f, Show (Term f), Show (f (Unification f))) => Show (Unification f) where
show u = "Expected: " ++ show (expected u) ++ "\n"
++ " Actual: " ++ show (actual u) ++ "\n"
6 changes: 0 additions & 6 deletions src/Data/Unification.hs

This file was deleted.

113 changes: 4 additions & 109 deletions src/Surface.hs
Original file line number Diff line number Diff line change
@@ -1,119 +1,14 @@
{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
module Surface (
module Surface',
lambda,
Surface.pi,
(-->),
apply,
checkHasFunctionType,
checkIsFunctionType,
checkIsType,
) where

import Prelude hiding (pi)
import Data.Binding as Surface'
import Data.Expression as Surface'
import Data.Module as Surface'
import Data.Name as Surface'
import Data.Name.Internal
import Data.Term as Surface'
import Data.Term.Types as Surface'
import Data.Typing as Surface'
import Data.Unification as Surface'
import Control.Applicative
import qualified Data.Set as Set

infixr `lambda`

-- | Construct a lambda from a type and a function from an argument variable to the resulting term. The variable will be picked automatically. The parameter type will be checked against `Type`, but there are no constraints on the type of the result.
lambda :: Term Expression -> (Term Expression -> Term Expression) -> Term Expression
lambda t f = checkedExpression checkType $ Lambda t body
where body = abstract f
checkType expected context = case out expected of
Binding (Expression (Lambda from to)) -> checkTypeAgainst from to context
Type _ -> checkTypeAgainst _type' _type' context
_ -> checkTypeAgainst implicit implicit context >>= expectUnifiable expected
checkTypeAgainst from to context = do
_ <- checkIsType t context
_ <- expectUnifiable from t
bodyType <- inferTypeOf body (extendContext t context body)
_ <- expectUnifiable to bodyType
return $ case shadowing body bodyType of
Just name -> t `pi` \ v -> substitute name v bodyType
_ -> t --> bodyType

infixr `pi`

-- | Construct a pi type from a type and a function from an argument variable to the resulting type. The variable will be picked automatically. The parameter type will be checked against `Type`, as will the substitution of the parameter type into the body.
pi :: Term Expression -> (Term Expression -> Term Expression) -> Term Expression
pi t f = checkedExpression checkType $ Lambda t body
where body = abstract f
checkType expected context = case out expected of
Binding (Expression (Lambda from to)) -> checkTypeAgainst from to context
Type _ -> checkTypeAgainst _type' _type' context
_ -> checkTypeAgainst implicit implicit context >>= expectUnifiable expected
checkTypeAgainst from to context = do
_ <- checkIsType t context
_ <- expectUnifiable from t
bodyType <- checkIsType body (extendContext t context body)
_ <- expectUnifiable to bodyType
return $ case shadowing body bodyType of
Just name -> t `pi` \ v -> substitute name v bodyType
_ -> t --> bodyType

infixr -->

-- | Construct a non-dependent function type between two types. Both operands will be checked against `Type`.
(-->) :: Term Expression -> Term Expression -> Term Expression
a --> b = checkedExpression (checkInferred inferType) $ Lambda a b
where inferType context = do
a' <- checkIsType a context
b' <- checkIsType b context
return $ a' --> b'

-- | Construct the application of one term to another. The first argument will be checked as a function type, and the second will be checked against that type’s domain. The resulting type will be the substitution of the domain type into the body.
apply :: Term Expression -> Term Expression -> Term Expression
apply a b = checkedExpression (checkInferred inferType) $ Application a b
where inferType context = do
(type', body) <- checkHasFunctionType a context
_ <- typeOf b type' context
return $ applySubstitution type' body


checkHasFunctionType :: Term Expression -> Context (Term Expression) -> Result (Term Expression, Term Expression)
checkHasFunctionType term context = inferTypeOf term context >>= checkIsFunctionType

checkIsFunctionType :: Term Expression -> Result (Term Expression, Term Expression)
checkIsFunctionType type' = case out type' of
Binding (Expression (Lambda type' body)) -> return (type', body)
_ -> Left "Expected function type."

instance Monoid a => Alternative (Either a) where
empty = Left mempty
Left a <|> Left b = Left $ a `mappend` b
Right a <|> _ = Right a
_ <|> Right b = Right b

checkIsType :: Term Expression -> Inferer (Term Expression)
checkIsType term context = typeOf term _type' context <|> typeOf term (_type' --> _type') context


instance Show (Term Expression) where
show = fst . para (\ b -> case b of
Binding (Variable n) -> (show n, maxBound)
Binding (Abstraction _ (_, body)) -> body

Type 0 -> ("Type", maxBound)
Type n -> ("Type" ++ showNumeral "₀₁₂₃₄₅₆₇₈₉" n, maxBound)

Binding (Expression (Application (_, a) (_, b))) -> (wrap 4 (<=) a ++ " " ++ wrap 4 (<) b, 4)

Binding (Expression (Lambda (_, type') (Term _ _ (Binding (Abstraction name term)), body))) | Set.member name (freeVariables term) -> ("λ " ++ show name ++ " : " ++ wrap 3 (<) type' ++ " . " ++ wrap 3 (<=) body, 3)
Binding (Expression (Lambda (_, type') (Term _ _ (Binding (Abstraction _ _)), body))) -> ("λ _ : " ++ wrap 3 (<) type' ++ " . " ++ wrap 3 (<=) body, 3)
Binding (Expression (Lambda (_, type') (_, body))) -> (wrap 3 (<) type' ++ " → " ++ wrap 3 (<=) body, 3)

Implicit -> ("_", maxBound)
Annotation (_, term) (_, type') -> (wrap 3 (<=) term ++ " : " ++ wrap 3 (<) type', 3)

:: (String, Int))
where wrap i op (s, j) | i `op` j = s
wrap _ _ (s, _) = "(" ++ s ++ ")"
import Surface.Language as Surface'
import Surface.Renaming as Surface'
import Surface.Unification as Surface'
Loading