pax_global_header00006660000000000000000000000064131574541460014524gustar00rootroot0000000000000052 comment=61efb3290a086d1335e8954b3734c102126818ba hm-1.0.0/000077500000000000000000000000001315745414600121265ustar00rootroot00000000000000hm-1.0.0/.gitignore000066400000000000000000000004331315745414600141160ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof # vendor /vendorhm-1.0.0/.travis.yml000066400000000000000000000004411315745414600142360ustar00rootroot00000000000000sudo: false language: go branches: only: - master go: - 1.5.x - 1.6.x - 1.7.x - 1.8.x - 1.9.x - tip env: global: - GOARCH=amd64 - TRAVISTEST=true before_install: - go get github.com/mattn/goveralls script: - $HOME/gopath/bin/goveralls -service=travis-cihm-1.0.0/Gopkg.lock000066400000000000000000000020071315745414600140460ustar00rootroot00000000000000# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. [[projects]] name = "github.com/davecgh/go-spew" packages = ["spew"] revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] name = "github.com/pkg/errors" packages = ["."] revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] name = "github.com/pmezard/go-difflib" packages = ["difflib"] revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] name = "github.com/stretchr/testify" packages = ["assert"] revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0" version = "v1.1.4" [[projects]] branch = "master" name = "github.com/xtgo/set" packages = ["."] revision = "4431f6b51265b1e0b76af4dafc09d6f12c2bdcd0" [solve-meta] analyzer-name = "dep" analyzer-version = 1 inputs-digest = "e010eb1269d6f582c0c9b306d2506a5bcfd99464e64a0f21c9be12478b262b37" solver-name = "gps-cdcl" solver-version = 1 hm-1.0.0/Gopkg.toml000066400000000000000000000014251315745414600140740ustar00rootroot00000000000000 # Gopkg.toml example # # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md # for detailed Gopkg.toml documentation. # # required = ["github.com/user/thing/cmd/thing"] # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] # # [[constraint]] # name = "github.com/user/project" # version = "1.0.0" # # [[constraint]] # name = "github.com/user/project2" # branch = "dev" # source = "github.com/myfork/project2" # # [[override]] # name = "github.com/x/y" # version = "2.4.0" ignored = ["github.com/alecthomas/assert"] [[constraint]] name = "github.com/pkg/errors" version = "0.8.0" [[constraint]] name = "github.com/stretchr/testify" version = "1.1.4" [[constraint]] branch = "master" name = "github.com/xtgo/set" hm-1.0.0/LICENCE000066400000000000000000000020541315745414600131140ustar00rootroot00000000000000MIT License Copyright (c) 2016 Xuanyi Chew Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hm-1.0.0/README.md000066400000000000000000000035211315745414600134060ustar00rootroot00000000000000# hm [![GoDoc](https://godoc.org/github.com/chewxy/hm?status.svg)](https://godoc.org/github.com/chewxy/hm) [![Build Status](https://travis-ci.org/chewxy/hm.svg?branch=master)](https://travis-ci.org/chewxy/hm) [![Coverage Status](https://coveralls.io/repos/github/chewxy/hm/badge.png)](https://coveralls.io/github/chewxy/hm) Package hm is a simple Hindley-Milner type inference system in Go. It provides the necessary data structures and functions for creating such a system. # Installation # This package is go-gettable: `go get -u github.com/chewxy/hm` There are very few dependencies that this package uses. Therefore there isn't a need for vendoring tools. However, package hm DOES provide a `Gopkg.toml` and `Gopkg.lock` for any potential users of the [dep](https://github.com/golang/dep) tool. Here is a listing of the dependencies of `hm`: |Package|Used For|Vitality|Notes|Licence| |-------|--------|--------|-----|-------| |[errors](https://github.com/pkg/errors)|Error wrapping|Can do without, but this is by far the superior error solution out there|Stable API for the past 6 months|[errors licence](https://github.com/pkg/errors/blob/master/LICENSE) (MIT/BSD-like)| |[testify/assert](https://github.com/stretchr/testify)|Testing|Can do without but will be a massive pain in the ass to test||[testify licence](https://github.com/stretchr/testify/blob/master/LICENSE) (MIT/BSD-like)| # Usage TODO: Write this # Notes This package is used by [Gorgonia](https://github.com/chewxy/gorgonia) as part of the graph building process. It is also used by several other internal projects of this author, all sharing a similar theme of requiring a type system, which is why this was abstracted out. # Contributing This library is developed using Github. Therefore the workflow is very github-centric. # Licence Package `hm` is licenced under the MIT licence. hm-1.0.0/const.go000066400000000000000000000006741315745414600136120ustar00rootroot00000000000000// Package hm provides a Hindley-Milner type and type inference system. // // If you are creating a new programming language and you'd like it to be // strongly typed with parametric polymorphism (or just have Haskell-envy), // this library provides the necessary types and functions for creating such a system. // // The key to the HM type inference system is in the Unify() function. package hm const letters = `abcdefghijklmnopqrstuvwxyz` hm-1.0.0/constraint.go000066400000000000000000000011061315745414600146370ustar00rootroot00000000000000package hm import "fmt" // A Constraint is well.. a constraint that says a must equal to b. It's used mainly in the constraint generation process. type Constraint struct { a, b Type } func (c Constraint) Apply(sub Subs) Substitutable { c.a = c.a.Apply(sub).(Type) c.b = c.b.Apply(sub).(Type) return c } func (c Constraint) FreeTypeVar() TypeVarSet { var retVal TypeVarSet retVal = c.a.FreeTypeVar().Union(retVal) retVal = c.b.FreeTypeVar().Union(retVal) return retVal } func (c Constraint) Format(state fmt.State, r rune) { fmt.Fprintf(state, "{%v = %v}", c.a, c.b) } hm-1.0.0/constraint_test.go000066400000000000000000000011441315745414600157000ustar00rootroot00000000000000package hm import "testing" func TestConstraint(t *testing.T) { c := Constraint{ a: TypeVariable('a'), b: NewFnType(TypeVariable('b'), TypeVariable('c')), } ftv := c.FreeTypeVar() if !ftv.Equals(TypeVarSet{TypeVariable('a'), TypeVariable('b'), TypeVariable('c')}) { t.Error("the free type variables of a Constraint is not as expected") } subs := mSubs{ 'a': NewFnType(proton, proton), 'b': proton, 'c': neutron, } c = c.Apply(subs).(Constraint) if !c.a.Eq(NewFnType(proton, proton)) { t.Errorf("c.a: %v", c) } if !c.b.Eq(NewFnType(proton, neutron)) { t.Errorf("c.b: %v", c) } } hm-1.0.0/debug.go000066400000000000000000000020001315745414600135330ustar00rootroot00000000000000// +build debug package hm import ( "fmt" "log" "os" "strings" "sync/atomic" ) // DEBUG returns true when it's in debug mode const DEBUG = false var tabcount uint32 var _logger_ = log.New(os.Stderr, "", 0) var replacement = "\n" func tc() int { return int(atomic.LoadUint32(&tabcount)) } func enterLoggingContext() { atomic.AddUint32(&tabcount, 1) tabs := tc() _logger_.SetPrefix(strings.Repeat("\t", tabs)) replacement = "\n" + strings.Repeat("\t", tabs) } func leaveLoggingContext() { tabs := tc() tabs-- if tabs < 0 { atomic.StoreUint32(&tabcount, 0) tabs = 0 } else { atomic.StoreUint32(&tabcount, uint32(tabs)) } _logger_.SetPrefix(strings.Repeat("\t", tabs)) replacement = "\n" + strings.Repeat("\t", tabs) } func logf(format string, others ...interface{}) { if DEBUG { // format = strings.Replace(format, "\n", replacement, -1) s := fmt.Sprintf(format, others...) s = strings.Replace(s, "\n", replacement, -1) _logger_.Println(s) // _logger_.Printf(format, others...) } } hm-1.0.0/env.go000066400000000000000000000017231315745414600132500ustar00rootroot00000000000000package hm // An Env is essentially a map of names to schemes type Env interface { Substitutable SchemeOf(string) (*Scheme, bool) Clone() Env Add(string, *Scheme) Env Remove(string) Env } type SimpleEnv map[string]*Scheme func (e SimpleEnv) Apply(sub Subs) Substitutable { logf("Applying %v to env", sub) if sub == nil { return e } for _, v := range e { v.Apply(sub) // apply mutates Scheme, so no need to set } return e } func (e SimpleEnv) FreeTypeVar() TypeVarSet { var retVal TypeVarSet for _, v := range e { retVal = v.FreeTypeVar().Union(retVal) } return retVal } func (e SimpleEnv) SchemeOf(name string) (retVal *Scheme, ok bool) { retVal, ok = e[name]; return } func (e SimpleEnv) Clone() Env { retVal := make(SimpleEnv) for k, v := range e { retVal[k] = v.Clone() } return retVal } func (e SimpleEnv) Add(name string, s *Scheme) Env { e[name] = s return e } func (e SimpleEnv) Remove(name string) Env { delete(e, name) return e } hm-1.0.0/env_test.go000066400000000000000000000033071315745414600143070ustar00rootroot00000000000000package hm import ( "testing" "github.com/stretchr/testify/assert" ) func TestSimpleEnv(t *testing.T) { assert := assert.New(t) var orig, env Env var expected SimpleEnv // Add orig = make(SimpleEnv) orig = orig.Add("foo", NewScheme( TypeVarSet{'a', 'b', 'c'}, TypeVariable('a'), )) orig = orig.Add("bar", NewScheme( TypeVarSet{'b', 'c', 'd'}, TypeVariable('a'), )) orig = orig.Add("baz", NewScheme( TypeVarSet{'a', 'b', 'c'}, neutron, )) qs := NewScheme( TypeVarSet{'a', 'b'}, proton, ) orig = orig.Add("qux", qs) expected = SimpleEnv{ "foo": NewScheme( TypeVarSet{'a', 'b', 'c'}, TypeVariable('a'), ), "bar": NewScheme( TypeVarSet{'b', 'c', 'd'}, TypeVariable('a'), ), "baz": NewScheme( TypeVarSet{'a', 'b', 'c'}, neutron, ), "qux": NewScheme( TypeVarSet{'a', 'b'}, proton, ), } assert.Equal(expected, orig) // Get s, ok := orig.SchemeOf("qux") if s != qs || !ok { t.Error("Expected to get scheme of \"qux\"") } // Remove orig = orig.Remove("qux") delete(expected, "qux") assert.Equal(expected, orig) // Clone env = orig.Clone() assert.Equal(orig, env) subs := mSubs{ 'a': proton, 'b': neutron, 'd': electron, 'e': proton, } env = env.Apply(subs).(Env) expected = SimpleEnv{ "foo": &Scheme{ tvs: TypeVarSet{'a', 'b', 'c'}, t: TypeVariable('a'), }, "bar": &Scheme{ tvs: TypeVarSet{'b', 'c', 'd'}, t: proton, }, "baz": &Scheme{ tvs: TypeVarSet{'a', 'b', 'c'}, t: neutron, }, } assert.Equal(expected, env) env = orig.Clone() ftv := env.FreeTypeVar() correctFTV := TypeVarSet{'a'} if !correctFTV.Equals(ftv) { t.Errorf("Expected freetypevars to be equal. Got %v instead", ftv) } } hm-1.0.0/example_greenspun_test.go000066400000000000000000000105031315745414600172340ustar00rootroot00000000000000package hm 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 Example_greenspun() { // 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: // fac Type: Float | isMonoType: true | err: } hm-1.0.0/expression.go000066400000000000000000000023451315745414600146600ustar00rootroot00000000000000package hm // A Namer is anything that knows its own name type Namer interface { Name() string } // A Typer is an Expression node that knows its own Type type Typer interface { Type() Type } // An Inferer is an Expression that can infer its own Type given an Env type Inferer interface { Infer(Env, Fresher) (Type, error) } // An Expression is basically an AST node. In its simplest form, it's lambda calculus type Expression interface { Body() Expression } // Var is an expression representing a variable type Var interface { Expression Namer Typer } // Literal is an Expression/AST Node representing a literal type Literal interface { Var IsLit() bool } // Apply is an Expression/AST node that represents a function application type Apply interface { Expression Fn() Expression } // LetRec is an Expression/AST node that represents a recursive let type LetRec interface { Let IsRecursive() bool } // Let is an Expression/AST node that represents the standard let polymorphism found in functional languages type Let interface { // let name = def in body Expression Namer Def() Expression } // Lambda is an Expression/AST node that represents a function definiton type Lambda interface { Expression Namer IsLambda() bool } hm-1.0.0/functionType.go000066400000000000000000000054451315745414600151540ustar00rootroot00000000000000package hm import "fmt" // FunctionType is a type constructor that builds function types. type FunctionType struct { a, b Type } // 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 NewFnType(ts ...Type) *FunctionType { if len(ts) < 2 { panic("Expected at least 2 input types") } retVal := borrowFnType() retVal.a = ts[0] if len(ts) > 2 { retVal.b = NewFnType(ts[1:]...) } else { retVal.b = ts[1] } return retVal } func (t *FunctionType) Name() string { return "→" } func (t *FunctionType) Apply(sub Subs) Substitutable { t.a = t.a.Apply(sub).(Type) t.b = t.b.Apply(sub).(Type) return t } func (t *FunctionType) FreeTypeVar() TypeVarSet { return t.a.FreeTypeVar().Union(t.b.FreeTypeVar()) } func (t *FunctionType) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v → %v", t.a, t.b) } func (t *FunctionType) String() string { return fmt.Sprintf("%v", t) } func (t *FunctionType) Normalize(k, v TypeVarSet) (Type, error) { var a, b Type var err error if a, err = t.a.Normalize(k, v); err != nil { return nil, err } if b, err = t.b.Normalize(k, v); err != nil { return nil, err } return NewFnType(a, b), nil } func (t *FunctionType) Types() Types { retVal := BorrowTypes(2) retVal[0] = t.a retVal[1] = t.b return retVal } func (t *FunctionType) Eq(other Type) bool { if ot, ok := other.(*FunctionType); ok { return ot.a.Eq(t.a) && ot.b.Eq(t.b) } return false } // Other methods (accessors mainly) // Arg returns the type of the function argument func (t *FunctionType) Arg() Type { return t.a } // Ret returns the return type of a function. If recursive is true, it will get the final return type func (t *FunctionType) Ret(recursive bool) Type { if !recursive { return t.b } if fnt, ok := t.b.(*FunctionType); ok { return fnt.Ret(recursive) } return t.b } // FlatTypes returns the types in FunctionTypes as a flat slice of types. This allows for easier iteration in some applications func (t *FunctionType) FlatTypes() Types { retVal := BorrowTypes(8) // start with 8. Can always grow retVal = retVal[:0] if a, ok := t.a.(*FunctionType); ok { ft := a.FlatTypes() retVal = append(retVal, ft...) ReturnTypes(ft) } else { retVal = append(retVal, t.a) } if b, ok := t.b.(*FunctionType); ok { ft := b.FlatTypes() retVal = append(retVal, ft...) ReturnTypes(ft) } else { retVal = append(retVal, t.b) } return retVal } // Clone implements Cloner func (t *FunctionType) Clone() interface{} { retVal := new(FunctionType) if ac, ok := t.a.(Cloner); ok { retVal.a = ac.Clone().(Type) } else { retVal.a = t.a } if bc, ok := t.b.(Cloner); ok { retVal.b = bc.Clone().(Type) } else { retVal.b = t.b } return retVal } hm-1.0.0/functionType_test.go000066400000000000000000000070761315745414600162150ustar00rootroot00000000000000package hm import ( "testing" "github.com/stretchr/testify/assert" ) func TestFunctionTypeBasics(t *testing.T) { fnType := NewFnType(TypeVariable('a'), TypeVariable('a'), TypeVariable('a')) if fnType.Name() != "→" { t.Errorf("FunctionType should have \"→\" as a name. Got %q instead", fnType.Name()) } if fnType.String() != "a → a → a" { t.Errorf("Expected \"a → a → a\". Got %q instead", fnType.String()) } if !fnType.Arg().Eq(TypeVariable('a')) { t.Error("Expected arg of function to be 'a'") } if !fnType.Ret(false).Eq(NewFnType(TypeVariable('a'), TypeVariable('a'))) { t.Error("Expected ret(false) to be a → a") } if !fnType.Ret(true).Eq(TypeVariable('a')) { t.Error("Expected final return type to be 'a'") } // a very simple fn fnType = NewFnType(TypeVariable('a'), TypeVariable('a')) if !fnType.Ret(true).Eq(TypeVariable('a')) { t.Error("Expected final return type to be 'a'") } ftv := fnType.FreeTypeVar() if len(ftv) != 1 { t.Errorf("Expected only one free type var") } for _, fas := range fnApplyTests { fn := fas.fn.Apply(fas.sub).(*FunctionType) if !fn.Eq(fas.expected) { t.Errorf("Expected %v. Got %v instead", fas.expected, fn) } } // bad shit f := func() { NewFnType(TypeVariable('a')) } assert.Panics(t, f) } var fnApplyTests = []struct { fn *FunctionType sub Subs expected *FunctionType }{ {NewFnType(TypeVariable('a'), TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, proton)}, {NewFnType(TypeVariable('a'), TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, neutron)}, {NewFnType(TypeVariable('a'), TypeVariable('b')), mSubs{'c': proton, 'd': neutron}, NewFnType(TypeVariable('a'), TypeVariable('b'))}, {NewFnType(TypeVariable('a'), TypeVariable('b')), mSubs{'a': proton, 'c': neutron}, NewFnType(proton, TypeVariable('b'))}, {NewFnType(TypeVariable('a'), TypeVariable('b')), mSubs{'c': proton, 'b': neutron}, NewFnType(TypeVariable('a'), neutron)}, {NewFnType(electron, proton), mSubs{'a': proton, 'b': neutron}, NewFnType(electron, proton)}, // a -> (b -> c) {NewFnType(TypeVariable('a'), TypeVariable('b'), TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, neutron, proton)}, {NewFnType(TypeVariable('a'), TypeVariable('a'), TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, proton, neutron)}, {NewFnType(TypeVariable('a'), TypeVariable('b'), TypeVariable('c')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, neutron, TypeVariable('c'))}, {NewFnType(TypeVariable('a'), TypeVariable('c'), TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, TypeVariable('c'), neutron)}, // (a -> b) -> c {NewFnType(NewFnType(TypeVariable('a'), TypeVariable('b')), TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFnType(NewFnType(proton, neutron), proton)}, } func TestFunctionType_FlatTypes(t *testing.T) { fnType := NewFnType(TypeVariable('a'), TypeVariable('b'), TypeVariable('c')) ts := fnType.FlatTypes() correct := Types{TypeVariable('a'), TypeVariable('b'), TypeVariable('c')} assert.Equal(t, ts, correct) fnType2 := NewFnType(fnType, TypeVariable('d')) correct = append(correct, TypeVariable('d')) ts = fnType2.FlatTypes() assert.Equal(t, ts, correct) } func TestFunctionType_Clone(t *testing.T) { fnType := NewFnType(TypeVariable('a'), TypeVariable('b'), TypeVariable('c')) assert.Equal(t, fnType.Clone(), fnType) rec := NewRecordType("", TypeVariable('a'), NewFnType(TypeVariable('a'), TypeVariable('b')), TypeVariable('c')) fnType = NewFnType(rec, rec) assert.Equal(t, fnType.Clone(), fnType) } hm-1.0.0/hm.go000066400000000000000000000230721315745414600130650ustar00rootroot00000000000000package hm import "github.com/pkg/errors" // Cloner is any type that can clone type Cloner interface { Clone() interface{} } // 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 Fresher interface { Fresh() TypeVariable } type inferer struct { env Env cs Constraints t Type count int } func newInferer(env Env) *inferer { return &inferer{ env: env, } } func (infer *inferer) Fresh() TypeVariable { retVal := letters[infer.count] infer.count++ return TypeVariable(retVal) } func (infer *inferer) lookup(name string) error { s, ok := infer.env.SchemeOf(name) if !ok { return errors.Errorf("Undefined %v", name) } infer.t = Instantiate(infer, s) return nil } func (infer *inferer) consGen(expr Expression) (err error) { // explicit types/inferers - can fail switch et := expr.(type) { case Typer: if infer.t = et.Type(); infer.t != nil { return nil } case Inferer: if infer.t, err = et.Infer(infer.env, infer); err == nil && infer.t != nil { return nil } err = nil // reset errors } // fallbacks switch et := expr.(type) { case Literal: return infer.lookup(et.Name()) case Var: if err = infer.lookup(et.Name()); err != nil { infer.env.Add(et.Name(), &Scheme{t: et.Type()}) err = nil } case Lambda: tv := infer.Fresh() env := infer.env // backup infer.env = infer.env.Clone() infer.env.Remove(et.Name()) sc := new(Scheme) sc.t = tv infer.env.Add(et.Name(), sc) if err = infer.consGen(et.Body()); err != nil { return errors.Wrapf(err, "Unable to infer body of %v. Body: %v", et, et.Body()) } infer.t = NewFnType(tv, infer.t) infer.env = env // restore backup case Apply: if err = infer.consGen(et.Fn()); err != nil { return errors.Wrapf(err, "Unable to infer Fn of Apply: %v. Fn: %v", et, et.Fn()) } fnType, fnCs := infer.t, infer.cs if err = infer.consGen(et.Body()); err != nil { return errors.Wrapf(err, "Unable to infer body of Apply: %v. Body: %v", et, et.Body()) } bodyType, bodyCs := infer.t, infer.cs tv := infer.Fresh() cs := append(fnCs, bodyCs...) cs = append(cs, Constraint{fnType, NewFnType(bodyType, tv)}) infer.t = tv infer.cs = cs case LetRec: tv := infer.Fresh() // env := infer.env // backup infer.env = infer.env.Clone() infer.env.Remove(et.Name()) infer.env.Add(et.Name(), &Scheme{tvs: TypeVarSet{tv}, t: tv}) if err = infer.consGen(et.Def()); err != nil { return errors.Wrapf(err, "Unable to infer the definition of a letRec %v. Def: %v", et, et.Def()) } defType, defCs := infer.t, infer.cs s := newSolver() s.solve(defCs) if s.err != nil { return errors.Wrapf(s.err, "Unable to solve constraints of def: %v", defCs) } sc := Generalize(infer.env.Apply(s.sub).(Env), defType.Apply(s.sub).(Type)) infer.env.Remove(et.Name()) infer.env.Add(et.Name(), sc) if err = infer.consGen(et.Body()); err != nil { return errors.Wrapf(err, "Unable to infer body of letRec %v. Body: %v", et, et.Body()) } infer.t = infer.t.Apply(s.sub).(Type) infer.cs = infer.cs.Apply(s.sub).(Constraints) infer.cs = append(infer.cs, defCs...) case Let: env := infer.env if err = infer.consGen(et.Def()); err != nil { return errors.Wrapf(err, "Unable to infer the definition of a let %v. Def: %v", et, et.Def()) } defType, defCs := infer.t, infer.cs s := newSolver() s.solve(defCs) if s.err != nil { return errors.Wrapf(s.err, "Unable to solve for the constraints of a def %v", defCs) } sc := Generalize(env.Apply(s.sub).(Env), defType.Apply(s.sub).(Type)) infer.env = infer.env.Clone() infer.env.Remove(et.Name()) infer.env.Add(et.Name(), sc) if err = infer.consGen(et.Body()); err != nil { return errors.Wrapf(err, "Unable to infer body of let %v. Body: %v", et, et.Body()) } infer.t = infer.t.Apply(s.sub).(Type) infer.cs = infer.cs.Apply(s.sub).(Constraints) infer.cs = append(infer.cs, defCs...) default: return errors.Errorf("Expression of %T is unhandled", expr) } return nil } // Instantiate takes a fresh name generator, an a polytype and makes a concrete type out of it. // // If ... // Γ ⊢ e: T1 T1 ⊑ T // ---------------------- // Γ ⊢ e: T // func Instantiate(f Fresher, s *Scheme) Type { l := len(s.tvs) tvs := make(TypeVarSet, l) var sub Subs if l > 30 { sub = make(mSubs) } else { sub = newSliceSubs(l) } for i, tv := range s.tvs { fr := f.Fresh() tvs[i] = fr sub = sub.Add(tv, fr) } return s.t.Apply(sub).(Type) } // 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 Generalize(env Env, t Type) *Scheme { logf("generalizing %v over %v", t, env) enterLoggingContext() defer leaveLoggingContext() var envFree, tFree, diff TypeVarSet if env != nil { envFree = env.FreeTypeVar() } tFree = t.FreeTypeVar() switch { case envFree == nil && tFree == nil: goto ret case len(envFree) > 0 && len(tFree) > 0: defer ReturnTypeVarSet(envFree) defer ReturnTypeVarSet(tFree) case len(envFree) > 0 && len(tFree) == 0: // cannot return envFree because envFree will just be sorted and set case len(envFree) == 0 && len(tFree) > 0: // return ? } diff = tFree.Difference(envFree) ret: return &Scheme{ tvs: diff, t: t, } } // 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 Infer(env Env, expr Expression) (*Scheme, error) { if expr == nil { return nil, errors.Errorf("Cannot infer a nil expression") } if env == nil { env = make(SimpleEnv) } infer := newInferer(env) if err := infer.consGen(expr); err != nil { return nil, err } s := newSolver() s.solve(infer.cs) if s.err != nil { return nil, s.err } if infer.t == nil { return nil, errors.Errorf("infer.t is nil") } t := infer.t.Apply(s.sub).(Type) return closeOver(t) } // 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] // func Unify(a, b Type) (sub Subs, err error) { logf("%v ~ %v", a, b) enterLoggingContext() defer leaveLoggingContext() switch at := a.(type) { case TypeVariable: return bind(at, b) default: if a.Eq(b) { return nil, nil } if btv, ok := b.(TypeVariable); ok { return bind(btv, a) } atypes := a.Types() btypes := b.Types() defer ReturnTypes(atypes) defer ReturnTypes(btypes) if len(atypes) == 0 && len(btypes) == 0 { goto e } return unifyMany(atypes, btypes) e: } err = errors.Errorf("Unification Fail: %v ~ %v cannot be unified", a, b) return } func unifyMany(a, b Types) (sub Subs, err error) { logf("UnifyMany %v %v", a, b) enterLoggingContext() defer leaveLoggingContext() if len(a) != len(b) { return nil, errors.Errorf("Unequal length. a: %v b %v", a, b) } for i, at := range a { bt := b[i] if sub != nil { at = at.Apply(sub).(Type) bt = bt.Apply(sub).(Type) } var s2 Subs if s2, err = Unify(at, bt); err != nil { return nil, err } if sub == nil { sub = s2 } else { sub2 := compose(sub, s2) defer ReturnSubs(s2) if sub2 != sub { defer ReturnSubs(sub) } sub = sub2 } } return } func bind(tv TypeVariable, t Type) (sub Subs, err error) { logf("Binding %v to %v", tv, t) switch { // case tv == t: case occurs(tv, t): err = errors.Errorf("recursive unification") default: ssub := BorrowSSubs(1) ssub.s[0] = Substitution{tv, t} sub = ssub } logf("Sub %v", sub) return } func occurs(tv TypeVariable, s Substitutable) bool { ftv := s.FreeTypeVar() defer ReturnTypeVarSet(ftv) return ftv.Contains(tv) } func closeOver(t Type) (sch *Scheme, err error) { sch = Generalize(nil, t) err = sch.Normalize() logf("closeoversch: %v", sch) return } hm-1.0.0/hm_test.go000066400000000000000000000116201315745414600141200ustar00rootroot00000000000000package hm import "testing" var unifyTests = []struct { name string a Type b Type subs Subs err bool // does it error? }{ {"a ~ a (recursive unification)", TypeVariable('a'), TypeVariable('a'), nil, true}, {"a ~ b", TypeVariable('a'), TypeVariable('b'), mSubs{'a': TypeVariable('b')}, false}, {"a ~ proton", TypeVariable('a'), proton, mSubs{'a': proton}, false}, {"proton ~ a", proton, TypeVariable('a'), mSubs{'a': proton}, false}, // typeconst ~ typeconst {"proton ~ proton", proton, proton, nil, false}, {"proton ~ neutron", proton, neutron, nil, true}, {"List a ~ List proton", list{TypeVariable('a')}, list{proton}, mSubs{'a': proton}, false}, // function types {"List a → List a ~ List proton → List proton", NewFnType(list{TypeVariable('a')}, list{TypeVariable('a')}), NewFnType(list{proton}, list{proton}), mSubs{'a': proton}, false}, {"List proton → List proton ~ List a → List a", NewFnType(list{proton}, list{proton}), NewFnType(list{TypeVariable('a')}, list{TypeVariable('a')}), mSubs{'a': proton}, false}, {"List a → a ~ List proton → proton", NewFnType(list{TypeVariable('a')}, TypeVariable('a')), NewFnType(list{proton}, proton), mSubs{'a': proton}, false}, {"List proton → proton ~ List a → a ", NewFnType(list{proton}, proton), NewFnType(list{TypeVariable('a')}, TypeVariable('a')), mSubs{'a': proton}, false}, {"List a → a → List a ~ List proton → proton → b", NewFnType(list{TypeVariable('a')}, TypeVariable('a'), list{TypeVariable('a')}), NewFnType(list{proton}, proton, TypeVariable('b')), mSubs{'a': proton, 'b': list{proton}}, false}, {"(a, a, b) ~ (proton, proton, neutron)", NewRecordType("", TypeVariable('a'), TypeVariable('a'), TypeVariable('b')), NewRecordType("", proton, proton, neutron), mSubs{'a': proton, 'b': neutron}, false}, } func TestUnify(t *testing.T) { // assert := assert.New(t) var t0, t1 Type var u0, u1 Type var sub Subs var err error for _, uts := range unifyTests { // logf("unifying %v", uts.name) t0 = uts.a t1 = uts.b sub, err = Unify(t0, t1) switch { case err == nil && uts.err: t.Errorf("Test %q - Expected an error: %v | u0: %#v, u1: %#v", uts.name, err, u0, u1) case err != nil && !uts.err: t.Errorf("Test %q errored: %v ", uts.name, err) } if uts.err { continue } if uts.subs == nil { if sub != nil { t.Errorf("Test: %q Expected no substitution. Got %v instead", uts.name, sub) } continue } for _, s := range uts.subs.Iter() { if T, ok := sub.Get(s.Tv); !ok { t.Errorf("Test: %q TypeVariable %v expected in result", uts.name, s.Tv) } else if T != s.T { t.Errorf("Test: %q Expected TypeVariable %v to be substituted by %v. Got %v instead", uts.name, s.Tv, s.T, T) } } if uts.subs.Size() != sub.Size() { t.Errorf("Test: %q Expected subs to be the same size", uts.name) } sub = nil } } var inferTests = []struct { name string expr Expression correct Type correctTVS TypeVarSet err bool }{ {"Lit", lit("1"), Float, nil, false}, {"Undefined Lit", lit("a"), nil, nil, true}, {"App", app{lit("+"), lit("1")}, NewFnType(Float, Float), nil, false}, {"Lambda", λ{"n", app{lit("+"), lit("1")}}, NewFnType(TypeVariable('a'), Float, Float), TypeVarSet{'a'}, false}, {"Lambda (+1)", λ{"a", app{lit("+1"), lit("a")}}, NewFnType(TypeVariable('a'), TypeVariable('a')), TypeVarSet{'a'}, false}, {"Var - found", variable("x"), proton, nil, false}, {"Var - notfound", variable("y"), nil, nil, true}, {"Self Infer - no err", selfInferer(true), proton, nil, false}, {"Self Infer - err", selfInferer(false), nil, nil, true}, {"nil expr", nil, nil, nil, true}, } func TestInfer(t *testing.T) { env := SimpleEnv{ "+": &Scheme{tvs: TypeVarSet{'a'}, t: NewFnType(TypeVariable('a'), TypeVariable('a'), TypeVariable('a'))}, "+1": &Scheme{tvs: TypeVarSet{'a'}, t: NewFnType(TypeVariable('a'), TypeVariable('a'))}, "x": NewScheme(nil, proton), } for _, its := range inferTests { sch, err := Infer(env, its.expr) if its.err { if err == nil { t.Errorf("Test %q : Expected error. %v", its.name, sch) } continue } else { if err != nil { t.Errorf("Test %q Error: %v", its.name, err) } } if !sch.t.Eq(its.correct) { t.Errorf("Test %q: Expected %v. Got %v", its.name, its.correct, sch.t) } for _, tv := range its.correctTVS { if !sch.tvs.Contains(tv) { t.Errorf("Test %q: Expected %v to be in the scheme.", its.name, tv) break } } if len(its.correctTVS) != len(sch.tvs) { t.Errorf("Test %q: Expected scheme to have %v. Got %v instead", its.name, its.correctTVS, sch.tvs) } } // test without env its := inferTests[0] sch, err := Infer(nil, its.expr) if err != nil { t.Errorf("Testing a nil Env. Shouldn't have errored. Got err: %v", err) } if !sch.t.Eq(its.correct) { t.Errorf("Testing nil Env. Expected %v to be in the scheme. Got scheme %v instead", its.correct, sch) } } hm-1.0.0/perf.go000066400000000000000000000067601315745414600134220ustar00rootroot00000000000000package hm import "sync" const ( poolSize = 4 extraCap = 2 ) var sSubPool = [poolSize]*sync.Pool{ &sync.Pool{ New: func() interface{} { return &sSubs{s: make([]Substitution, 1, 1+extraCap)} }, }, &sync.Pool{ New: func() interface{} { return &sSubs{s: make([]Substitution, 2, 2+extraCap)} }, }, &sync.Pool{ New: func() interface{} { return &sSubs{s: make([]Substitution, 3, 3+extraCap)} }, }, &sync.Pool{ New: func() interface{} { return &sSubs{s: make([]Substitution, 4, 4+extraCap)} }, }, } var mSubPool = &sync.Pool{ New: func() interface{} { return make(mSubs) }, } // ReturnSubs returns substitutions to the pool. USE WITH CAUTION. func ReturnSubs(sub Subs) { switch s := sub.(type) { case mSubs: for k := range s { delete(s, k) } mSubPool.Put(sub) case *sSubs: size := cap(s.s) - 2 if size > 0 && size < poolSize+1 { // reset to empty for i := range s.s { s.s[i] = Substitution{} } s.s = s.s[:size] sSubPool[size-1].Put(sub) } } } // BorrowMSubs gets a map based substitution from a shared pool. USE WITH CAUTION func BorrowMSubs() mSubs { return mSubPool.Get().(mSubs) } // BorrowSSubs gets a slice based substituiton from a shared pool. USE WITH CAUTION func BorrowSSubs(size int) *sSubs { if size > 0 && size < 5 { retVal := sSubPool[size-1].Get().(*sSubs) return retVal } s := make([]Substitution, size) return &sSubs{s: s} } var typesPool = [poolSize]*sync.Pool{ &sync.Pool{ New: func() interface{} { return make(Types, 1) }, }, &sync.Pool{ New: func() interface{} { return make(Types, 2) }, }, &sync.Pool{ New: func() interface{} { return make(Types, 3) }, }, &sync.Pool{ New: func() interface{} { return make(Types, 4) }, }, } // BorrowTypes gets a slice of Types with size. USE WITH CAUTION. func BorrowTypes(size int) Types { if size > 0 && size < poolSize+1 { return typesPool[size-1].Get().(Types) } return make(Types, size) } // ReturnTypes returns the slice of types into the pool. USE WITH CAUTION func ReturnTypes(ts Types) { if size := cap(ts); size > 0 && size < poolSize+1 { ts = ts[:cap(ts)] for i := range ts { ts[i] = nil } typesPool[size-1].Put(ts) } } var typeVarSetPool = [poolSize]*sync.Pool{ &sync.Pool{ New: func() interface{} { return make(TypeVarSet, 1) }, }, &sync.Pool{ New: func() interface{} { return make(TypeVarSet, 2) }, }, &sync.Pool{ New: func() interface{} { return make(TypeVarSet, 3) }, }, &sync.Pool{ New: func() interface{} { return make(TypeVarSet, 4) }, }, } // BorrowTypeVarSet gets a TypeVarSet of size from pool. USE WITH CAUTION func BorrowTypeVarSet(size int) TypeVarSet { if size > 0 && size < poolSize+1 { return typeVarSetPool[size-1].Get().(TypeVarSet) } return make(TypeVarSet, size) } // ReturnTypeVarSet returns the TypeVarSet to pool. USE WITH CAUTION func ReturnTypeVarSet(ts TypeVarSet) { var def TypeVariable if size := cap(ts); size > 0 && size < poolSize+1 { ts = ts[:cap(ts)] for i := range ts { ts[i] = def } typeVarSetPool[size-1].Put(ts) } } var fnTypePool = &sync.Pool{ New: func() interface{} { return new(FunctionType) }, } func borrowFnType() *FunctionType { return fnTypePool.Get().(*FunctionType) } // ReturnFnType returns a *FunctionType to the pool. NewFnType automatically borrows from the pool. USE WITH CAUTION func ReturnFnType(fnt *FunctionType) { if a, ok := fnt.a.(*FunctionType); ok { ReturnFnType(a) } if b, ok := fnt.b.(*FunctionType); ok { ReturnFnType(b) } fnt.a = nil fnt.b = nil fnTypePool.Put(fnt) } hm-1.0.0/perf_test.go000066400000000000000000000043631315745414600144560ustar00rootroot00000000000000package hm import "testing" func TestSubsPool(t *testing.T) { var def TypeVariable for i := 0; i < poolSize; i++ { s := BorrowSSubs(i + 1) if cap(s.s) != (i+1)+extraCap { t.Errorf("Expected s to have cap of %d", i+1+extraCap) goto mSubTest } if len(s.s) != (i + 1) { t.Errorf("Expected s to have a len of %d", i+1) goto mSubTest } s.s[0] = Substitution{TypeVariable('a'), electron} ReturnSubs(s) s = BorrowSSubs(i + 1) for _, subst := range s.s { if subst.T != nil { t.Errorf("sSubsPool %d error: not clean: %v", i, subst) break } if subst.Tv != def { t.Errorf("sSubsPool %d error: not clean: %v", i, subst) break } } mSubTest: m := BorrowMSubs() if len(m) != 0 { t.Errorf("Expected borrowed mSubs to have 0 length") } m['a'] = electron ReturnSubs(m) m = BorrowMSubs() if len(m) != 0 { t.Errorf("Expected borrowed mSubs to have 0 length") } } // oob tests s := BorrowSSubs(10) if cap(s.s) != 10 { t.Error("Expected a cap of 10") } ReturnSubs(s) } func TestTypesPool(t *testing.T) { for i := 0; i < poolSize; i++ { ts := BorrowTypes(i + 1) if cap(ts) != i+1 { t.Errorf("Expected ts to have a cap of %v", i+1) } ts[0] = proton ReturnTypes(ts) ts = BorrowTypes(i + 1) for _, v := range ts { if v != nil { t.Errorf("Expected reshly borrowed Types to be nil") } } } // oob ts := BorrowTypes(10) if cap(ts) != 10 { t.Errorf("Expected a cap to 10") } } func TestTypeVarSetPool(t *testing.T) { var def TypeVariable for i := 0; i < poolSize; i++ { ts := BorrowTypeVarSet(i + 1) if cap(ts) != i+1 { t.Errorf("Expected ts to have a cap of %v", i+1) } ts[0] = 'z' ReturnTypeVarSet(ts) ts = BorrowTypeVarSet(i + 1) for _, v := range ts { if v != def { t.Errorf("Expected reshly borrowed Types to be def") } } } // oob tvs := BorrowTypeVarSet(10) if cap(tvs) != 10 { t.Error("Expected a cap of 10") } } func TestFnTypeOol(t *testing.T) { f := borrowFnType() f.a = NewFnType(proton, electron) f.b = NewFnType(proton, neutron) ReturnFnType(f) f = borrowFnType() if f.a != nil { t.Error("FunctionType not cleaned up: a is not nil") } if f.b != nil { t.Error("FunctionType not cleaned up: b is not nil") } } hm-1.0.0/release.go000066400000000000000000000002671315745414600141020ustar00rootroot00000000000000// +build !debug package hm func enterLoggingContext() {} func leaveLoggingContext() {} func logf(format string, others ...interface{}) {} hm-1.0.0/scheme.go000066400000000000000000000034261315745414600137260ustar00rootroot00000000000000package hm import "fmt" // 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. type Scheme struct { tvs TypeVarSet t Type } func NewScheme(tvs TypeVarSet, t Type) *Scheme { return &Scheme{ tvs: tvs, t: t, } } func (s *Scheme) Apply(sub Subs) Substitutable { logf("s: %v, sub: %v", s, sub) if sub == nil { return s } sub = sub.Clone() defer ReturnSubs(sub) for _, tv := range s.tvs { sub = sub.Remove(tv) } s.t = s.t.Apply(sub).(Type) return s } func (s *Scheme) FreeTypeVar() TypeVarSet { ftvs := s.t.FreeTypeVar() tvs := s.tvs.Set() return ftvs.Difference(tvs) } func (s *Scheme) Clone() *Scheme { tvs := make(TypeVarSet, len(s.tvs)) for i, v := range s.tvs { tvs[i] = v } return &Scheme{ tvs: tvs, t: s.t, } } func (s *Scheme) Format(state fmt.State, c rune) { state.Write([]byte("∀[")) for i, tv := range s.tvs { if i < len(s.tvs)-1 { fmt.Fprintf(state, "%v, ", tv) } else { fmt.Fprintf(state, "%v", tv) } } fmt.Fprintf(state, "]: %v", s.t) } // Type returns the type of the scheme, as well as a boolean indicating if *Scheme represents a monotype. If it's a polytype, it'll return false func (s *Scheme) Type() (t Type, isMonoType bool) { if len(s.tvs) == 0 { return s.t, true } return s.t, false } // Normalize normalizes the type variables in a scheme, so all the names will be in alphabetical order func (s *Scheme) Normalize() (err error) { tfv := s.t.FreeTypeVar() if len(tfv) == 0 { return nil } defer ReturnTypeVarSet(tfv) ord := BorrowTypeVarSet(len(tfv)) for i := range tfv { ord[i] = TypeVariable(letters[i]) } s.t, err = s.t.Normalize(tfv, ord) s.tvs = ord.Set() return } hm-1.0.0/scheme_test.go000066400000000000000000000026651315745414600147710ustar00rootroot00000000000000package hm import ( "fmt" "testing" ) func TestSchemeBasics(t *testing.T) { s := new(Scheme) s.tvs = TypeVarSet{'a', 'b'} s.t = NewFnType(TypeVariable('c'), proton) sub := mSubs{ 'a': proton, 'b': neutron, 'c': electron, } s2 := s.Apply(nil).(*Scheme) if s2 != s { t.Errorf("Different pointers") } s2 = s.Apply(sub).(*Scheme) if s2 != s { t.Errorf("Different pointers") } if !s.tvs.Equals(TypeVarSet{'a', 'b'}) { t.Error("TypeVarSet mutated") } if !s.t.Eq(NewFnType(electron, proton)) { t.Error("Application failed") } s = new(Scheme) s.tvs = TypeVarSet{'a', 'b'} s.t = NewFnType(TypeVariable('c'), proton) ftv := s.FreeTypeVar() if !ftv.Equals(TypeVarSet{'c'}) { t.Errorf("Expected ftv: {'c'}. Got %v instead", ftv) } // format if fmt.Sprintf("%v", s) != "∀[a, b]: c → proton" { t.Errorf("Scheme format is wrong.: Got %q", fmt.Sprintf("%v", s)) } // Polytype scheme.Type T, isMono := s.Type() if isMono { t.Errorf("%v is supposed to be a polytype. It shouldn't return true", s) } if !T.Eq(NewFnType(TypeVariable('c'), proton)) { t.Error("Wrong type returned by scheme") } } func TestSchemeNormalize(t *testing.T) { s := new(Scheme) s.tvs = TypeVarSet{'c', 'z', 'd'} s.t = NewFnType(TypeVariable('a'), TypeVariable('c')) err := s.Normalize() if err != nil { t.Error(err) } if !s.tvs.Equals(TypeVarSet{'a', 'b'}) { t.Errorf("Expected: TypeVarSet{'a','b'}. Got: %v", s.tvs) } } hm-1.0.0/solver.go000066400000000000000000000010671315745414600137730ustar00rootroot00000000000000package hm type solver struct { sub Subs err error } func newSolver() *solver { return new(solver) } func (s *solver) solve(cs Constraints) { logf("solving constraints: %d", len(cs)) enterLoggingContext() defer leaveLoggingContext() logf("starting sub %v", s.sub) if s.err != nil { return } switch len(cs) { case 0: return default: var sub Subs c := cs[0] sub, s.err = Unify(c.a, c.b) defer ReturnSubs(s.sub) s.sub = compose(sub, s.sub) cs = cs[1:].Apply(s.sub).(Constraints) s.solve(cs) } logf("Ending Sub %v", s.sub) return } hm-1.0.0/solver_test.go000066400000000000000000000027271315745414600150360ustar00rootroot00000000000000package hm import "testing" var solverTest = []struct { cs Constraints expected Subs err bool }{ {Constraints{{TypeVariable('a'), proton}}, mSubs{'a': proton}, false}, {Constraints{{NewFnType(TypeVariable('a'), proton), neutron}}, nil, true}, {Constraints{{NewFnType(TypeVariable('a'), proton), NewFnType(proton, proton)}}, mSubs{'a': proton}, false}, {Constraints{ { NewFnType(TypeVariable('a'), TypeVariable('a'), list{TypeVariable('a')}), NewFnType(proton, proton, TypeVariable('b')), }, }, mSubs{'a': proton, 'b': list{proton}}, false, }, { Constraints{ {TypeVariable('a'), TypeVariable('b')}, {TypeVariable('a'), proton}, }, mSubs{'a': proton}, false, }, { Constraints{ { NewRecordType("", TypeVariable('a'), TypeVariable('a'), TypeVariable('b')), NewRecordType("", neutron, neutron, proton), }, }, mSubs{'a': neutron, 'b': proton}, false, }, } func TestSolver(t *testing.T) { for i, sts := range solverTest { solver := newSolver() solver.solve(sts.cs) if sts.err { if solver.err == nil { t.Errorf("Test %d Expected an error", i) } continue } else if solver.err != nil { t.Error(solver.err) } for _, v := range sts.expected.Iter() { if T, ok := solver.sub.Get(v.Tv); !ok { t.Errorf("Test %d: Expected type variable %v in subs: %v", i, v.Tv, solver.sub) break } else if T != v.T { t.Errorf("Test %d: Expected replacement to be %v. Got %v instead", i, v.T, T) } } } } hm-1.0.0/substitutables.go000066400000000000000000000025741315745414600155420ustar00rootroot00000000000000package hm import "fmt" // Constraints is a slice of Constraint. Like a Constraint, it is also a Substitutable type Constraints []Constraint func (cs Constraints) Apply(sub Subs) Substitutable { // an optimization if sub == nil { return cs } if len(cs) == 0 { return cs } logf("Constraints: %d", len(cs)) logf("Applying %v to %v", sub, cs) for i, c := range cs { cs[i] = c.Apply(sub).(Constraint) } logf("Constraints %v", cs) return cs } func (cs Constraints) FreeTypeVar() TypeVarSet { var retVal TypeVarSet for _, v := range cs { retVal = v.FreeTypeVar().Union(retVal) } return retVal } func (cs Constraints) Format(state fmt.State, c rune) { state.Write([]byte("Constraints[")) for i, c := range cs { if i < len(cs)-1 { fmt.Fprintf(state, "%v, ", c) } else { fmt.Fprintf(state, "%v", c) } } state.Write([]byte{']'}) } // Types is a slice of Type. Future additions to the methods of this type may be possible type Types []Type func (ts Types) Contains(t Type) bool { for _, T := range ts { if t.Eq(T) { return true } } return false } // func (ts Types) Apply(sub Subs) Substitutable { // for i, t := range ts { // ts[i] = t.Apply(sub).(Type) // } // return ts // } // func (ts Types) FreeTypeVar() TypeVarSet { // var retVal TypeVarSet // for _, v := range ts { // retVal = v.FreeTypeVar().Union(retVal) // } // return retVal // } hm-1.0.0/substitutables_test.go000066400000000000000000000021321315745414600165670ustar00rootroot00000000000000package hm import ( "fmt" "testing" ) func TestConstraints(t *testing.T) { cs := Constraints{ {TypeVariable('a'), proton}, {TypeVariable('b'), proton}, } correct := TypeVarSet{'a', 'b'} ftv := cs.FreeTypeVar() for _, v := range correct { if !ftv.Contains(v) { t.Errorf("Expected free type vars to contain %v", v) break } } sub := mSubs{ 'a': neutron, } cs = cs.Apply(sub).(Constraints) if cs[0].a != neutron { t.Error("Expected neutron") } if cs[0].b != proton { t.Error("Expected proton") } if cs[1].a != TypeVariable('b') { t.Error("There was nothing to substitute b with") } if cs[1].b != proton { t.Error("Expected proton") } if fmt.Sprintf("%v", cs) != "Constraints[{neutron = proton}, {b = proton}]" { t.Errorf("Error in formatting cs") } } func TestTypes_Contains(t *testing.T) { ts := Types{TypeVariable('a'), proton} if !ts.Contains(TypeVariable('a')) { t.Error("Expected ts to contain 'a'") } if !ts.Contains(proton) { t.Error("Expected ts to contain proton") } if ts.Contains(neutron) { t.Error("ts shouldn't contain neutron") } } hm-1.0.0/substitutions.go000066400000000000000000000052551315745414600154230ustar00rootroot00000000000000package hm import "fmt" // Subs is a list of substitution. Internally there are two very basic substitutions - one backed by map and the other a normal slice type Subs interface { Get(TypeVariable) (Type, bool) Add(TypeVariable, Type) Subs Remove(TypeVariable) Subs // Iter() <-chan Substitution Iter() []Substitution Size() int Clone() Subs } // A Substitution is a tuple representing the TypeVariable and the replacement Type type Substitution struct { Tv TypeVariable T Type } type sSubs struct { s []Substitution } func newSliceSubs(maybeSize ...int) *sSubs { var size int if len(maybeSize) > 0 && maybeSize[0] > 0 { size = maybeSize[0] } retVal := BorrowSSubs(size) retVal.s = retVal.s[:0] return retVal } func (s *sSubs) Get(tv TypeVariable) (Type, bool) { if i := s.index(tv); i >= 0 { return s.s[i].T, true } return nil, false } func (s *sSubs) Add(tv TypeVariable, t Type) Subs { if i := s.index(tv); i >= 0 { s.s[i].T = t return s } s.s = append(s.s, Substitution{tv, t}) return s } func (s *sSubs) Remove(tv TypeVariable) Subs { if i := s.index(tv); i >= 0 { // for now we keep the order copy(s.s[i:], s.s[i+1:]) s.s[len(s.s)-1].T = nil s.s = s.s[:len(s.s)-1] } return s } func (s *sSubs) Iter() []Substitution { return s.s } func (s *sSubs) Size() int { return len(s.s) } func (s *sSubs) Clone() Subs { retVal := BorrowSSubs(len(s.s)) copy(retVal.s, s.s) return retVal } func (s *sSubs) index(tv TypeVariable) int { for i, sub := range s.s { if sub.Tv == tv { return i } } return -1 } func (s *sSubs) Format(state fmt.State, c rune) { state.Write([]byte{'{'}) for i, v := range s.s { if i < len(s.s)-1 { fmt.Fprintf(state, "%v: %v, ", v.Tv, v.T) } else { fmt.Fprintf(state, "%v: %v", v.Tv, v.T) } } state.Write([]byte{'}'}) } type mSubs map[TypeVariable]Type func (s mSubs) Get(tv TypeVariable) (Type, bool) { retVal, ok := s[tv]; return retVal, ok } func (s mSubs) Add(tv TypeVariable, t Type) Subs { s[tv] = t; return s } func (s mSubs) Remove(tv TypeVariable) Subs { delete(s, tv); return s } func (s mSubs) Iter() []Substitution { retVal := make([]Substitution, len(s)) var i int for k, v := range s { retVal[i] = Substitution{k, v} i++ } return retVal } func (s mSubs) Size() int { return len(s) } func (s mSubs) Clone() Subs { retVal := make(mSubs) for k, v := range s { retVal[k] = v } return retVal } func compose(a, b Subs) (retVal Subs) { if b == nil { return a } retVal = b.Clone() if a == nil { return } for _, v := range a.Iter() { retVal = retVal.Add(v.Tv, v.T) } for _, v := range retVal.Iter() { retVal = retVal.Add(v.Tv, v.T.Apply(a).(Type)) } return retVal } hm-1.0.0/substitutions_test.go000066400000000000000000000065141315745414600164610ustar00rootroot00000000000000package hm import ( "fmt" "testing" ) var subsTests = []struct { op string tv TypeVariable t Type ok bool size int }{ {"get", TypeVariable('a'), nil, false, 0}, {"add", TypeVariable('a'), proton, true, 1}, {"get", TypeVariable('a'), proton, true, 1}, {"add", TypeVariable('a'), neutron, true, 1}, {"get", TypeVariable('a'), neutron, true, 1}, {"rem", TypeVariable('b'), nil, false, 1}, {"rem", TypeVariable('a'), nil, false, 0}, {"add", TypeVariable('a'), proton, true, 1}, {"add", TypeVariable('b'), proton, true, 2}, {"add", TypeVariable('c'), proton, true, 3}, } func testSubs(t *testing.T, sub Subs) { var T Type var ok bool for _, sts := range subsTests { switch sts.op { case "get": if T, ok = sub.Get(sts.tv); ok != sts.ok { t.Errorf("Expected Get to return %t. Got a value of %v instead", sts.ok, T) } case "add": sub = sub.Add(sts.tv, sts.t) case "rem": sub = sub.Remove(sts.tv) } if sub.Size() != sts.size { t.Errorf("Inconsistent size. Want %d. Got %d", sts.size, sub.Size()) } } // Iter correct := []Substitution{ {TypeVariable('a'), proton}, {TypeVariable('b'), proton}, {TypeVariable('c'), proton}, } for _, s := range sub.Iter() { var found bool for _, c := range correct { if s.T == c.T && s.Tv == c.Tv { found = true break } } if !found { t.Errorf("Testing of %T: cannot find %v in Range", sub, s) } } // Clone cloned := sub.Clone() cloned = cloned.Add(TypeVariable('a'), photon) gt, ok := sub.Get(TypeVariable('a')) if !ok { t.Errorf("Expected the key 'a' to be found") } if gt == photon { t.Errorf("Mutable cloning found") } } func TestSliceSubs(t *testing.T) { var sub Subs sub = newSliceSubs() if sub.Size() != 0 { t.Error("Expected a size of 0") } sub = newSliceSubs(5) if cap(sub.(*sSubs).s) != 5 { t.Error("Expected a cap of 5") } if sub.Size() != 0 { t.Error("Expected a size of 0") } testSubs(t, sub) // Format for completeness sake sub = newSliceSubs(2) sub = sub.Add('a', proton) sub = sub.Add('b', neutron) if fmt.Sprintf("%v", sub) != "{a: proton, b: neutron}" { t.Errorf("Format of sub is wrong. Got %q instead", sub) } } func TestMapSubs(t *testing.T) { var sub Subs sub = make(mSubs) if sub.Size() != 0 { t.Error("Expected a size of 0") } testSubs(t, sub) } var composeTests = []struct { a Subs b Subs expected Subs }{ {mSubs{'a': proton}, &sSubs{[]Substitution{{'b', neutron}}}, &sSubs{[]Substitution{{'a', proton}, {'b', neutron}}}}, {&sSubs{[]Substitution{{'b', neutron}}}, mSubs{'a': proton}, mSubs{'a': proton, 'b': neutron}}, {mSubs{'a': proton, 'b': neutron}, &sSubs{[]Substitution{{'b', neutron}}}, &sSubs{[]Substitution{{'a', proton}, {'b', neutron}}}}, {mSubs{'a': proton, 'b': TypeVariable('a')}, &sSubs{[]Substitution{{'b', neutron}}}, &sSubs{[]Substitution{{'a', proton}, {'b', proton}}}}, {mSubs{'a': proton}, &sSubs{[]Substitution{{'b', TypeVariable('a')}}}, &sSubs{[]Substitution{{'a', proton}, {'b', proton}}}}, } func TestCompose(t *testing.T) { for i, cts := range composeTests { subs := compose(cts.a, cts.b) for _, v := range cts.expected.Iter() { if T, ok := subs.Get(v.Tv); !ok { t.Errorf("Test %d: Expected TypeVariable %v to be in subs", i, v.Tv) } else if T != v.T { t.Errorf("Test %d: Expected replacement to be %v. Got %v instead", i, v.T, T) } } } } hm-1.0.0/test_test.go000066400000000000000000000046541315745414600145040ustar00rootroot00000000000000package hm import ( "fmt" "github.com/pkg/errors" ) const ( proton TypeConst = "proton" neutron TypeConst = "neutron" quark TypeConst = "quark" electron TypeConst = "electron" positron TypeConst = "positron" muon TypeConst = "muon" photon TypeConst = "photon" higgs TypeConst = "higgs" ) type list struct { t Type } func (l list) Name() string { return "List" } func (l list) Apply(subs Subs) Substitutable { l.t = l.t.Apply(subs).(Type); return l } func (l list) FreeTypeVar() TypeVarSet { return l.t.FreeTypeVar() } func (l list) Format(s fmt.State, c rune) { fmt.Fprintf(s, "List %v", l.t) } func (l list) String() string { return fmt.Sprintf("%v", l) } func (l list) Normalize(k, v TypeVarSet) (Type, error) { var t Type var err error if t, err = l.t.Normalize(k, v); err != nil { return nil, err } l.t = t return l, nil } func (l list) Types() Types { return Types{l.t} } func (l list) Eq(other Type) bool { if ot, ok := other.(list); ok { return ot.t.Eq(l.t) } return false } type mirrorUniverseList struct { t Type } func (l mirrorUniverseList) Name() string { return "GoateeList" } func (l mirrorUniverseList) Apply(subs Subs) Substitutable { l.t = l.t.Apply(subs).(Type); return l } func (l mirrorUniverseList) FreeTypeVar() TypeVarSet { return l.t.FreeTypeVar() } func (l mirrorUniverseList) Format(s fmt.State, c rune) { fmt.Fprintf(s, "List %v", l.t) } func (l mirrorUniverseList) String() string { return fmt.Sprintf("%v", l) } func (l mirrorUniverseList) Normalize(k, v TypeVarSet) (Type, error) { var t Type var err error if t, err = l.t.Normalize(k, v); err != nil { return nil, err } l.t = t return l, nil } func (l mirrorUniverseList) Types() Types { return Types{l.t} } func (l mirrorUniverseList) Eq(other Type) bool { if ot, ok := other.(list); ok { return ot.t.Eq(l.t) } return false } // satisfies the Inferer interface for testing type selfInferer bool func (t selfInferer) Infer(Env, Fresher) (Type, error) { if bool(t) { return proton, nil } return nil, errors.Errorf("fail") } func (t selfInferer) Body() Expression { panic("not implemented") } // satisfies the Var interface for testing. It also doesn't know its own type type variable string func (t variable) Body() Expression { return nil } func (t variable) Name() string { return string(t) } func (t variable) Type() Type { return nil } hm-1.0.0/type.go000066400000000000000000000065321315745414600134440ustar00rootroot00000000000000package hm import ( "fmt" ) // Type represents all the possible type constructors. 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 } // 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 Substitutable interface { Apply(Subs) Substitutable FreeTypeVar() TypeVarSet } // TypeConst are the default implementation of a constant type. Feel free to implement your own. TypeConsts should be immutable (so no pointer types plz) type TypeConst string func (t TypeConst) Name() string { return string(t) } func (t TypeConst) Apply(Subs) Substitutable { return t } func (t TypeConst) FreeTypeVar() TypeVarSet { return nil } func (t TypeConst) Normalize(k, v TypeVarSet) (Type, error) { return t, nil } func (t TypeConst) Types() Types { return nil } func (t TypeConst) String() string { return string(t) } func (t TypeConst) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%s", string(t)) } func (t TypeConst) Eq(other Type) bool { return other == t } // Record is a basic record/tuple type. It takes an optional name. type Record struct { ts []Type name string } // NewRecordType creates a new Record Type func NewRecordType(name string, ts ...Type) *Record { return &Record{ ts: ts, name: name, } } func (t *Record) Apply(subs Subs) Substitutable { ts := make([]Type, len(t.ts)) for i, v := range t.ts { ts[i] = v.Apply(subs).(Type) } return NewRecordType(t.name, ts...) } func (t *Record) FreeTypeVar() TypeVarSet { var tvs TypeVarSet for _, v := range t.ts { tvs = v.FreeTypeVar().Union(tvs) } return tvs } func (t *Record) Name() string { if t.name != "" { return t.name } return t.String() } func (t *Record) Normalize(k, v TypeVarSet) (Type, error) { ts := make([]Type, len(t.ts)) var err error for i, tt := range t.ts { if ts[i], err = tt.Normalize(k, v); err != nil { return nil, err } } return NewRecordType(t.name, ts...), nil } func (t *Record) Types() Types { ts := BorrowTypes(len(t.ts)) copy(ts, t.ts) return ts } func (t *Record) Eq(other Type) bool { if ot, ok := other.(*Record); ok { if len(ot.ts) != len(t.ts) { return false } for i, v := range t.ts { if !v.Eq(ot.ts[i]) { return false } } return true } return false } func (t *Record) Format(f fmt.State, c rune) { f.Write([]byte("(")) for i, v := range t.ts { if i < len(t.ts)-1 { fmt.Fprintf(f, "%v, ", v) } else { fmt.Fprintf(f, "%v)", v) } } } func (t *Record) String() string { return fmt.Sprintf("%v", t) } // Clone implements Cloner func (t *Record) Clone() interface{} { retVal := new(Record) ts := BorrowTypes(len(t.ts)) for i, tt := range t.ts { if c, ok := tt.(Cloner); ok { ts[i] = c.Clone().(Type) } else { ts[i] = tt } } retVal.ts = ts retVal.name = t.name return retVal } hm-1.0.0/typeVarSet.go000066400000000000000000000030631315745414600145650ustar00rootroot00000000000000package hm import ( "sort" "github.com/xtgo/set" ) // TypeVarSet is a set of TypeVariable type TypeVarSet []TypeVariable // TypeVariables are orderable, so we fulfil the interface for sort.Interface func (s TypeVarSet) Len() int { return len(s) } func (s TypeVarSet) Less(i, j int) bool { return s[i] < s[j] } func (s TypeVarSet) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s TypeVarSet) Set() TypeVarSet { sort.Sort(s) n := set.Uniq(s) s = s[:n] return s } func (s TypeVarSet) Union(other TypeVarSet) TypeVarSet { if other == nil { return s } sort.Sort(s) sort.Sort(other) s2 := append(s, other...) n := set.Union(s2, len(s)) return s2[:n] } func (s TypeVarSet) Intersect(other TypeVarSet) TypeVarSet { if len(s) == 0 || len(other) == 0 { return nil } sort.Sort(s) sort.Sort(other) s2 := append(s, other...) n := set.Inter(s2, len(s)) return s2[:n] } func (s TypeVarSet) Difference(other TypeVarSet) TypeVarSet { sort.Sort(s) sort.Sort(other) s2 := append(s, other...) n := set.Diff(s2, len(s)) return s2[:n] } func (s TypeVarSet) Contains(tv TypeVariable) bool { for _, v := range s { if v == tv { return true } } return false } func (s TypeVarSet) Index(tv TypeVariable) int { for i, v := range s { if v == tv { return i } } return -1 } func (s TypeVarSet) Equals(other TypeVarSet) bool { if len(s) != len(other) { return false } if len(s) == 0 { return true } if &s[0] == &other[0] { return true } for _, v := range s { if !other.Contains(v) { return false } } return true } hm-1.0.0/typeVarSet_test.go000066400000000000000000000044671315745414600156350ustar00rootroot00000000000000package hm import "testing" var tvSetTests = []struct { op string tvs0 TypeVarSet tvs1 TypeVarSet expected TypeVarSet ind int eq bool }{ {"set", TypeVarSet{'a', 'a', 'a'}, nil, TypeVarSet{'a'}, 0, false}, {"set", TypeVarSet{'c', 'b', 'a'}, nil, TypeVarSet{'a', 'b', 'c'}, 0, false}, {"intersect", TypeVarSet{'a', 'b', 'c'}, TypeVarSet{'d', 'e', 'f'}, TypeVarSet{}, -1, false}, {"intersect", TypeVarSet{'a', 'b', 'c'}, TypeVarSet{'b', 'c', 'd'}, TypeVarSet{'b', 'c'}, -1, false}, {"intersect", TypeVarSet{'a', 'b', 'c'}, nil, nil, -1, false}, {"intersect", TypeVarSet{'a', 'b', 'c'}, TypeVarSet{'c', 'b', 'a'}, TypeVarSet{'a', 'b', 'c'}, 0, true}, {"union", TypeVarSet{'a', 'b'}, TypeVarSet{'c', 'd'}, TypeVarSet{'a', 'b', 'c', 'd'}, 0, false}, {"union", TypeVarSet{'a', 'c', 'b'}, TypeVarSet{'c', 'd'}, TypeVarSet{'a', 'b', 'c', 'd'}, 0, false}, {"union", TypeVarSet{'a', 'b'}, nil, TypeVarSet{'a', 'b'}, 0, false}, {"diff", TypeVarSet{'a', 'b', 'c'}, TypeVarSet{'d', 'e', 'c'}, TypeVarSet{'a', 'b'}, 0, false}, {"diff", TypeVarSet{'a', 'b', 'c'}, TypeVarSet{'c', 'd', 'e'}, TypeVarSet{'a', 'b'}, 0, false}, {"diff", TypeVarSet{'a', 'b', 'c'}, TypeVarSet{'d', 'e', 'f'}, TypeVarSet{'a', 'b', 'c'}, 0, false}, } func TestTypeVarSet(t *testing.T) { for i, tst := range tvSetTests { var s TypeVarSet switch tst.op { case "set": s = tst.tvs0.Set() if !s.Equals(tst.expected) { t.Errorf("%s op (%d): expected: %v, got %v", tst.op, i, tst.expected, s) } case "intersect": s = tst.tvs0.Intersect(tst.tvs1) if !s.Equals(tst.expected) { t.Errorf("%s op (%d): expected: %v, got %v", tst.op, i, tst.expected, s) } case "union": s = tst.tvs0.Union(tst.tvs1) if !s.Equals(tst.expected) { t.Errorf("%s op (%d): expected: %v, got %v", tst.op, i, tst.expected, s) } case "diff": s = tst.tvs0.Difference(tst.tvs1) if !s.Equals(tst.expected) { t.Errorf("%s op (%d): expected: %v, got %v", tst.op, i, tst.expected, s) } } if ind := s.Index('a'); ind != tst.ind { t.Errorf("%s op %d index : expected %d got %v", tst.op, i, tst.ind, ind) } if eq := tst.tvs0.Equals(tst.tvs1); eq != tst.eq { t.Errorf("%s op %d eq: expected %t got %v", tst.op, i, tst.eq, eq) } } tvs := TypeVarSet{'a'} if !tvs.Equals(tvs) { t.Error("A set should be equal to itself") } } hm-1.0.0/typeVariable.go000066400000000000000000000017111315745414600151040ustar00rootroot00000000000000package hm import ( "fmt" "github.com/pkg/errors" ) // TypeVariable is a variable that ranges over the types - that is to say it can take any type. type TypeVariable rune func (t TypeVariable) Name() string { return string(t) } func (t TypeVariable) Apply(sub Subs) Substitutable { if sub == nil { return t } if retVal, ok := sub.Get(t); ok { return retVal } return t } func (t TypeVariable) FreeTypeVar() TypeVarSet { tvs := BorrowTypeVarSet(1); tvs[0] = t; return tvs } func (t TypeVariable) Normalize(k, v TypeVarSet) (Type, error) { if i := k.Index(t); i >= 0 { return v[i], nil } return nil, errors.Errorf("Type Variable %v not in signature", t) } func (t TypeVariable) Types() Types { return nil } func (t TypeVariable) String() string { return string(t) } func (t TypeVariable) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%c", rune(t)) } func (t TypeVariable) Eq(other Type) bool { return other == t } hm-1.0.0/typeVariable_test.go000066400000000000000000000031271315745414600161460ustar00rootroot00000000000000package hm import ( "fmt" "testing" ) func TestTypeVariableBasics(t *testing.T) { tv := TypeVariable('a') if name := tv.Name(); name != "a" { t.Errorf("Expected name to be \"a\". Got %q instead", name) } if str := tv.String(); str != "a" { t.Errorf("Expected String() of 'a'. Got %q instead", str) } if tv.Types() != nil { t.Errorf("Expected Types() of TypeVariable to be nil") } ftv := tv.FreeTypeVar() if len(ftv) != 1 { t.Errorf("Expected a type variable to be free when FreeTypeVar() is called") } if ftv[0] != tv { t.Errorf("Expected ...") } sub := mSubs{ 'a': proton, } if tv.Apply(sub) != proton { t.Error("Expected proton") } sub = mSubs{ 'b': proton, } if tv.Apply(sub) != tv { t.Error("Expected unchanged") } } func TestTypeVariableNormalize(t *testing.T) { original := TypeVarSet{'c', 'a', 'd'} normalized := TypeVarSet{'a', 'b', 'c'} tv := TypeVariable('a') norm, err := tv.Normalize(original, normalized) if err != nil { t.Error(err) } if norm != TypeVariable('b') { t.Errorf("Expected 'b'. Got %v", norm) } tv = TypeVariable('e') if _, err = tv.Normalize(original, normalized); err == nil { t.Error("Expected an error") } } func TestTypeConst(t *testing.T) { T := proton if T.Name() != "proton" { t.Error("Expected name to be proton") } if fmt.Sprintf("%v", T) != "proton" { t.Error("Expected name to be proton") } if T.String() != "proton" { t.Error("Expected name to be proton") } if T2, err := T.Normalize(nil, nil); err != nil { t.Error(err) } else if T2 != T { t.Error("Const types should return itself") } }