Variable names:
- kind:
k
(Kind) - type constructor:
tc
(Tycon) - type variable:
v
(Tyvar)- fixed type variable:
f
- generic type variable:
g
- fixed type variable:
- type:
t
(Type) - class:
c
(Class) - instance:
it
(Instance) - predicate:
p
,q
(Pred)- deferred predicate:
d
- retained predicate:
r
- deferred predicate:
- qualified type:
qt
(QualType) - class environment:
ce
(ClassEnv) - scheme:
sc
(Scheme) - substitution:
s
(Subst) - unifier:
u
(Subst) - assumption:
s
(Assump) - identifier:
i
(Id) - literal:
l
(Literal) - pattern:
pat
(Pat) - expression:
e
,f
, ... (Expr) - alternative:
alt
(Alt) - binding group:
bg
(BindGroup)
- identifier: a variable (or function) name
- type: the possible forms a value can take (don't sue me, I know that's a bad definition)
- kind: a description of how types need to be composed with other types
(e.g. the kind of
Int
is*
, the kind of(a, b)
is* -> * -> *
) - substitutions: a mapping of type variables to types (possibly other type varaiables).
- merge: make sure two substitutions agree, then concatenate them.
- unifier: a substitution that, when applied to two types, makes those types equal.
- most general unifier (mgu): a unifier
u
such that any other unifiers
can be written ass' @@ u
for some substitutions'
. - matching types: "Given two types
t1
andt2
, the goal of matching is to find a substitutions
such thatapply s t1 = t2
." It's sometimes called "one-way matching." - predicate: a constraint on a type, requiring that a type variable be a
member of a type class.
- E.g. in
(Num a) => a -> Int
, the predicate is(Num a)
- E.g. in
- qualified: a type class restricted in how it can be instantiated by a list of predicates.
- context: The list of predicates in a qualified type.
- head: The actual type in a qualified type.
- class environment: the type classes that currently exist
- default types: in some situations, like show (1 + read "1"), the type can be ambiguous (read coult return either an Int or a Float and both would work). The default types are a list of what order to try types in when multiple can match.
- head-normal form: class arguments of the form
v t1 ... tn
wherev
is a type variable andt1..tn
are zero or more types (i.e. it starts with a variable).- Haskell requires that class arguments be in head normal form.
- entail: a predicate is entailed by other predicates if it always holds when the others hold (it's like implication).
- simplifying predicates: reducing a list of predicates to its minimal form
by:
- Removing duplicates
- Eliminating predicates that are known to hold (like
Num Int
) - Simplifying using superclass info (e.g.
[Eq a, Ord a]
to justOrd a
)
- type scheme (or just scheme): a type along with a set of bound type variables (a bound type variable is one that is defined within the scheme itself).
- quantify a type: replace all occurances of certain type variables with generic variables?
- assumption: a scheme that we assume applies to a variable.
- instantiate a type: replace all generic variables with other variables?
- alternative: "An Alt specifies the left and right hand sides of a function
definition" - huh?
- I think that this is actually a way to express a list of possible ways to match things (e.g. in a case statement of function definition).
- generalization: calculating the most general types that are possible.
- ambiguous type scheme: A type scheme
ps => t
whereps
contains generic variables that do not appear int
. (A generic in the predicate that's not in the type -- would that happen with rank-N types?) Example:stringInc x = show (read x + 1)
-- what type doesx
have? The type ofstringInc
would be(Read a, Num a) => String -> String
. - default types: a special feature of Haskell where it tries to resolve ambiguous types by trying some default types until it finds one that fits.
- enumId
Int -> Id
- Creates an identifier from a number
- fn (infix)
Type -> Type -> Type
- A helper function that creates the type (a -> b) from the types a and b
- list
Type -> Type
- A helper function that creates the type [a] from the type a
- pair
Type -> Type -> Type
- A helper function that creates the type (a, b) from the types a and b
- kind
t -> Kind
- The function defined by the class
HasKind
- Finds the kind of a type
- nullSubst
Subst
- Creates an empty substitution
- +-> (infix)
Tyvar -> Type -> Subst
- Creates a list of substitutions with one substitution in it, replacing left side to the right side.
- apply
Subst -> t -> t
- A function in the class
Types
- Replaces parts of the type with their substitutions
- tv
t -> [Tyvar]
- A function in the class
Types
- Walks a type to find the set of all the type variables in it
- @@ (infix)
Subst -> Subst -> Subst
- "Composes" substitutions
apply (s1 @@ s2)
is equivalent toapply s1 . apply s2
- code:
s1 @@ s2 = [ (u, apply s1 t) | (u,t) <- s2 ] ++ s1
- It works by resubstituting anything in
s2
that is substituted ins1
, then merging the result withs1
.
- merge
Monad m => Subst -> Subst -> m Subst
- Similar to
@@
- Requires that substitutions in the two agree (where they substitute the same variable).
- Doesn't replace anything in one with anything from the other.
- mgu
Monad m => Type -> Type -> m Subst
- Finds (if possible) the most general unifier of two types (i.e. unifies)
- varBind
Monad m => Tyvar -> Type -> m Subst
- A helper function used in unifying a type variable with another type
- match
Monad m => Type -> Type -> m Subst
- matches two types
- similar to
mgu
, except that it usesmerge
instead of@@
to combine substitutions.
- mguPred
Pred -> Pred -> Maybe Subst
- Defined as
lift mgu
- matchPred
Pred -> Pred -> Maybe Subst
- Defined as
lift match
- Matches the heads of both predicates, assuming they have the same context.
- lift
- Applys the function (either
mgu
ormatch
) to the head of both predicates, assuming both predicates have the same context. - i.e. it unifies the types if the conditions match.
- Probably unifying the types will mostly be merging two type variables.
- Applys the function (either
- classes
ClassEnv -> (Id -> Maybe Class)
- Gets the class mapping function out of a ClassEnv
- defaults
ClassEnv -> [Type]
- Gets the list of default types
- super
ClassEnv -> Id -> [Id]
- Looks up the parent classes for a given class name
- insts
ClassEnv -> Id -> [Inst]
- Looks up the instaces of a given class name
- defined
Maybe a -> Bool
- A helper function (basically "isJust")
- modify
ClassEnv -> Id -> Class -> ClassEnv
- Updates/Sets the definition of a class in the class environment
- initialEnv
:: ClassEnv
- Creates an empty set of classes
- <:> (infix)
EnvTransformer -> EnvTransformer -> EnvTransformer
- Composes two environment transformer functions into one.
- Since environment transformers return a Maybe type, this is just lifting the two into the maybe monad.
- addClass
Id -> [Id] -> EnvTransformer
- Adds a new class (name is first arg, parents are secoond arg) to the environment.
- Requires that all parents be defined already, so calls to this must be topologically sorted beforehand.
- addPreludeClasses (also, addCoreClasses and addNumClasses)
:: EnvTransformer
- helper functions to add default classes to the environment
- addInst
[Pred] -> Pred -> EnvTransformer
- Adds an instance to a class (the predicate in the second argument contains the name of the class this instance is being added to, so it doesn't need to be a separate argument.)
- The first list of predicates is what must hold in order for this instance to be implemented / this predicate to hold.
- overlap
Pred -> Pred -> Bool
- A helper function that tests if there is some predicate that is a substitution instance of the heads of both instance declarations.
- That can happen with two identical instances (e.g. two copies of
Eq Int
) or two cases that can unify (e.g.Eq [a]
andEq [Int]
).
- exampleInsts
:: EnvTransformer
- a helper function that creates a default class environment with default classes and default instances
- bySuper
ClassEnv -> Pred -> [Pred]
- Result may contain duplicates
- Returns the list of predicates that also hold when the given predicate holds becuase those predicates are superclasses of the class in the predicate (and if a type is an instance of a class, it's always also an instance of all of the superclasses of that class).
- byInst
ClassEnv -> Pred -> Maybe [Pred]
- Determines subgoals for a given predicate.
- Gives the predicates that must be true for that predicate to hold by looking at the predicates for the matching instance of the class.
- entail
ClassEnv -> [Pred] -> Pred -> Bool
- Tests if the predicate is entailed by the list of predicates.
- (uses
bySuper
andbyInst
) - It returns true if.f.
p
(third arg) holds true when all the predicatesps
(second arg) also hold true. - This can be for one of two reasons:
- The predicate
p
is already in the listps
or it's super classes - All the subgoals of
p
(as found bybyInst
) are entailed byps
- The predicate
- inHnf
Pred -> Bool
- Short for: "in head-normal form"
- toHnf
Monad m => ClassEnv -> Pred -> m [Pred]
- Returns the predicate unchanged (but in a list) if it's already in HNF
- Otherwise, it tries to convert all the subgoals (found using
byInst
) to HNF.
- toHnfs
Monad m => ClassEnv -> [Pred] -> m [Pred]
- Converts a list of predicates to HNF (basically
concat $ mapM toHnf
)
- simplify
ClassEnv -> [Pred] -> [Pred]
- simplifies a list of predicates.
- Removes predicates that are entailed by other predicates.
- reduce
Monad m => ClassEnv -> [Pred] -> m [Pred]
- Converts all predicates to HNF then simplifies
- (Pretty much just a convenience function)
- scEntail
ClassEnv -> [Pred] -> Pred -> Bool
- an alternative (faster?) entail function that could be used in
simplify
- quantify
[Tyvar] -> Qual Type -> Scheme
(remember,Qual Type
means[Pred] :=> Type
)- quantifies a qualified type
- toScheme
Type -> Scheme
- Helper function that converts a type to a scheme with no kinds and no predicates
- find
Monad m => Id -> [Assump] -> m Scheme
- Helper function that looks up a scheme by the variable associated with it
- runTI
TI a -> a
- helper function to run the TI monad
- getSubst
TI Subst
- returns the current substitution in the TI monad
- e.g.
s <- getSubst
- unify
Type -> Type -> TI ()
- Extends the substitution with the most general unifier of its arguments
- extSubst
Subst -> TI ()
- Helper function that extends the current substitution in the TI monad
- Uses
@@
to combine the new substitution with the current one.
- newTVar
Kind -> TI Type
- Creates a new type variable (with a unique name) of the given kind (in the TI monad).
- freshInst
Scheme -> TI (Qual Type)
- Creates new type variables for each kind in the scheme, then instantiates the qualified type with them.
- inst
[Type] -> t -> t
- Defined by the class
Instantiate
- A variation of
apply
that works on generic variables - It replaces each occurrence of a generic variable
TGen n
int
withts !! n
.
- tiLit
Literal -> TI ([Pred], Type)
- Gets the type for a literal value in the inference monad
- Some literals (e.g. Char) get a complete type (e.g.
([], tChar)
) - Others (e.g. Int) get a type var + predicate (e.g.
([IsIn "Num" v], v)
)
- tiPat
Pat -> TI ([Pred], [Assump], Type)
- Gets the type of the thing that the pattern matches along with (in the assumptions) information about any variable that gets introduced.
- tiPats
[Pat] -> Ti ([Pred], [Assump], Type)
- A variant of tiPat that works on lists of patterns (to make life easier)
- tiExpr
Infer Expr Type
- ==
ClassEnv -> [Assump] -> Expr -> Ti ([Pred], Type)
- ==
- does type inference for expressions
- For variables and constants, the result is what you get from making a
fresh instantiation of the scheme.
- TODO: why? Are constants polymorphic?
- Inferring the type of an application of expressions works by first
inferring the type of the two expressions, then unifies the type of the
left expression (which should be a function) with the type
tf -> t
wheretf
is the type of the right expression andt
is a new type variable (apparently representing the return value). - To infer the type of a let binding, it uses
tiBindingGroup
(not defined yet!) and types the epxression using those assumptions (TODO: understand this better).
- tiAlt
Infer Alt Type
- ==
ClassEnv -> [Assump] -> Alt -> Ti ([Pred], Type)
- ==
- Does type inference for an alternative
- It types the patterns, then types the expressions using the assumptions from typing the patterns (TODO: understand the implications of using the assumptions), and finally returns a function type that takes as aguments the values in the pattern and returns the type of the expression.
- tiAlts
ClassEnv -> [Assump] -> [Alt] -> Type -> Ti [Pred]
- why does this take a type and not return a list of types?
- Because it's checking that all the alternatives aggree with that type, it's not using them to figure out the type (?) / it's probably adding the type to the inference monad.
- split
Monad m => ClassEnv -> [Tyvar] -> [Tyvar] -> [Pred] -> m ([Pred], [Pred])
- Does three things:
- Performs context reduction
- Identifies deferred predicates
- Eliminates some predicates using defaults
- ambiguities
ClassEnv -> [Tyvar] -> [Pred] -> [Ambiguity]
- Finds type ambiguities
- It works by:
- Finding type variables in the predicates that are not in the given list of type variables.
- For each of those, finding the predicates that mention that type variable.
- candidates
ClassEnv -> Ambiguity -> [Type]
- Finds candidates for resolving a particular ambiguity
- Uses the 4 rules for picking defaults to find the types (if any) that can be used as defaults for the ambiguity.
- withDefaults
Monad m => ([Ambiguity] -> [Type] -> a) -> ClassEnv -> [Tyvar] -> [Pred] -> m a
- Finds ambiguities and resolves them using the given function.
- (Fails if any of the ambiguous types can't be defaulted)
- defaultedPreds
Monad m => ClassEnv -> [Tyvar] -> [Pred] -> m [Pred]
- Uses
withDefaults
to get a list of all predicates for ambiguous types.
- defaultSubst
Monad m => ClassEnv -> [Tyvar] -> [Pred] -> m Subst
- Uses
withDefaults
to build a substitution that applies the defaults. - It gets all the ambiguous types variables from the ambiguities and maps them to the default type for those variables.
- HasKind
- Instances: Tyvar, Tycoon, Type
- Functions: kind
- Types
- Instances: Type, [Types], Qual Types, Scheme, Assumption
- Functions: apply, tv
- Monad (defined elsewhere)
- Instances: TI
- Instantiate
- Instances: Type, Pred, [Instantiate], Qual [Instantiate]
- Functions: inst
- Id
type Id = String
- an identifier
- Depending on the context, this can be a variable, a type variable, or the name of a type constructor.
- Kind (
Star
orKfun
)data Kind = Star | Kfun Kind Kind
- Represents a kind
- Type (
TVar
,TCon
,TAp
, orTGen
)data Type = TVar Tyvar | TCon Tycon | TAp Type Type | TGen Int
- Represents a type
- E.g.
tArrow = TCon (Tycon "(->)" (Kfun Star (Kfun Star Star)))
- Arrow means function
- TVar is a type variable (like
a
) - TCon is a type constructor (like
Either
) - TGen stores a generic type variable, identified only by an integer.
- TAp applies a type constructor to other types (e.g.
TAp tList tInt
) - Tyvar
data Tyvar = Tyvar Id Kind
- Stores a type variable and the kind of type that variable must have.
- Tycon
data Tycon = Tycon Id Kind
- Represents a type constructor and what kind of type it is
- e.g.
tList = TCon (Tycon "[]" (Kfun Star Star))
- Subst
type Subst = [(Tyvar, Type)]
- A mapping from type variable to its current substitution
- Pred (
IsIn
)data Pred = IsIn Id Type
- Stores a predicate on a type variable.
- Qual (
:=>
)data Qual t = [Pred] :=> t
- Stores a type qualified by a list of 0+ predicates
- Class
type Class = ([Id], [Inst])
- Used to represent a class
- The first list (
[Id]
) is the list of super classes (e.g. Eq is a superclass of Ord). - The second list (
[Inst]
) stores instances of that class (each of which is qualified, because Inst is an alias for a qualified predicate).
- Inst
type Inst = Qual Pred
- Used to represent an instance of a class.
- Expands to
[Pred] :=> Pred
- It means that a predicate (on the right) is true if the list of predicates on the left hold.
- ClassEnv
- Fields:
- classes ::
ID -> Maybe Class
- defaults ::
[Type]
- classes ::
- Represents a class environment
- Fields:
- EnvTransformer
type EnvTransformer = ClassEnv -> Maybe ClassEnv
- A type used for functions that modify class environments, with the possibility of an error.
- Scheme (
Forall
)data Scheme = Forall [Kind] (Qual Type)
- "Type schemes are used to represent polymorphic types"
- Represents a type scheme
- Contains:
- A list of Kinds (used as a mapping between integers and kinds). These
are the kinds for any generic types that appear in the qualified type.
TGen n
has kindks !! n
- A qualified type (a list of predicates and a type).
- A list of Kinds (used as a mapping between integers and kinds). These
are the kinds for any generic types that appear in the qualified type.
- Assump (
:>:
)data Assump = Id :>: Scheme
- Represents an assumption
- TI (i.e. Type Inference)
newtype TI a = TI (Subst -> Int -> (Subst, Int, a))
- An instance of Monad
- Maintains the current substitution and the next number to use when generating type variables.
- Infer
type Infer e t = ClassEnv -> [Assump] -> e -> TI ([Pred], t)
- Infer is a type alias for convenience.
- Most typing rules are variants of it.
- Literal (
LitInt
,LitChar
,LitRat
,LitStr
)data Literal = ...
- Represents a literal value in the code to be typped
- Pat (
PVar
,PWildcard
,PAs
,PLit
,PNpk
,PCon
)data Pat =
PVar Id
PWildcard
PAs Id Pat
PLit Literal
PNpk Id Integer
PCon Assump [Pat]
- Represents a pattern (e.g. in a
case
statement)
- Expr (
Var
,Lit
,Const
,Ap
,Let
)data Expr =
Var Id
Lit Literal
Const Assump
Ap Expr Expr
Let BindingGroup
- Represents an expression
- Alt
type Alt = ([Pat], Expr)
- Represents an alternative
- Ambiguity
type Ambiguity = (Tyvar, [Pred])
- Represents a type ambiguity
- The first part is the type variable that is ambiguous
- The second part is the predicates known for that type var
byInst :: ClassEnv -> Pred -> Maybe [Pred]
byInst ce p@(IsIn i t) = msum [ tryInst it | it <- insts ce i ]
where tryInst (ps :=> h) = do u <- matchPred h p
Just (map (apply u) ps)
Relevant bits:
- ClassEnv: classes (ID -> Maybe Class) and defaults ([Type])
- Pred: IsIn Id Type
- Qual: [Pred] :=> t
- matchPred: Matches the heads of both predicates, assuming they have the same context.
- apply: Replaces parts of the type with their substitutions.
Subst -> t -> t
it
is one of the instance instances of class i, which is the class in the
predicate.
tryInst
matches the head (a Pred
in this case) of the instance with the
given predicate (i.e. sees if it could be made to match by making it more
specific). If it succeeds, it then applies the substitution that resulted from
matching to the context of the instance. The result of that is what gets
returned.
Example:
Say the predicate is IsIn "Ord" "Eq [Int]"
(I'm not sure if that totally makes
sense). It might find a matching instance Eq [a]
with the other predicate
IsIn "Ord" "a"
. That instance would match with the substitution a +-> Int
. When applied to the predicates, you get IsIn "Ord" "Int"
.
code:
quantify :: [Tyvar] -> Qual Type -> Scheme
quantify vs qt = Forall ks (apply s qt)
where vs' = [ v | v <- tv qt, v `elem` vs ]
ks = map kind vs'
s = zip vs' (map TGen [0..])
vs'
is the list of the type variables in qt
(2nd arg) that appear in vs
(1st arg). ks
is a list of the kinds of vs'
. s
is a list of tuples where
the first element is one of vs'
and the second is a TGen
with a unique
number.
The result is a type scheme with the kinds ks
and the qualified type resulting from applying s
to qt
.
This is finding the type variables in the qualified type that also appear in the first arg, creating generic variables of the same kind, and then substituting those generic variables in for the type variables.
References:
- Types
- Tyvar (
Tyvar Id Kind
) - Qual Type (=== to
[Pred] :=> Type
) - Pred (
IsIn Id Type
) - Scheme (
Forall [Kind] (Qual Type)
) - Type (
TGen Int
)
- Tyvar (
- Functions
- apply (
Subst -> t -> t
, of class Types): Replaces things with thir substitutions. - kind (
t -> Kind
, of class HasKind): Finds the kind of a type - tv (
t -> [Tyvar]
, of class Types): Finds all type variables.
- apply (
tiPat (PCon (i:>:sc) pats) = do
(ps,as,ts) <- tiPats pats
t' <- newTVar Star
(qs :=> t) <- freshInst sc
unify t (foldr fn t' ts)
return (ps++qs, as, t')
By lines:
- Type inference for all the sub patterns
- Create a new type variable with kind *
- Instantiates the given scheme (replacing generic variables)
- Unifies the type resulting from instantiating the scheme with the type of a function that takes the parameters to the constructor and returns the new type variable.a c
- Adds the predicates from instantiating the scheme to the list of predicates and returns the new type variable.
References:
- Types
- Pat
- Assump (
data Assump = Id :>: Scheme
) - Scheme (
Forall [Kind] (Qual Type)
) - Qual (
data Qual t = [Pred] :=> t
)
- Functioons
- tiPats (
[Pat] -> Ti ([Pred], [Assump], Type)
) - newTVar (
Kind -> TI Type
) - freshInst (
Scheme -> TI (Qual Type)
) - fn (
Type -> Type -> Type
)
- tiPats (
Code:
split :: Monad m => ClassEnv -> [Tyvar] -> [Tyvar] -> [Pred]
-> m ([Pred], [Pred])
split ce fs gs ps = do
ps' <- reduce ce ps
let (ds, rs) = partition (all (`elem` fs) . tv) ps'
rs' <- defaultedPreds ce (fs++gs) rs
return (ds, rs \\ rs')
Some things which this function may or may not do:
- Simplify the list of predicates (it looks like this does happen, using
reduce
) - Defer "fixed" variables (those that appear in assumptions) to the enclosing bindings (huh?)
- Use default types in when there is ambiguity. It seems to do this with
defaultedPreds
.
It breaks the list of predicates into deferred predicates (ds
) and retained
predicates (rs
). The retained predicates are the ones that become part of the
type (rs :=> t
) while the deferred predicates become constraints for the
enclosing scope. The deferred predicates are the ones "that contain only fixed
type variables." (TODO: what does it mean for a type variable to be "fixed"?)
It determines which of the two groups to put a predicate in by the output of a
inner function. That function first takes all the type variables in the
predicate (using tv
), then tests if they are all elements of the given list of
type variables fs
. If they all are, the predicate goes in the list of deferred
predicates.
It returns a tuple containing the list of deferred predicates and a list of the retained predicates that are not in the list of defaulted predicates (i.e. it removes the predicates that are replaced with a default type)
Aguments:
ce
(ClassEnv)fs
([Tyvar]) "Fixed" variables -- "the variables appearing free in the assumptions" (TODO: what does that mean?)gs
([Tyvar]) The set of variables over which we would like to quantify.ps
([Pred])
References:
- Types
- ClassEnv (classes
Id -> Maybe Class
, defaults[Type]
) - Class
type Class = ([Id], [Inst])
- Tyvar
Tyvar Id Kind
- Pred
IsIn Id Type
- ClassEnv (classes
- Functions
- reduce
Monad m => ClassEnv -> [Pred] -> m [Pred]
Converts all predicates to HNF then simplifies. - partition (from Data.List),
(a -> Bool) -> [a] -> ([a], [a])
- defaultedPreds
Monad m => ClassEnv -> [Tyvar] -> [Pred] -> m [Pred]
\\
(infix, from Data.List)Eq a => [a] -> [a] -> [a]
List difference.- elem (from prelude)
Eq a -> a -> t a -> Bool
Tests for inclusion.
- reduce
Rules for when defaulting is allowed:
- All of the predicates are in the form
IsIn c (TVar v)
wherec
is a class. - At least one of the classes is a standard numeric class (one of: Num, Integral, Floating, Fractional, Real, RealFloat, RealFrac)
- All of the classes involved are standard elasses (which includes the numeric classes, plus things like Eq, Ord, Show, Functor, etc)
- There is at least one type in the list of default types that is an instance of all the mentioned classes.
The first type in the list of defaults that matches is the one that gets used.
- Near the end of section 4, it talks about being able to figure out the kind of
TAp t t'
using onlyt
, assuming it's well-formed. If it's well-formed, wouldn't the kind always end up being just*
in the end? - What really is an alternative?
- How does a type scheme (/ an assumption) capture that some variables must be a
type compatible with the type of some function (for let polymorphism), even
when that type isn't known yet?
- Does the type scheme include the type variable of the thing that it must be a subtype of?