Documentation ¶
Overview ¶
Example (Greenspun) ¶
Phillip Greenspun's tenth law says:
"Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp."
So let's implement a half-arsed lisp (Or rather, an AST that can optionally be executed upon if you write the correct interpreter)!
package main import ( "fmt" "log" "strings" "github.com/pkg/errors" ) const digits = "0123456789" type TyperExpression interface { Expression Typer } type λ struct { name string body Expression } func (n λ) Name() string { return n.name } func (n λ) Body() Expression { return n.body } func (n λ) IsLambda() bool { return true } type lit string func (n lit) Name() string { return string(n) } func (n lit) Body() Expression { return n } func (n lit) Type() Type { switch { case strings.ContainsAny(digits, string(n)) && strings.ContainsAny(digits, string(n[0])): return Float case string(n) == "true" || string(n) == "false": return Bool default: return nil } } func (n lit) IsLit() bool { return true } func (n lit) IsLambda() bool { return true } type app struct { f Expression arg Expression } func (n app) Fn() Expression { return n.f } func (n app) Body() Expression { return n.arg } func (n app) Arg() Expression { return n.arg } type let struct { name string def Expression in Expression } func (n let) Name() string { return n.name } func (n let) Def() Expression { return n.def } func (n let) Body() Expression { return n.in } type letrec struct { name string def Expression in Expression } func (n letrec) Name() string { return n.name } func (n letrec) Def() Expression { return n.def } func (n letrec) Body() Expression { return n.in } func (n letrec) Children() []Expression { return []Expression{n.def, n.in} } func (n letrec) IsRecursive() bool { return true } type prim byte const ( Float prim = iota Bool ) // implement Type func (t prim) Name() string { return t.String() } func (t prim) Apply(Subs) Substitutable { return t } func (t prim) FreeTypeVar() TypeVarSet { return nil } func (t prim) Normalize(TypeVarSet, TypeVarSet) (Type, error) { return t, nil } func (t prim) Types() Types { return nil } func (t prim) Eq(other Type) bool { if ot, ok := other.(prim); ok { return ot == t } return false } func (t prim) Format(s fmt.State, c rune) { fmt.Fprintf(s, t.String()) } func (t prim) String() string { switch t { case Float: return "Float" case Bool: return "Bool" } return "HELP" } // Phillip Greenspun's tenth law says: // // "Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp." // // So let's implement a half-arsed lisp (Or rather, an AST that can optionally be executed upon if you write the correct interpreter)! func main() { // haskell envy in a greenspun's tenth law example function! // // We'll assume the following is the "input" code // let fac n = if n == 0 then 1 else n * fac (n - 1) in fac 5 // and what we have is the AST fac := letrec{ "fac", λ{ "n", app{ app{ app{ lit("if"), app{ lit("isZero"), lit("n"), }, }, lit("1"), }, app{ app{lit("mul"), lit("n")}, app{lit("fac"), app{lit("--"), lit("n")}}, }, }, }, app{lit("fac"), lit("5")}, } // but first, let's start with something simple: // let x = 3 in x+5 simple := let{ "x", lit("3"), app{ app{ lit("+"), lit("5"), }, lit("x"), }, } env := SimpleEnv{ "--": &Scheme{tvs: TypeVarSet{'a'}, t: NewFnType(TypeVariable('a'), TypeVariable('a'))}, "if": &Scheme{tvs: TypeVarSet{'a'}, t: NewFnType(Bool, TypeVariable('a'), TypeVariable('a'), TypeVariable('a'))}, "isZero": &Scheme{t: NewFnType(Float, Bool)}, "mul": &Scheme{t: NewFnType(Float, Float, Float)}, "+": &Scheme{tvs: TypeVarSet{'a'}, t: NewFnType(TypeVariable('a'), TypeVariable('a'), TypeVariable('a'))}, } var scheme *Scheme var err error scheme, err = Infer(env, simple) if err != nil { log.Printf("%+v", errors.Cause(err)) } simpleType, ok := scheme.Type() fmt.Printf("simple Type: %v | isMonoType: %v | err: %v\n", simpleType, ok, err) scheme, err = Infer(env, fac) if err != nil { log.Printf("%+v", errors.Cause(err)) } facType, ok := scheme.Type() fmt.Printf("fac Type: %v | isMonoType: %v | err: %v", facType, ok, err) }
Output: simple Type: Float | isMonoType: true | err: <nil> fac Type: Float | isMonoType: true | err: <nil>
Index ¶
- func BorrowMSubs() mSubs
- func BorrowSSubs(size int) *sSubs
- func ReturnFnType(fnt *FunctionType)
- func ReturnSubs(sub Subs)
- func ReturnTypeVarSet(ts TypeVarSet)
- func ReturnTypes(ts Types)
- type Apply
- type Cloner
- type Constraint
- type Constraints
- type Env
- type Expression
- type Fresher
- type FunctionType
- func (t *FunctionType) Apply(sub Subs) Substitutable
- func (t *FunctionType) Arg() Type
- func (t *FunctionType) Clone() interface{}
- func (t *FunctionType) Eq(other Type) bool
- func (t *FunctionType) FlatTypes() Types
- func (t *FunctionType) Format(s fmt.State, c rune)
- func (t *FunctionType) FreeTypeVar() TypeVarSet
- func (t *FunctionType) Name() string
- func (t *FunctionType) Normalize(k, v TypeVarSet) (Type, error)
- func (t *FunctionType) Ret(recursive bool) Type
- func (t *FunctionType) String() string
- func (t *FunctionType) Types() Types
- type Inferer
- type Lambda
- type Let
- type LetRec
- type Literal
- type Namer
- type Record
- func (t *Record) Apply(subs Subs) Substitutable
- func (t *Record) Clone() interface{}
- func (t *Record) Eq(other Type) bool
- func (t *Record) Format(f fmt.State, c rune)
- func (t *Record) FreeTypeVar() TypeVarSet
- func (t *Record) Name() string
- func (t *Record) Normalize(k, v TypeVarSet) (Type, error)
- func (t *Record) String() string
- func (t *Record) Types() Types
- type Scheme
- type SimpleEnv
- type Subs
- type Substitutable
- type Substitution
- type Type
- type TypeConst
- func (t TypeConst) Apply(Subs) Substitutable
- func (t TypeConst) Eq(other Type) bool
- func (t TypeConst) Format(s fmt.State, c rune)
- func (t TypeConst) FreeTypeVar() TypeVarSet
- func (t TypeConst) Name() string
- func (t TypeConst) Normalize(k, v TypeVarSet) (Type, error)
- func (t TypeConst) String() string
- func (t TypeConst) Types() Types
- type TypeVarSet
- func (s TypeVarSet) Contains(tv TypeVariable) bool
- func (s TypeVarSet) Difference(other TypeVarSet) TypeVarSet
- func (s TypeVarSet) Equals(other TypeVarSet) bool
- func (s TypeVarSet) Index(tv TypeVariable) int
- func (s TypeVarSet) Intersect(other TypeVarSet) TypeVarSet
- func (s TypeVarSet) Len() int
- func (s TypeVarSet) Less(i, j int) bool
- func (s TypeVarSet) Set() TypeVarSet
- func (s TypeVarSet) Swap(i, j int)
- func (s TypeVarSet) Union(other TypeVarSet) TypeVarSet
- type TypeVariable
- func (t TypeVariable) Apply(sub Subs) Substitutable
- func (t TypeVariable) Eq(other Type) bool
- func (t TypeVariable) Format(s fmt.State, c rune)
- func (t TypeVariable) FreeTypeVar() TypeVarSet
- func (t TypeVariable) Name() string
- func (t TypeVariable) Normalize(k, v TypeVarSet) (Type, error)
- func (t TypeVariable) String() string
- func (t TypeVariable) Types() Types
- type Typer
- type Types
- type Var
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BorrowMSubs ¶
func BorrowMSubs() mSubs
BorrowMSubs gets a map based substitution from a shared pool. USE WITH CAUTION
func BorrowSSubs ¶
func BorrowSSubs(size int) *sSubs
BorrowSSubs gets a slice based substituiton from a shared pool. USE WITH CAUTION
func ReturnFnType ¶
func ReturnFnType(fnt *FunctionType)
ReturnFnType returns a *FunctionType to the pool. NewFnType automatically borrows from the pool. USE WITH CAUTION
func ReturnSubs ¶
func ReturnSubs(sub Subs)
ReturnSubs returns substitutions to the pool. USE WITH CAUTION.
func ReturnTypeVarSet ¶
func ReturnTypeVarSet(ts TypeVarSet)
ReturnTypeVarSet returns the TypeVarSet to pool. USE WITH CAUTION
func ReturnTypes ¶
func ReturnTypes(ts Types)
ReturnTypes returns the slice of types into the pool. USE WITH CAUTION
Types ¶
type Apply ¶
type Apply interface { Expression Fn() Expression }
Apply is an Expression/AST node that represents a function application
type Constraint ¶
type Constraint struct {
// contains filtered or unexported fields
}
A Constraint is well.. a constraint that says a must equal to b. It's used mainly in the constraint generation process.
func (Constraint) Apply ¶
func (c Constraint) Apply(sub Subs) Substitutable
func (Constraint) FreeTypeVar ¶
func (c Constraint) FreeTypeVar() TypeVarSet
type Constraints ¶
type Constraints []Constraint
Constraints is a slice of Constraint. Like a Constraint, it is also a Substitutable
func (Constraints) Apply ¶
func (cs Constraints) Apply(sub Subs) Substitutable
func (Constraints) FreeTypeVar ¶
func (cs Constraints) FreeTypeVar() TypeVarSet
type Env ¶
type Env interface { Substitutable SchemeOf(string) (*Scheme, bool) Clone() Env Add(string, *Scheme) Env Remove(string) Env }
An Env is essentially a map of names to schemes
type Expression ¶
type Expression interface {
Body() Expression
}
An Expression is basically an AST node. In its simplest form, it's lambda calculus
type Fresher ¶
type Fresher interface {
Fresh() TypeVariable
}
Fresher keeps track of all the TypeVariables that has been generated so far. It has one method - Fresh(), which is to create a new TypeVariable
type FunctionType ¶
type FunctionType struct {
// contains filtered or unexported fields
}
FunctionType is a type constructor that builds function types.
func NewFnType ¶
func NewFnType(ts ...Type) *FunctionType
NewFnType creates a new FunctionType. Functions are by default right associative. This:
NewFnType(a, a, a)
is short hand for this:
NewFnType(a, NewFnType(a, a))
func (*FunctionType) Apply ¶
func (t *FunctionType) Apply(sub Subs) Substitutable
func (*FunctionType) Arg ¶
func (t *FunctionType) Arg() Type
Arg returns the type of the function argument
func (*FunctionType) Eq ¶
func (t *FunctionType) Eq(other Type) bool
func (*FunctionType) FlatTypes ¶
func (t *FunctionType) FlatTypes() Types
FlatTypes returns the types in FunctionTypes as a flat slice of types. This allows for easier iteration in some applications
func (*FunctionType) FreeTypeVar ¶
func (t *FunctionType) FreeTypeVar() TypeVarSet
func (*FunctionType) Name ¶
func (t *FunctionType) Name() string
func (*FunctionType) Normalize ¶
func (t *FunctionType) Normalize(k, v TypeVarSet) (Type, error)
func (*FunctionType) Ret ¶
func (t *FunctionType) Ret(recursive bool) Type
Ret returns the return type of a function. If recursive is true, it will get the final return type
func (*FunctionType) String ¶
func (t *FunctionType) String() string
func (*FunctionType) Types ¶
func (t *FunctionType) Types() Types
type Lambda ¶
type Lambda interface { Expression Namer IsLambda() bool }
Lambda is an Expression/AST node that represents a function definiton
type Let ¶
type Let interface { // let name = def in body Expression Namer Def() Expression }
Let is an Expression/AST node that represents the standard let polymorphism found in functional languages
type Record ¶
type Record struct {
// contains filtered or unexported fields
}
Record is a basic record/tuple type. It takes an optional name.
func NewRecordType ¶
NewRecordType creates a new Record Type
func (*Record) Apply ¶
func (t *Record) Apply(subs Subs) Substitutable
func (*Record) FreeTypeVar ¶
func (t *Record) FreeTypeVar() TypeVarSet
type Scheme ¶
type Scheme struct {
// contains filtered or unexported fields
}
Scheme represents a polytype. It basically says this:
∀TypeVariables.Type.
What this means is for all TypeVariables enclosed in Type, those TypeVariables can be of any Type.
func Generalize ¶
Generalize takes an env and a type and creates the most general possible type - which is a polytype
Generalization ¶
If ...
Γ ⊢ e: T1 T1 ∉ free(Γ) --------------------------- Γ ⊢ e: ∀ α.T1
func Infer ¶
func Infer(env Env, expr Expression) (*Scheme, error)
Infer takes an env, and an expression, and returns a scheme.
The Infer function is the core of the HM type inference system. This is a reference implementation and is completely servicable, but not quite performant. You should use this as a reference and write your own infer function.
Very briefly, these rules are implemented:
Var ¶
If x is of type T, in a collection of statements Γ, then we can infer that x has type T when we come to a new instance of x
x: T ∈ Γ ----------- Γ ⊢ x: T
Apply ¶
If f is a function that takes T1 and returns T2; and if x is of type T1; then we can infer that the result of applying f on x will yield a result has type T2
Γ ⊢ f: T1→T2 Γ ⊢ x: T1 ------------------------- Γ ⊢ f(x): T2
Lambda Abstraction ¶
If we assume x has type T1, and because of that we were able to infer e has type T2 then we can infer that the lambda abstraction of e with respect to the variable x, λx.e, will be a function with type T1→T2
Γ, x: T1 ⊢ e: T2 ------------------- Γ ⊢ λx.e: T1→T2
Let ¶
If we can infer that e1 has type T1 and if we take x to have type T1 such that we could infer that e2 has type T2, then we can infer that the result of letting x = e1 and substituting it into e2 has type T2
Γ, e1: T1 Γ, x: T1 ⊢ e2: T2 -------------------------------- Γ ⊢ let x = e1 in e2: T2
func NewScheme ¶
func NewScheme(tvs TypeVarSet, t Type) *Scheme
func (*Scheme) Apply ¶
func (s *Scheme) Apply(sub Subs) Substitutable
func (*Scheme) FreeTypeVar ¶
func (s *Scheme) FreeTypeVar() TypeVarSet
type SimpleEnv ¶
func (SimpleEnv) Apply ¶
func (e SimpleEnv) Apply(sub Subs) Substitutable
func (SimpleEnv) FreeTypeVar ¶
func (e SimpleEnv) FreeTypeVar() TypeVarSet
type Subs ¶
type Subs interface { Get(TypeVariable) (Type, bool) Add(TypeVariable, Type) Subs Remove(TypeVariable) Subs // Iter() <-chan Substitution Iter() []Substitution Size() int Clone() Subs }
Subs is a list of substitution. Internally there are two very basic substitutions - one backed by map and the other a normal slice
func Unify ¶
Unify unifies the two types and returns a list of substitutions. These are the rules:
Type Constants and Type Constants ¶
Type constants (atomic types) have no substitution
c ~ c : []
Type Variables and Type Variables ¶
Type variables have no substitutions if there are no instances:
a ~ a : []
Default Unification ¶
if type variable 'a' is not in 'T', then unification is simple: replace all instances of 'a' with 'T'
a ∉ T --------------- a ~ T : [a/T]
type Substitutable ¶
type Substitutable interface { Apply(Subs) Substitutable FreeTypeVar() TypeVarSet }
Substitutable is any type that can have a set of substitutions applied on it, as well as being able to know what its free type variables are
type Substitution ¶
type Substitution struct { Tv TypeVariable T Type }
A Substitution is a tuple representing the TypeVariable and the replacement Type
type Type ¶
type Type interface { Substitutable Name() string // Name is the name of the constructor Normalize(TypeVarSet, TypeVarSet) (Type, error) // Normalize normalizes all the type variable names in the type Types() Types // If the type is made up of smaller types, then it will return them Eq(Type) bool // equality operation fmt.Formatter fmt.Stringer }
Type represents all the possible type constructors.
func Instantiate ¶
Instantiate takes a fresh name generator, an a polytype and makes a concrete type out of it.
If ...
Γ ⊢ e: T1 T1 ⊑ T ---------------------- Γ ⊢ e: T
type TypeConst ¶
type TypeConst string
TypeConst are the default implementation of a constant type. Feel free to implement your own. TypeConsts should be immutable (so no pointer types plz)
func (TypeConst) Apply ¶
func (t TypeConst) Apply(Subs) Substitutable
func (TypeConst) FreeTypeVar ¶
func (t TypeConst) FreeTypeVar() TypeVarSet
type TypeVarSet ¶
type TypeVarSet []TypeVariable
TypeVarSet is a set of TypeVariable
func BorrowTypeVarSet ¶
func BorrowTypeVarSet(size int) TypeVarSet
BorrowTypeVarSet gets a TypeVarSet of size from pool. USE WITH CAUTION
func (TypeVarSet) Contains ¶
func (s TypeVarSet) Contains(tv TypeVariable) bool
func (TypeVarSet) Difference ¶
func (s TypeVarSet) Difference(other TypeVarSet) TypeVarSet
func (TypeVarSet) Equals ¶
func (s TypeVarSet) Equals(other TypeVarSet) bool
func (TypeVarSet) Index ¶
func (s TypeVarSet) Index(tv TypeVariable) int
func (TypeVarSet) Intersect ¶
func (s TypeVarSet) Intersect(other TypeVarSet) TypeVarSet
func (TypeVarSet) Len ¶
func (s TypeVarSet) Len() int
func (TypeVarSet) Less ¶
func (s TypeVarSet) Less(i, j int) bool
func (TypeVarSet) Set ¶
func (s TypeVarSet) Set() TypeVarSet
func (TypeVarSet) Swap ¶
func (s TypeVarSet) Swap(i, j int)
func (TypeVarSet) Union ¶
func (s TypeVarSet) Union(other TypeVarSet) TypeVarSet
type TypeVariable ¶
type TypeVariable rune
TypeVariable is a variable that ranges over the types - that is to say it can take any type.
func (TypeVariable) Apply ¶
func (t TypeVariable) Apply(sub Subs) Substitutable
func (TypeVariable) Eq ¶
func (t TypeVariable) Eq(other Type) bool
func (TypeVariable) FreeTypeVar ¶
func (t TypeVariable) FreeTypeVar() TypeVarSet
func (TypeVariable) Name ¶
func (t TypeVariable) Name() string
func (TypeVariable) Normalize ¶
func (t TypeVariable) Normalize(k, v TypeVarSet) (Type, error)
func (TypeVariable) String ¶
func (t TypeVariable) String() string
func (TypeVariable) Types ¶
func (t TypeVariable) Types() Types
type Typer ¶
type Typer interface {
Type() Type
}
A Typer is an Expression node that knows its own Type
type Types ¶
type Types []Type
Types is a slice of Type. Future additions to the methods of this type may be possible
func BorrowTypes ¶
BorrowTypes gets a slice of Types with size. USE WITH CAUTION.
type Var ¶
type Var interface { Expression Namer Typer }
Var is an expression representing a variable