From 374f355004a539ffc96afb209645f731f8844cf0 Mon Sep 17 00:00:00 2001 From: chewxy Date: Wed, 27 Dec 2017 19:17:08 +1100 Subject: [PATCH 01/21] Added function to create Subs --- substitutions.go | 7 +++++++ type.go | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/substitutions.go b/substitutions.go index a21dd11..546d052 100644 --- a/substitutions.go +++ b/substitutions.go @@ -14,6 +14,13 @@ type Subs interface { Clone() Subs } +func MakeSubs(n int) Subs { + if n >= 30 { + return make(mSubs) + } + return newSliceSubs(n) +} + // A Substitution is a tuple representing the TypeVariable and the replacement Type type Substitution struct { Tv TypeVariable diff --git a/type.go b/type.go index 6d2a1bc..384687e 100644 --- a/type.go +++ b/type.go @@ -7,10 +7,10 @@ import ( // 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 + Name() string // Name is the name of the constructor + Normalize(k TypeVarSet, v 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 From dfec7d229fecd503dc3ec0fc31dba08786a4b924 Mon Sep 17 00:00:00 2001 From: chewxy Date: Wed, 27 Dec 2017 19:17:35 +1100 Subject: [PATCH 02/21] Added a built-in types package, basically refactoring out bits and pieces of the parent package --- types/commonutils.go | 36 ++++++++++++ types/function.go | 121 +++++++++++++++++++++++++++++++++++++++++ types/function_test.go | 99 +++++++++++++++++++++++++++++++++ types/interfaces.go | 5 ++ types/perf.go | 31 +++++++++++ types/perf_test.go | 19 +++++++ types/record.go | 107 ++++++++++++++++++++++++++++++++++++ types/test_test.go | 42 ++++++++++++++ 8 files changed, 460 insertions(+) create mode 100644 types/commonutils.go create mode 100644 types/function.go create mode 100644 types/function_test.go create mode 100644 types/interfaces.go create mode 100644 types/perf.go create mode 100644 types/perf_test.go create mode 100644 types/record.go create mode 100644 types/test_test.go diff --git a/types/commonutils.go b/types/commonutils.go new file mode 100644 index 0000000..87802a1 --- /dev/null +++ b/types/commonutils.go @@ -0,0 +1,36 @@ +package hmtypes + +import "github.com/chewxy/hm" + +// Pair is a convenient structural abstraction for types that are composed of two types. +// Depending on use cases, it may be useful to embed Pair, or define a new type base on *Pair. +// +// Pair partially implements hm.Substitutable, but with very specific semantics - +// it's useful for a small subset of types like function types, or supertypes. +// See the documentation for Apply and FreeTypeVar. +type Pair struct { + A, B hm.Type +} + +// Apply applies a substitution on both the first and second types of the Pair. +func (t *Pair) Apply(sub hm.Subs) { + t.A = t.A.Apply(sub).(hm.Type) + t.B = t.B.Apply(sub).(hm.Type) +} + +// FreeTypeVar returns a set of free (unbound) type variables. +func (t Pair) FreeTypeVar() hm.TypeVarSet { return t.A.FreeTypeVar().Union(t.B.FreeTypeVar()) } + +// Monuple is a convenient structural abstraction for types that are composed of one type. +// +// Monuple implements hm.Substitutable, but with very specific semantics - +// It's useful for singly polymorphic types like arrays, linear types, reference types, etc +type Monuple struct { + A hm.Type +} + +// Apply applies a substitution to the monuple type. +func (t Monuple) Apply(subs hm.Subs) hm.Substitutable { t.A = t.A.Apply(subs).(hm.Type); return t } + +// FreeTypeVar returns the set of free type variables in the monuple. +func (t Monuple) FreeTypeVar() hm.TypeVarSet { return t.A.FreeTypeVar() } diff --git a/types/function.go b/types/function.go new file mode 100644 index 0000000..6dc0ec2 --- /dev/null +++ b/types/function.go @@ -0,0 +1,121 @@ +package hmtypes + +import ( + "fmt" + + "github.com/chewxy/hm" +) + +// FunctionType is a type constructor that builds function types. +type FunctionType Pair + +// 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 ...hm.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 hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } +func (t *FunctionType) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).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 hm.TypeVarSet) (hm.Type, error) { + var a, b hm.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() hm.Types { + retVal := hm.BorrowTypes(2) + retVal[0] = t.A + retVal[1] = t.B + return retVal +} + +func (t *FunctionType) Eq(other hm.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() hm.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) hm.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() hm.Types { + retVal := hm.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...) + hm.ReturnTypes(ft) + } else { + retVal = append(retVal, t.A) + } + + if b, ok := t.B.(*FunctionType); ok { + ft := b.FlatTypes() + retVal = append(retVal, ft...) + hm.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().(hm.Type) + } else { + retVal.A = t.A + } + + if bc, ok := t.B.(Cloner); ok { + retVal.B = bc.Clone().(hm.Type) + } else { + retVal.B = t.B + } + return retVal +} diff --git a/types/function_test.go b/types/function_test.go new file mode 100644 index 0000000..93acb89 --- /dev/null +++ b/types/function_test.go @@ -0,0 +1,99 @@ +package hmtypes + +import ( + "testing" + + "github.com/chewxy/hm" + "github.com/stretchr/testify/assert" +) + +func TestFunctionTypeBasics(t *testing.T) { + fnType := NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a'), hm.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(hm.TypeVariable('a')) { + t.Error("Expected arg of function to be 'a'") + } + + if !fnType.Ret(false).Eq(NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a'))) { + t.Error("Expected ret(false) to be a → a") + } + + if !fnType.Ret(true).Eq(hm.TypeVariable('a')) { + t.Error("Expected final return type to be 'a'") + } + + // a very simple fn + fnType = NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a')) + if !fnType.Ret(true).Eq(hm.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(hm.TypeVariable('a')) + } + assert.Panics(t, f) +} + +var fnApplyTests = []struct { + fn *FunctionType + sub hm.Subs + + expected *FunctionType +}{ + {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, proton)}, + {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, neutron)}, + {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'c': proton, 'd': neutron}, NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'))}, + {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'a': proton, 'c': neutron}, NewFnType(proton, hm.TypeVariable('b'))}, + {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'c': proton, 'b': neutron}, NewFnType(hm.TypeVariable('a'), neutron)}, + {NewFnType(electron, proton), mSubs{'a': proton, 'b': neutron}, NewFnType(electron, proton)}, + + // a -> (b -> c) + {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, neutron, proton)}, + {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, proton, neutron)}, + {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, neutron, hm.TypeVariable('c'))}, + {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('c'), hm.TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, hm.TypeVariable('c'), neutron)}, + + // (a -> b) -> c + {NewFnType(NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), hm.TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFnType(NewFnType(proton, neutron), proton)}, +} + +func TestFunctionType_FlatTypes(t *testing.T) { + fnType := NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')) + ts := fnType.FlatTypes() + correct := hm.Types{hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')} + assert.Equal(t, ts, correct) + + fnType2 := NewFnType(fnType, hm.TypeVariable('d')) + correct = append(correct, hm.TypeVariable('d')) + ts = fnType2.FlatTypes() + assert.Equal(t, ts, correct) +} + +func TestFunctionType_Clone(t *testing.T) { + fnType := NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')) + assert.Equal(t, fnType.Clone(), fnType) + + rec := NewRecordType("", hm.TypeVariable('a'), NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), hm.TypeVariable('c')) + fnType = NewFnType(rec, rec) + assert.Equal(t, fnType.Clone(), fnType) +} diff --git a/types/interfaces.go b/types/interfaces.go new file mode 100644 index 0000000..b0afe8a --- /dev/null +++ b/types/interfaces.go @@ -0,0 +1,5 @@ +package hmtypes + +type Cloner interface { + Clone() interface{} +} diff --git a/types/perf.go b/types/perf.go new file mode 100644 index 0000000..6b369ed --- /dev/null +++ b/types/perf.go @@ -0,0 +1,31 @@ +package hmtypes + +import ( + "sync" + "unsafe" +) + +var pairPool = &sync.Pool{ + New: func() interface{} { return new(Pair) }, +} + +func borrowFnType() *FunctionType { + got := pairPool.Get().(*Pair) + return (*FunctionType)(unsafe.Pointer(got)) +} + +// 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 + p := (*Pair)(unsafe.Pointer(fnt)) + pairPool.Put(p) +} diff --git a/types/perf_test.go b/types/perf_test.go new file mode 100644 index 0000000..8572a2e --- /dev/null +++ b/types/perf_test.go @@ -0,0 +1,19 @@ +package hmtypes + +import "testing" + +func TestFnTypePool(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") + } + +} diff --git a/types/record.go b/types/record.go new file mode 100644 index 0000000..6e19ab0 --- /dev/null +++ b/types/record.go @@ -0,0 +1,107 @@ +package hmtypes + +import ( + "fmt" + + "github.com/chewxy/hm" +) + +// Record is a basic record/tuple type. It takes an optional name. +type Record struct { + ts []hm.Type + name string +} + +// NewRecordType creates a new Record hm.Type +func NewRecordType(name string, ts ...hm.Type) *Record { + return &Record{ + ts: ts, + name: name, + } +} + +func (t *Record) Apply(subs hm.Subs) hm.Substitutable { + ts := make([]hm.Type, len(t.ts)) + for i, v := range t.ts { + ts[i] = v.Apply(subs).(hm.Type) + } + return NewRecordType(t.name, ts...) +} + +func (t *Record) FreeTypeVar() hm.TypeVarSet { + var tvs hm.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 hm.TypeVarSet) (hm.Type, error) { + ts := make([]hm.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() hm.Types { + ts := hm.BorrowTypes(len(t.ts)) + copy(ts, t.ts) + return ts +} + +func (t *Record) Eq(other hm.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 := hm.BorrowTypes(len(t.ts)) + for i, tt := range t.ts { + if c, ok := tt.(Cloner); ok { + ts[i] = c.Clone().(hm.Type) + } else { + ts[i] = tt + } + } + retVal.ts = ts + retVal.name = t.name + + return retVal +} diff --git a/types/test_test.go b/types/test_test.go new file mode 100644 index 0000000..c3a67ba --- /dev/null +++ b/types/test_test.go @@ -0,0 +1,42 @@ +package hmtypes + +import "github.com/chewxy/hm" + +const ( + proton hm.TypeConst = "proton" + neutron hm.TypeConst = "neutron" + quark hm.TypeConst = "quark" + + electron hm.TypeConst = "electron" + positron hm.TypeConst = "positron" + muon hm.TypeConst = "muon" + + photon hm.TypeConst = "photon" + higgs hm.TypeConst = "higgs" +) + +// useful copy pasta from the hm package +type mSubs map[hm.TypeVariable]hm.Type + +func (s mSubs) Get(tv hm.TypeVariable) (hm.Type, bool) { retVal, ok := s[tv]; return retVal, ok } +func (s mSubs) Add(tv hm.TypeVariable, t hm.Type) hm.Subs { s[tv] = t; return s } +func (s mSubs) Remove(tv hm.TypeVariable) hm.Subs { delete(s, tv); return s } + +func (s mSubs) Iter() []hm.Substitution { + retVal := make([]hm.Substitution, len(s)) + var i int + for k, v := range s { + retVal[i] = hm.Substitution{k, v} + i++ + } + return retVal +} + +func (s mSubs) Size() int { return len(s) } +func (s mSubs) Clone() hm.Subs { + retVal := make(mSubs) + for k, v := range s { + retVal[k] = v + } + return retVal +} From 6b26671f8cd039db0d04d30d3d57f228eebd0f67 Mon Sep 17 00:00:00 2001 From: chewxy Date: Wed, 27 Dec 2017 19:20:09 +1100 Subject: [PATCH 03/21] Updated variable naming scheme --- typeVariable.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/typeVariable.go b/typeVariable.go index 2d33945..6ea1d4c 100644 --- a/typeVariable.go +++ b/typeVariable.go @@ -9,7 +9,7 @@ import ( // 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) Name() string { return fmt.Sprintf("%v", t) } func (t TypeVariable) Apply(sub Subs) Substitutable { if sub == nil { return t @@ -30,7 +30,13 @@ func (t TypeVariable) Normalize(k, v TypeVarSet) (Type, error) { 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 } +func (t TypeVariable) Types() Types { return nil } +func (t TypeVariable) String() string { return fmt.Sprintf("%v", t) } +func (t TypeVariable) Format(s fmt.State, c rune) { + if t >= 'a' && t <= 'z' { + fmt.Fprintf(s, "%c", t) + return + } + fmt.Fprintf(s, "<%d>", t) +} +func (t TypeVariable) Eq(other Type) bool { return other == t } From f3d66733fceea2488a3ed024ae722bea847ddad5 Mon Sep 17 00:00:00 2001 From: chewxy Date: Wed, 27 Dec 2017 21:00:26 +1100 Subject: [PATCH 04/21] Added Monuples --- typeVariable.go | 4 +-- types/commonutils.go | 52 ++++++++++++++++++++++++++--- types/function.go | 74 +++++++++++++++++------------------------- types/function_test.go | 46 +++++++++++++------------- types/monuples.go | 55 +++++++++++++++++++++++++++++++ types/perf.go | 20 +++++++----- types/perf_test.go | 10 +++--- 7 files changed, 173 insertions(+), 88 deletions(-) create mode 100644 types/monuples.go diff --git a/typeVariable.go b/typeVariable.go index 6ea1d4c..1fc383e 100644 --- a/typeVariable.go +++ b/typeVariable.go @@ -34,9 +34,9 @@ func (t TypeVariable) Types() Types { return nil } func (t TypeVariable) String() string { return fmt.Sprintf("%v", t) } func (t TypeVariable) Format(s fmt.State, c rune) { if t >= 'a' && t <= 'z' { - fmt.Fprintf(s, "%c", t) + fmt.Fprintf(s, "%c", rune(t)) return } - fmt.Fprintf(s, "<%d>", t) + fmt.Fprintf(s, "<%d>", rune(t)) } func (t TypeVariable) Eq(other Type) bool { return other == t } diff --git a/types/commonutils.go b/types/commonutils.go index 87802a1..61f16c3 100644 --- a/types/commonutils.go +++ b/types/commonutils.go @@ -5,7 +5,9 @@ import "github.com/chewxy/hm" // Pair is a convenient structural abstraction for types that are composed of two types. // Depending on use cases, it may be useful to embed Pair, or define a new type base on *Pair. // -// Pair partially implements hm.Substitutable, but with very specific semantics - +// Pair partially implements hm.Type, as the intention is merely for syntactic abstraction +// +// It has very specific semantics - // it's useful for a small subset of types like function types, or supertypes. // See the documentation for Apply and FreeTypeVar. type Pair struct { @@ -18,19 +20,59 @@ func (t *Pair) Apply(sub hm.Subs) { t.B = t.B.Apply(sub).(hm.Type) } +// Types returns all the types of the Pair's constituents +func (t *Pair) Types() hm.Types { + retVal := hm.BorrowTypes(2) + retVal[0] = t.A + retVal[1] = t.B + return retVal +} + // FreeTypeVar returns a set of free (unbound) type variables. -func (t Pair) FreeTypeVar() hm.TypeVarSet { return t.A.FreeTypeVar().Union(t.B.FreeTypeVar()) } +func (t *Pair) FreeTypeVar() hm.TypeVarSet { return t.A.FreeTypeVar().Union(t.B.FreeTypeVar()) } + +// Clone implements Cloner +func (t *Pair) Clone() *Pair { + retVal := borrowPair() + + if ac, ok := t.A.(Cloner); ok { + retVal.A = ac.Clone().(hm.Type) + } else { + retVal.A = t.A + } + + if bc, ok := t.B.(Cloner); ok { + retVal.B = bc.Clone().(hm.Type) + } else { + retVal.B = t.B + } + return retVal +} // Monuple is a convenient structural abstraction for types that are composed of one type. // // Monuple implements hm.Substitutable, but with very specific semantics - // It's useful for singly polymorphic types like arrays, linear types, reference types, etc type Monuple struct { - A hm.Type + T hm.Type } // Apply applies a substitution to the monuple type. -func (t Monuple) Apply(subs hm.Subs) hm.Substitutable { t.A = t.A.Apply(subs).(hm.Type); return t } +func (t Monuple) Apply(subs hm.Subs) Monuple { + t.T = t.T.Apply(subs).(hm.Type) + return t +} // FreeTypeVar returns the set of free type variables in the monuple. -func (t Monuple) FreeTypeVar() hm.TypeVarSet { return t.A.FreeTypeVar() } +func (t Monuple) FreeTypeVar() hm.TypeVarSet { return t.T.FreeTypeVar() } + +// Normalize is the method to normalize all type variables +func (t Monuple) Normalize(k, v hm.TypeVarSet) (Monuple, error) { + var t2 hm.Type + var err error + if t2, err = t.T.Normalize(k, v); err != nil { + return nil, err + } + t.T = t2 + return t, nil +} diff --git a/types/function.go b/types/function.go index 6dc0ec2..a727dd3 100644 --- a/types/function.go +++ b/types/function.go @@ -6,35 +6,35 @@ import ( "github.com/chewxy/hm" ) -// FunctionType is a type constructor that builds function types. -type FunctionType Pair +// Function is a type constructor that builds function types. +type Function Pair -// NewFnType creates a new FunctionType. Functions are by default right associative. This: -// NewFnType(a, a, a) +// NewFunction creates a new FunctionType. Functions are by default right associative. This: +// NewFunction(a, a, a) // is short hand for this: -// NewFnType(a, NewFnType(a, a)) -func NewFnType(ts ...hm.Type) *FunctionType { +// NewFunction(a, NewFunction(a, a)) +func NewFunction(ts ...hm.Type) *Function { if len(ts) < 2 { panic("Expected at least 2 input types") } - retVal := borrowFnType() + retVal := borrowFn() retVal.A = ts[0] if len(ts) > 2 { - retVal.B = NewFnType(ts[1:]...) + retVal.B = NewFunction(ts[1:]...) } else { retVal.B = ts[1] } return retVal } -func (t *FunctionType) Name() string { return "→" } -func (t *FunctionType) Apply(sub hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } -func (t *FunctionType) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).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 hm.TypeVarSet) (hm.Type, error) { +func (t *Function) Name() string { return "→" } +func (t *Function) Apply(sub hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } +func (t *Function) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).FreeTypeVar() } +func (t *Function) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v → %v", t.A, t.B) } +func (t *Function) String() string { return fmt.Sprintf("%v", t) } +func (t *Function) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { var a, b hm.Type var err error if a, err = t.A.Normalize(k, v); err != nil { @@ -45,17 +45,12 @@ func (t *FunctionType) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { return nil, err } - return NewFnType(a, b), nil -} -func (t *FunctionType) Types() hm.Types { - retVal := hm.BorrowTypes(2) - retVal[0] = t.A - retVal[1] = t.B - return retVal + return NewFunction(a, b), nil } +func (t *Function) Types() hm.Types { return ((*Pair)(t)).Types() } -func (t *FunctionType) Eq(other hm.Type) bool { - if ot, ok := other.(*FunctionType); ok { +func (t *Function) Eq(other hm.Type) bool { + if ot, ok := other.(*Function); ok { return ot.A.Eq(t.A) && ot.B.Eq(t.B) } return false @@ -64,15 +59,15 @@ func (t *FunctionType) Eq(other hm.Type) bool { // Other methods (accessors mainly) // Arg returns the type of the function argument -func (t *FunctionType) Arg() hm.Type { return t.A } +func (t *Function) Arg() hm.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) hm.Type { +func (t *Function) Ret(recursive bool) hm.Type { if !recursive { return t.B } - if fnt, ok := t.B.(*FunctionType); ok { + if fnt, ok := t.B.(*Function); ok { return fnt.Ret(recursive) } @@ -80,11 +75,11 @@ func (t *FunctionType) Ret(recursive bool) hm.Type { } // FlatTypes returns the types in FunctionTypes as a flat slice of types. This allows for easier iteration in some applications -func (t *FunctionType) FlatTypes() hm.Types { +func (t *Function) FlatTypes() hm.Types { retVal := hm.BorrowTypes(8) // start with 8. Can always grow retVal = retVal[:0] - if a, ok := t.A.(*FunctionType); ok { + if a, ok := t.A.(*Function); ok { ft := a.FlatTypes() retVal = append(retVal, ft...) hm.ReturnTypes(ft) @@ -92,7 +87,7 @@ func (t *FunctionType) FlatTypes() hm.Types { retVal = append(retVal, t.A) } - if b, ok := t.B.(*FunctionType); ok { + if b, ok := t.B.(*Function); ok { ft := b.FlatTypes() retVal = append(retVal, ft...) hm.ReturnTypes(ft) @@ -102,20 +97,9 @@ func (t *FunctionType) FlatTypes() hm.Types { return retVal } -// Clone implements Cloner -func (t *FunctionType) Clone() interface{} { - retVal := new(FunctionType) - - if ac, ok := t.A.(Cloner); ok { - retVal.A = ac.Clone().(hm.Type) - } else { - retVal.A = t.A - } - - if bc, ok := t.B.(Cloner); ok { - retVal.B = bc.Clone().(hm.Type) - } else { - retVal.B = t.B - } - return retVal +// Clone implenents cloner +func (t *Function) Clone() interface{} { + p := (*Pair)(t) + cloned := p.Clone() + return (*Function)(cloned) } diff --git a/types/function_test.go b/types/function_test.go index 93acb89..4e28fa4 100644 --- a/types/function_test.go +++ b/types/function_test.go @@ -8,7 +8,7 @@ import ( ) func TestFunctionTypeBasics(t *testing.T) { - fnType := NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a'), hm.TypeVariable('a')) + fnType := NewFunction(hm.TypeVariable('a'), hm.TypeVariable('a'), hm.TypeVariable('a')) if fnType.Name() != "→" { t.Errorf("FunctionType should have \"→\" as a name. Got %q instead", fnType.Name()) } @@ -21,7 +21,7 @@ func TestFunctionTypeBasics(t *testing.T) { t.Error("Expected arg of function to be 'a'") } - if !fnType.Ret(false).Eq(NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a'))) { + if !fnType.Ret(false).Eq(NewFunction(hm.TypeVariable('a'), hm.TypeVariable('a'))) { t.Error("Expected ret(false) to be a → a") } @@ -30,7 +30,7 @@ func TestFunctionTypeBasics(t *testing.T) { } // a very simple fn - fnType = NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a')) + fnType = NewFunction(hm.TypeVariable('a'), hm.TypeVariable('a')) if !fnType.Ret(true).Eq(hm.TypeVariable('a')) { t.Error("Expected final return type to be 'a'") } @@ -41,7 +41,7 @@ func TestFunctionTypeBasics(t *testing.T) { } for _, fas := range fnApplyTests { - fn := fas.fn.Apply(fas.sub).(*FunctionType) + fn := fas.fn.Apply(fas.sub).(*Function) if !fn.Eq(fas.expected) { t.Errorf("Expected %v. Got %v instead", fas.expected, fn) } @@ -49,51 +49,51 @@ func TestFunctionTypeBasics(t *testing.T) { // bad shit f := func() { - NewFnType(hm.TypeVariable('a')) + NewFunction(hm.TypeVariable('a')) } assert.Panics(t, f) } var fnApplyTests = []struct { - fn *FunctionType + fn *Function sub hm.Subs - expected *FunctionType + expected *Function }{ - {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, proton)}, - {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, neutron)}, - {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'c': proton, 'd': neutron}, NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'))}, - {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'a': proton, 'c': neutron}, NewFnType(proton, hm.TypeVariable('b'))}, - {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'c': proton, 'b': neutron}, NewFnType(hm.TypeVariable('a'), neutron)}, - {NewFnType(electron, proton), mSubs{'a': proton, 'b': neutron}, NewFnType(electron, proton)}, + {NewFunction(hm.TypeVariable('a'), hm.TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFunction(proton, proton)}, + {NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFunction(proton, neutron)}, + {NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'c': proton, 'd': neutron}, NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b'))}, + {NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'a': proton, 'c': neutron}, NewFunction(proton, hm.TypeVariable('b'))}, + {NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'c': proton, 'b': neutron}, NewFunction(hm.TypeVariable('a'), neutron)}, + {NewFunction(electron, proton), mSubs{'a': proton, 'b': neutron}, NewFunction(electron, proton)}, // a -> (b -> c) - {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, neutron, proton)}, - {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, proton, neutron)}, - {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, neutron, hm.TypeVariable('c'))}, - {NewFnType(hm.TypeVariable('a'), hm.TypeVariable('c'), hm.TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFnType(proton, hm.TypeVariable('c'), neutron)}, + {NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFunction(proton, neutron, proton)}, + {NewFunction(hm.TypeVariable('a'), hm.TypeVariable('a'), hm.TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFunction(proton, proton, neutron)}, + {NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')), mSubs{'a': proton, 'b': neutron}, NewFunction(proton, neutron, hm.TypeVariable('c'))}, + {NewFunction(hm.TypeVariable('a'), hm.TypeVariable('c'), hm.TypeVariable('b')), mSubs{'a': proton, 'b': neutron}, NewFunction(proton, hm.TypeVariable('c'), neutron)}, // (a -> b) -> c - {NewFnType(NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), hm.TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFnType(NewFnType(proton, neutron), proton)}, + {NewFunction(NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b')), hm.TypeVariable('a')), mSubs{'a': proton, 'b': neutron}, NewFunction(NewFunction(proton, neutron), proton)}, } func TestFunctionType_FlatTypes(t *testing.T) { - fnType := NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')) + fnType := NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')) ts := fnType.FlatTypes() correct := hm.Types{hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')} assert.Equal(t, ts, correct) - fnType2 := NewFnType(fnType, hm.TypeVariable('d')) + fnType2 := NewFunction(fnType, hm.TypeVariable('d')) correct = append(correct, hm.TypeVariable('d')) ts = fnType2.FlatTypes() assert.Equal(t, ts, correct) } func TestFunctionType_Clone(t *testing.T) { - fnType := NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')) + fnType := NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')) assert.Equal(t, fnType.Clone(), fnType) - rec := NewRecordType("", hm.TypeVariable('a'), NewFnType(hm.TypeVariable('a'), hm.TypeVariable('b')), hm.TypeVariable('c')) - fnType = NewFnType(rec, rec) + rec := NewRecordType("", hm.TypeVariable('a'), NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b')), hm.TypeVariable('c')) + fnType = NewFunction(rec, rec) assert.Equal(t, fnType.Clone(), fnType) } diff --git a/types/monuples.go b/types/monuples.go new file mode 100644 index 0000000..ba507e2 --- /dev/null +++ b/types/monuples.go @@ -0,0 +1,55 @@ +package hmtypes + +import ( + "fmt" + + "github.com/chewxy/hm" +) + +// Slice is the type of a Slice/List +type Slice Monuple + +func (t Slice) Name() string { return "List" } +func (t Slice) Apply(subs hm.Subs) hm.Substitutable { return Slice(Monuple(t).Apply(subs)) } +func (t Slice) FreeTypeVar() hm.TypeVarSet { return Monuple(t).FreeTypeVar() } +func (t Slice) Format(s fmt.State, c rune) { fmt.Fprintf(s, "[]%v", t.T) } +func (t Slice) String() string { return fmt.Sprintf("%v", t) } +func (t Slice) Types() hm.Types { return hm.Types{t.T} } +func (t Slice) Eq(other hm.Type) bool { + if ot, ok := other.(Slice); ok { + return ot.T.Eq(t.T) + } + return false +} + +// Linear is a linear type (i.e types that can only appear once) +type Linear Monuple + +func (t Linear) Name() string { return "Linear" } +func (t Linear) Apply(subs hm.Subs) hm.Substitutable { return Linear(Monuple(t).Apply(subs)) } +func (t Linear) FreeTypeVar() hm.TypeVarSet { return Monuple(t).FreeTypeVar() } +func (t Linear) Format(s fmt.State, c rune) { fmt.Fprintf(s, "Linear[%v]", t.T) } +func (t Linear) String() string { return fmt.Sprintf("%v", t) } +func (t Linear) Types() hm.Types { return hm.Types{t.T} } +func (t Linear) Eq(other hm.Type) bool { + if ot, ok := other.(Linear); ok { + return ot.T.Eq(t.T) + } + return false +} + +// Ref is a reference type (think pointers) +type Ref Monuple + +func (t Ref) Name() string { return "Ref" } +func (t Ref) Apply(subs hm.Subs) hm.Substitutable { return Ref(Monuple(t).Apply(subs)) } +func (t Ref) FreeTypeVar() hm.TypeVarSet { return Monuple(t).FreeTypeVar() } +func (t Ref) Format(s fmt.State, c rune) { fmt.Fprintf(s, "*%v", t.T) } +func (t Ref) String() string { return fmt.Sprintf("%v", t) } +func (t Ref) Types() hm.Types { return hm.Types{t.T} } +func (t Ref) Eq(other hm.Type) bool { + if ot, ok := other.(Ref); ok { + return ot.T.Eq(t.T) + } + return false +} diff --git a/types/perf.go b/types/perf.go index 6b369ed..e682236 100644 --- a/types/perf.go +++ b/types/perf.go @@ -9,19 +9,23 @@ var pairPool = &sync.Pool{ New: func() interface{} { return new(Pair) }, } -func borrowFnType() *FunctionType { +func borrowPair() *Pair { + return pairPool.Get().(*Pair) +} + +func borrowFn() *Function { got := pairPool.Get().(*Pair) - return (*FunctionType)(unsafe.Pointer(got)) + return (*Function)(unsafe.Pointer(got)) } -// 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) +// ReturnFn returns a *FunctionType to the pool. NewFnType automatically borrows from the pool. USE WITH CAUTION +func ReturnFn(fnt *Function) { + if a, ok := fnt.A.(*Function); ok { + ReturnFn(a) } - if b, ok := fnt.B.(*FunctionType); ok { - ReturnFnType(b) + if b, ok := fnt.B.(*Function); ok { + ReturnFn(b) } fnt.A = nil diff --git a/types/perf_test.go b/types/perf_test.go index 8572a2e..7d82844 100644 --- a/types/perf_test.go +++ b/types/perf_test.go @@ -3,12 +3,12 @@ package hmtypes import "testing" func TestFnTypePool(t *testing.T) { - f := borrowFnType() - f.A = NewFnType(proton, electron) - f.B = NewFnType(proton, neutron) + f := borrowFn() + f.A = NewFunction(proton, electron) + f.B = NewFunction(proton, neutron) - ReturnFnType(f) - f = borrowFnType() + ReturnFn(f) + f = borrowFn() if f.A != nil { t.Error("FunctionType not cleaned up: a is not nil") } From 0317cce5005e7a9f7fe99ace4897a2d82b2c2c87 Mon Sep 17 00:00:00 2001 From: chewxy Date: Thu, 28 Dec 2017 12:22:20 +1100 Subject: [PATCH 05/21] Added pair types and quantified types --- types/pairs.go | 33 +++++++++++++++++++++++++++++++++ types/quantified.go | 9 +++++++++ 2 files changed, 42 insertions(+) create mode 100644 types/pairs.go create mode 100644 types/quantified.go diff --git a/types/pairs.go b/types/pairs.go new file mode 100644 index 0000000..7c0cf30 --- /dev/null +++ b/types/pairs.go @@ -0,0 +1,33 @@ +package hmtypes + +// pair types + +// Choice is the type of choice of algorithm to use within a class method. +// +// Imagine how one would implement a class in an OOP language. +// Then imagine how one would implement method overloading for the class. +// The typical approach is name mangling followed by having a jump table. +// +// Now consider OOP classes and the ability to override methods, based on subclassing ability. +// The typical approach to this is to use a Vtable. +// +// Both overloading and overriding have a general notion: a jump table of sorts. +// How does one type such a table? +// +// By using Choice. +// +// The first type is the key of either the vtable or the name mangled table. +// The second type is the value of the table. +// +// TODO: implement hm.Type +type Choice Pair + +// Super is the inverse of Choice. It allows for supertyping functions. +// +// Supertyping is typically implemented as a adding an entry to the vtable/mangled table. +// But there needs to be a separate accounting structure to keep account of the types. +// +// This is where Super comes in. +// +// TODO: implement hm.Type +type Super Pair diff --git a/types/quantified.go b/types/quantified.go new file mode 100644 index 0000000..a54ae75 --- /dev/null +++ b/types/quantified.go @@ -0,0 +1,9 @@ +package hmtypes + +import "github.com/chewxy/hm" + +// Quantified is essentially a replacement scheme that is made into a Type +// TODO: implement hm.Type +type Quantified struct { + hm.Scheme +} From 71799abce23fb5bf41db6c9e2631f5ab3b6eb7fe Mon Sep 17 00:00:00 2001 From: chewxy Date: Thu, 28 Dec 2017 13:04:17 +1100 Subject: [PATCH 06/21] Added Normalize to the monuples --- types/commonutils.go | 2 +- types/monuples.go | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/types/commonutils.go b/types/commonutils.go index 61f16c3..5c1e736 100644 --- a/types/commonutils.go +++ b/types/commonutils.go @@ -71,7 +71,7 @@ func (t Monuple) Normalize(k, v hm.TypeVarSet) (Monuple, error) { var t2 hm.Type var err error if t2, err = t.T.Normalize(k, v); err != nil { - return nil, err + return Monuple{}, err } t.T = t2 return t, nil diff --git a/types/monuples.go b/types/monuples.go index ba507e2..48bb9ae 100644 --- a/types/monuples.go +++ b/types/monuples.go @@ -15,6 +15,15 @@ func (t Slice) FreeTypeVar() hm.TypeVarSet { return Monuple(t).FreeType func (t Slice) Format(s fmt.State, c rune) { fmt.Fprintf(s, "[]%v", t.T) } func (t Slice) String() string { return fmt.Sprintf("%v", t) } func (t Slice) Types() hm.Types { return hm.Types{t.T} } + +func (t Slice) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { + t2, err := Monuple(t).Normalize(k, v) + if err != nil { + return nil, err + } + return Slice(t2), nil +} + func (t Slice) Eq(other hm.Type) bool { if ot, ok := other.(Slice); ok { return ot.T.Eq(t.T) @@ -31,6 +40,15 @@ func (t Linear) FreeTypeVar() hm.TypeVarSet { return Monuple(t).FreeTyp func (t Linear) Format(s fmt.State, c rune) { fmt.Fprintf(s, "Linear[%v]", t.T) } func (t Linear) String() string { return fmt.Sprintf("%v", t) } func (t Linear) Types() hm.Types { return hm.Types{t.T} } + +func (t Linear) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { + t2, err := Monuple(t).Normalize(k, v) + if err != nil { + return nil, err + } + return Linear(t2), nil +} + func (t Linear) Eq(other hm.Type) bool { if ot, ok := other.(Linear); ok { return ot.T.Eq(t.T) @@ -47,6 +65,15 @@ func (t Ref) FreeTypeVar() hm.TypeVarSet { return Monuple(t).FreeTypeVa func (t Ref) Format(s fmt.State, c rune) { fmt.Fprintf(s, "*%v", t.T) } func (t Ref) String() string { return fmt.Sprintf("%v", t) } func (t Ref) Types() hm.Types { return hm.Types{t.T} } + +func (t Ref) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { + t2, err := Monuple(t).Normalize(k, v) + if err != nil { + return nil, err + } + return Ref(t2), nil +} + func (t Ref) Eq(other hm.Type) bool { if ot, ok := other.(Ref); ok { return ot.T.Eq(t.T) From cc8b3e031ff6924a9fc02e03f662d88b53cbf0b7 Mon Sep 17 00:00:00 2001 From: chewxy Date: Thu, 28 Dec 2017 13:33:29 +1100 Subject: [PATCH 07/21] exported a few internal functions - they're useful in other unificationbased type systems that aren't strictly HM --- hm.go | 14 ++++++++------ solver.go | 2 +- substitutions.go | 3 ++- substitutions_test.go | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/hm.go b/hm.go index 5d8cc8b..39098c9 100644 --- a/hm.go +++ b/hm.go @@ -334,14 +334,14 @@ func Unify(a, b Type) (sub Subs, err error) { switch at := a.(type) { case TypeVariable: - return bind(at, b) + return Bind(at, b) default: if a.Eq(b) { return nil, nil } if btv, ok := b.(TypeVariable); ok { - return bind(btv, a) + return Bind(btv, a) } atypes := a.Types() btypes := b.Types() @@ -385,7 +385,7 @@ func unifyMany(a, b Types) (sub Subs, err error) { if sub == nil { sub = s2 } else { - sub2 := compose(sub, s2) + sub2 := Compose(sub, s2) defer ReturnSubs(s2) if sub2 != sub { defer ReturnSubs(sub) @@ -396,11 +396,12 @@ func unifyMany(a, b Types) (sub Subs, err error) { return } -func bind(tv TypeVariable, t Type) (sub Subs, err error) { +// Bind binds a TypeVariable to a Type. It returns a substitution list. +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): + case Occurs(tv, t): err = errors.Errorf("recursive unification") default: ssub := BorrowSSubs(1) @@ -411,7 +412,8 @@ func bind(tv TypeVariable, t Type) (sub Subs, err error) { return } -func occurs(tv TypeVariable, s Substitutable) bool { +// Occurs checks if a TypeVariable exists in any Substitutable (type, scheme, map etc). +func Occurs(tv TypeVariable, s Substitutable) bool { ftv := s.FreeTypeVar() defer ReturnTypeVarSet(ftv) diff --git a/solver.go b/solver.go index 80a1142..3c055f0 100644 --- a/solver.go +++ b/solver.go @@ -27,7 +27,7 @@ func (s *solver) solve(cs Constraints) { sub, s.err = Unify(c.a, c.b) defer ReturnSubs(s.sub) - s.sub = compose(sub, s.sub) + s.sub = Compose(sub, s.sub) cs = cs[1:].Apply(s.sub).(Constraints) s.solve(cs) diff --git a/substitutions.go b/substitutions.go index 546d052..abd0b62 100644 --- a/substitutions.go +++ b/substitutions.go @@ -123,7 +123,8 @@ func (s mSubs) Clone() Subs { return retVal } -func compose(a, b Subs) (retVal Subs) { +// Compose composes two substitution lists together. +func Compose(a, b Subs) (retVal Subs) { if b == nil { return a } diff --git a/substitutions_test.go b/substitutions_test.go index aabbd14..9e467a0 100644 --- a/substitutions_test.go +++ b/substitutions_test.go @@ -131,7 +131,7 @@ var composeTests = []struct { func TestCompose(t *testing.T) { for i, cts := range composeTests { - subs := compose(cts.a, cts.b) + subs := Compose(cts.a, cts.b) for _, v := range cts.expected.Iter() { if T, ok := subs.Get(v.Tv); !ok { From 88fd6bdd4651c1862dbb3f21aa0838a41d441fe7 Mon Sep 17 00:00:00 2001 From: chewxy Date: Thu, 28 Dec 2017 19:24:04 +1100 Subject: [PATCH 08/21] Added Application, added comments to MakeSub --- substitutions.go | 2 ++ types/pairs.go | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/substitutions.go b/substitutions.go index abd0b62..3a85e25 100644 --- a/substitutions.go +++ b/substitutions.go @@ -14,6 +14,8 @@ type Subs interface { Clone() Subs } +// MakeSubs is a utility function to help make substitution lists. +// This is useful for cases where there isn't a real need to implement Subs func MakeSubs(n int) Subs { if n >= 30 { return make(mSubs) diff --git a/types/pairs.go b/types/pairs.go index 7c0cf30..9dc539b 100644 --- a/types/pairs.go +++ b/types/pairs.go @@ -31,3 +31,14 @@ type Choice Pair // // TODO: implement hm.Type type Super Pair + +// Application is the pre-unified type for a function application. +// In a simple HM system this would not be needed as the type of an +// application expression would be found during the unification phase of +// the expression. +// +// In advanced systems where unification may be done concurrently, this would +// be required, as a "thunk" of sorts for the type system. +// +// TODO: implement hm.Type +type Application Pair From 44a2cf0f344aeb1c32a065e90d674546e2583600 Mon Sep 17 00:00:00 2001 From: chewxy Date: Thu, 28 Dec 2017 19:36:44 +1100 Subject: [PATCH 09/21] Implemented Type for the Pair types. Except for Normalize --- types/pairs.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/types/pairs.go b/types/pairs.go index 9dc539b..e7d7008 100644 --- a/types/pairs.go +++ b/types/pairs.go @@ -1,5 +1,17 @@ package hmtypes +import ( + "fmt" + + "github.com/chewxy/hm" +) + +var ( + _ hm.Type = &Choice{} + _ hm.Type = &Super{} + _ hm.Type = &Application{} +) + // pair types // Choice is the type of choice of algorithm to use within a class method. @@ -18,20 +30,54 @@ package hmtypes // // The first type is the key of either the vtable or the name mangled table. // The second type is the value of the table. -// -// TODO: implement hm.Type type Choice Pair +func (t *Choice) Name() string { return ":" } +func (t *Choice) Apply(sub hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } +func (t *Choice) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).FreeTypeVar() } +func (t *Choice) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v : %v", t.A, t.B) } +func (t *Choice) String() string { return fmt.Sprintf("%v", t) } + +func (t *Choice) Normalize(k hm.TypeVarSet, v hm.TypeVarSet) (hm.Type, error) { + panic("not implemented") +} + +func (t *Choice) Types() hm.Types { return ((*Pair)(t)).Types() } + +func (t *Choice) Eq(other hm.Type) bool { + if ot, ok := other.(*Choice); ok { + return ot.A.Eq(t.A) && ot.B.Eq(t.B) + } + return false +} + // Super is the inverse of Choice. It allows for supertyping functions. // // Supertyping is typically implemented as a adding an entry to the vtable/mangled table. // But there needs to be a separate accounting structure to keep account of the types. // // This is where Super comes in. -// -// TODO: implement hm.Type type Super Pair +func (t *Super) Name() string { return "§" } +func (t *Super) Apply(sub hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } +func (t *Super) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).FreeTypeVar() } +func (t *Super) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v §: %v", t.A, t.B) } +func (t *Super) String() string { return fmt.Sprintf("%v", t) } + +func (t *Super) Normalize(k hm.TypeVarSet, v hm.TypeVarSet) (hm.Type, error) { + panic("not implemented") +} + +func (t *Super) Types() hm.Types { return ((*Pair)(t)).Types() } + +func (t *Super) Eq(other hm.Type) bool { + if ot, ok := other.(*Super); ok { + return ot.A.Eq(t.A) && ot.B.Eq(t.B) + } + return false +} + // Application is the pre-unified type for a function application. // In a simple HM system this would not be needed as the type of an // application expression would be found during the unification phase of @@ -39,6 +85,23 @@ type Super Pair // // In advanced systems where unification may be done concurrently, this would // be required, as a "thunk" of sorts for the type system. -// -// TODO: implement hm.Type type Application Pair + +func (t *Application) Name() string { return "•" } +func (t *Application) Apply(sub hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } +func (t *Application) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).FreeTypeVar() } +func (t *Application) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v • %v", t.A, t.B) } +func (t *Application) String() string { return fmt.Sprintf("%v", t) } + +func (t *Application) Normalize(k hm.TypeVarSet, v hm.TypeVarSet) (hm.Type, error) { + panic("not implemented") +} + +func (t *Application) Types() hm.Types { return ((*Pair)(t)).Types() } + +func (t *Application) Eq(other hm.Type) bool { + if ot, ok := other.(*Application); ok { + return ot.A.Eq(t.A) && ot.B.Eq(t.B) + } + return false +} From 0327ec5372dc2636f0c236140a8c29a32914bb68 Mon Sep 17 00:00:00 2001 From: chewxy Date: Fri, 5 Jan 2018 01:47:20 +1100 Subject: [PATCH 10/21] Added a few methods and a couple of interfaces for Monuples and Pairs --- types/commonutils.go | 12 ++++++++++++ types/monuples.go | 6 ++++++ types/pairs.go | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/types/commonutils.go b/types/commonutils.go index 5c1e736..fd9a8d9 100644 --- a/types/commonutils.go +++ b/types/commonutils.go @@ -76,3 +76,15 @@ func (t Monuple) Normalize(k, v hm.TypeVarSet) (Monuple, error) { t.T = t2 return t, nil } + +// Pairer is any type that can be represented by a Pair +type Pairer interface { + hm.Type + AsPair() *Pair +} + +// Monupler is any type that can be represented by a Monuple +type Monupler interface { + hm.Type + AsMonuple() Monuple +} diff --git a/types/monuples.go b/types/monuples.go index 48bb9ae..2faded1 100644 --- a/types/monuples.go +++ b/types/monuples.go @@ -31,6 +31,8 @@ func (t Slice) Eq(other hm.Type) bool { return false } +func (t Slice) Monuple() Monuple { return Monuple(t) } + // Linear is a linear type (i.e types that can only appear once) type Linear Monuple @@ -56,6 +58,8 @@ func (t Linear) Eq(other hm.Type) bool { return false } +func (t Linear) Monuple() Monuple { return Monuple(t) } + // Ref is a reference type (think pointers) type Ref Monuple @@ -80,3 +84,5 @@ func (t Ref) Eq(other hm.Type) bool { } return false } + +func (t Ref) Monuple() Monuple { return Monuple(t) } diff --git a/types/pairs.go b/types/pairs.go index e7d7008..13c94f8 100644 --- a/types/pairs.go +++ b/types/pairs.go @@ -51,6 +51,10 @@ func (t *Choice) Eq(other hm.Type) bool { return false } +func (t *Choice) Clone() interface{} { return (*Choice)((*Pair)(t).Clone()) } + +func (t *Choice) Pair() *Pair { return (*Pair)(t) } + // Super is the inverse of Choice. It allows for supertyping functions. // // Supertyping is typically implemented as a adding an entry to the vtable/mangled table. @@ -78,6 +82,10 @@ func (t *Super) Eq(other hm.Type) bool { return false } +func (t *Super) Clone() interface{} { return (*Super)((*Pair)(t).Clone()) } + +func (t *Super) Pair() *Pair { return (*Pair)(t) } + // Application is the pre-unified type for a function application. // In a simple HM system this would not be needed as the type of an // application expression would be found during the unification phase of @@ -105,3 +113,7 @@ func (t *Application) Eq(other hm.Type) bool { } return false } + +func (t *Application) Clone() interface{} { return (*Application)((*Pair)(t).Clone()) } + +func (t *Application) Pair() *Pair { return (*Pair)(t) } From ff247665d2b90c536421ed77f1bc5a65c9ae75c9 Mon Sep 17 00:00:00 2001 From: chewxy Date: Fri, 12 Jan 2018 09:53:35 +1100 Subject: [PATCH 11/21] Added the separation between Tuple and Record --- types/record.go | 180 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 140 insertions(+), 40 deletions(-) diff --git a/types/record.go b/types/record.go index 6e19ab0..3156503 100644 --- a/types/record.go +++ b/types/record.go @@ -6,29 +6,26 @@ import ( "github.com/chewxy/hm" ) -// Record is a basic record/tuple type. It takes an optional name. -type Record struct { +// Tuple is a basic tuple type. It takes an optional name +type Tuple struct { ts []hm.Type name string } -// NewRecordType creates a new Record hm.Type -func NewRecordType(name string, ts ...hm.Type) *Record { - return &Record{ +// NewTupleType creates a new Tuple +func NewTupleType(name string, ts ...hm.Type) *Tuple { + return &Tuple{ ts: ts, name: name, } } -func (t *Record) Apply(subs hm.Subs) hm.Substitutable { - ts := make([]hm.Type, len(t.ts)) - for i, v := range t.ts { - ts[i] = v.Apply(subs).(hm.Type) - } - return NewRecordType(t.name, ts...) +func (t *Tuple) Apply(subs hm.Subs) hm.Substitutable { + ts := t.apply(subs) + return NewTupleType(t.name, ts...) } -func (t *Record) FreeTypeVar() hm.TypeVarSet { +func (t *Tuple) FreeTypeVar() hm.TypeVarSet { var tvs hm.TypeVarSet for _, v := range t.ts { tvs = v.FreeTypeVar().Union(tvs) @@ -36,32 +33,42 @@ func (t *Record) FreeTypeVar() hm.TypeVarSet { return tvs } -func (t *Record) Name() string { +func (t *Tuple) Name() string { if t.name != "" { return t.name } return t.String() } -func (t *Record) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { - ts := make([]hm.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 - } +func (t *Tuple) Normalize(k, v hm.TypeVarSet) (T hm.Type, err error) { + var ts []hm.Type + if ts, err = t.normalize(k, v); err != nil { + return nil, err } - return NewRecordType(t.name, ts...), nil + return NewTupleType(t.name, ts...), nil } -func (t *Record) Types() hm.Types { +func (t *Tuple) Types() hm.Types { ts := hm.BorrowTypes(len(t.ts)) copy(ts, t.ts) return ts } -func (t *Record) Eq(other hm.Type) bool { - if ot, ok := other.(*Record); ok { +func (t *Tuple) 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 *Tuple) String() string { return fmt.Sprintf("%v", t) } + +func (t *Tuple) Eq(other hm.Type) bool { + if ot, ok := other.(*Tuple); ok { if len(ot.ts) != len(t.ts) { return false } @@ -75,23 +82,9 @@ func (t *Record) Eq(other hm.Type) bool { 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) +func (t *Tuple) Clone() interface{} { + retVal := new(Tuple) ts := hm.BorrowTypes(len(t.ts)) for i, tt := range t.ts { if c, ok := tt.(Cloner); ok { @@ -105,3 +98,110 @@ func (t *Record) Clone() interface{} { return retVal } + +// internal function to be used by Tuple.Apply and Record.Apply +func (t *Tuple) apply(subs hm.Subs) []hm.Type { + ts := make([]hm.Type, len(t.ts)) + for i, v := range t.ts { + ts[i] = v.Apply(subs).(hm.Type) + } + return ts +} + +// internal function to be used by Tuple.Normalize and Record.Normalize +func (t *Tuple) normalize(k, v hm.TypeVarSet) ([]hm.Type, error) { + ts := make([]hm.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 ts +} + +// Field is a name-type pair. +type Field struct { + Name string + Type hm.Type +} + +// Record is a basic record type. It's like Tuple except there are named fields. It takes an optional name. +type Record struct { + Tuple + ns []string // field names +} + +// NewRecordType creates a new Record hm.Type +func NewRecordType(name string, fields ...Field) *Record { + ts := make([]hm.Type, len(fields)) + ns := make([]string, len(fields)) + for i := range fields { + ts[i] = fields[i].Name + ns[i] = fields[i].Type + } + return &Record{ + Tuple: Tuple{ + ts: ts, + name: name, + }, + ns: ns, + } +} + +func (t *Record) Apply(subs hm.Subs) hm.Substitutable { + ts := t.apply(subs) + return &Record{ + Tuple: Tuple{ + ts: ts, + name: t.name, + }, + ns: t.ns, + } +} + +func (t *Record) Normalize(k, v hm.TypeVarSet) (T hm.Type, err error) { + var ts []hm.Type + if ts, err = t.normalize(k, v); err != nil { + return nil, err + } + return &Record{ + Tuple: Tuple{ + ts: ts, + name: t.name, + }, + ns: t.ns, + }, nil +} + +func (t *Record) Format(f fmt.State, c rune) { + if t.name != "" { + f.Write([]byte(t.name)) + } + f.Write([]byte("{")) + for i, v := range t.ts { + if i < len(t.ts)-1 { + fmt.Fprintf(f, "%v: %v, ", t.ns[i], v) + } else { + fmt.Fprintf(f, "%v: %v}", t.ns[i], v) + } + } +} + +func (t *Record) Eq(other hm.Type) bool { + if ot, ok := other.(*Record); ok { + if len(ot.ts) != len(t.ts) { + return false + } + for i, v := range t.ts { + if t.ns[i] != ot.ns[i] { + return false + } + if !v.Eq(ot.ts[i]) { + return false + } + } + return true + } + return false +} From 1457e7f04c2cedf4427d1f7d06c7467f4c82226b Mon Sep 17 00:00:00 2001 From: chewxy Date: Fri, 12 Jan 2018 09:56:07 +1100 Subject: [PATCH 12/21] forgot to save --- types/record.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/record.go b/types/record.go index 3156503..5ebfb45 100644 --- a/types/record.go +++ b/types/record.go @@ -117,7 +117,7 @@ func (t *Tuple) normalize(k, v hm.TypeVarSet) ([]hm.Type, error) { return nil, err } } - return ts + return ts, nil } // Field is a name-type pair. @@ -137,8 +137,8 @@ func NewRecordType(name string, fields ...Field) *Record { ts := make([]hm.Type, len(fields)) ns := make([]string, len(fields)) for i := range fields { - ts[i] = fields[i].Name - ns[i] = fields[i].Type + ns[i] = fields[i].Name + ts[i] = fields[i].Type } return &Record{ Tuple: Tuple{ From 5162b54f06d3b5a9d93bea20c7f5b6e668bbc1a4 Mon Sep 17 00:00:00 2001 From: chewxy Date: Fri, 12 Jan 2018 15:56:41 +1100 Subject: [PATCH 13/21] Moved Pairs and Monuples back into package hm --- constraint.go | 23 +++------------- constraint_test.go | 8 +++--- perf.go | 20 +++++++++++++- solver.go | 2 +- types/commonutils.go => struct.go | 44 +++++++++++++++---------------- substitutables_test.go | 8 +++--- types/function.go | 10 +++---- types/function_test.go | 2 +- types/monuples.go | 30 ++++++++++----------- types/pairs.go | 36 ++++++++++++------------- types/perf.go | 17 ++++-------- 11 files changed, 98 insertions(+), 102 deletions(-) rename types/commonutils.go => struct.go (65%) diff --git a/constraint.go b/constraint.go index c526fd3..a29a923 100644 --- a/constraint.go +++ b/constraint.go @@ -3,23 +3,8 @@ package 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 -} +type Constraint Pair -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) -} +func (c Constraint) Apply(sub Subs) Substitutable { (*Pair)(&c).Apply(sub); return c } +func (c Constraint) FreeTypeVar() TypeVarSet { return Pair(c).FreeTypeVar() } +func (c Constraint) Format(state fmt.State, r rune) { fmt.Fprintf(state, "{%v = %v}", c.A, c.B) } diff --git a/constraint_test.go b/constraint_test.go index 5894a3e..10a9506 100644 --- a/constraint_test.go +++ b/constraint_test.go @@ -4,8 +4,8 @@ import "testing" func TestConstraint(t *testing.T) { c := Constraint{ - a: TypeVariable('a'), - b: NewFnType(TypeVariable('b'), TypeVariable('c')), + A: TypeVariable('a'), + B: NewFnType(TypeVariable('b'), TypeVariable('c')), } ftv := c.FreeTypeVar() @@ -20,11 +20,11 @@ func TestConstraint(t *testing.T) { } c = c.Apply(subs).(Constraint) - if !c.a.Eq(NewFnType(proton, proton)) { + if !c.A.Eq(NewFnType(proton, proton)) { t.Errorf("c.a: %v", c) } - if !c.b.Eq(NewFnType(proton, neutron)) { + if !c.B.Eq(NewFnType(proton, neutron)) { t.Errorf("c.b: %v", c) } } diff --git a/perf.go b/perf.go index c324932..e5e50da 100644 --- a/perf.go +++ b/perf.go @@ -1,6 +1,8 @@ package hm -import "sync" +import ( + "sync" +) const ( poolSize = 4 @@ -160,3 +162,19 @@ func ReturnFnType(fnt *FunctionType) { fnt.b = nil fnTypePool.Put(fnt) } + +var pairPool = &sync.Pool{ + New: func() interface{} { return new(Pair) }, +} + +// BorrowPair allows access to this package's pair pool +func BorrowPair() *Pair { + return pairPool.Get().(*Pair) +} + +// ReturnPair allows accesso this package's pair pool +func ReturnPair(p *Pair) { + p.A = nil + p.B = nil + pairPool.Put(p) +} diff --git a/solver.go b/solver.go index 3c055f0..cc5887f 100644 --- a/solver.go +++ b/solver.go @@ -24,7 +24,7 @@ func (s *solver) solve(cs Constraints) { default: var sub Subs c := cs[0] - sub, s.err = Unify(c.a, c.b) + sub, s.err = Unify(c.A, c.B) defer ReturnSubs(s.sub) s.sub = Compose(sub, s.sub) diff --git a/types/commonutils.go b/struct.go similarity index 65% rename from types/commonutils.go rename to struct.go index fd9a8d9..eb4adc7 100644 --- a/types/commonutils.go +++ b/struct.go @@ -1,48 +1,48 @@ -package hmtypes +package hm -import "github.com/chewxy/hm" +// this file provides a common structural abstraction // Pair is a convenient structural abstraction for types that are composed of two types. // Depending on use cases, it may be useful to embed Pair, or define a new type base on *Pair. // -// Pair partially implements hm.Type, as the intention is merely for syntactic abstraction +// Pair partially implements Type, as the intention is merely for syntactic abstraction // // It has very specific semantics - // it's useful for a small subset of types like function types, or supertypes. // See the documentation for Apply and FreeTypeVar. type Pair struct { - A, B hm.Type + A, B Type } // Apply applies a substitution on both the first and second types of the Pair. -func (t *Pair) Apply(sub hm.Subs) { - t.A = t.A.Apply(sub).(hm.Type) - t.B = t.B.Apply(sub).(hm.Type) +func (t *Pair) Apply(sub Subs) { + t.A = t.A.Apply(sub).(Type) + t.B = t.B.Apply(sub).(Type) } // Types returns all the types of the Pair's constituents -func (t *Pair) Types() hm.Types { - retVal := hm.BorrowTypes(2) +func (t Pair) Types() Types { + retVal := BorrowTypes(2) retVal[0] = t.A retVal[1] = t.B return retVal } // FreeTypeVar returns a set of free (unbound) type variables. -func (t *Pair) FreeTypeVar() hm.TypeVarSet { return t.A.FreeTypeVar().Union(t.B.FreeTypeVar()) } +func (t Pair) FreeTypeVar() TypeVarSet { return t.A.FreeTypeVar().Union(t.B.FreeTypeVar()) } // Clone implements Cloner func (t *Pair) Clone() *Pair { - retVal := borrowPair() + retVal := BorrowPair() if ac, ok := t.A.(Cloner); ok { - retVal.A = ac.Clone().(hm.Type) + retVal.A = ac.Clone().(Type) } else { retVal.A = t.A } if bc, ok := t.B.(Cloner); ok { - retVal.B = bc.Clone().(hm.Type) + retVal.B = bc.Clone().(Type) } else { retVal.B = t.B } @@ -51,24 +51,24 @@ func (t *Pair) Clone() *Pair { // Monuple is a convenient structural abstraction for types that are composed of one type. // -// Monuple implements hm.Substitutable, but with very specific semantics - +// Monuple implements Substitutable, but with very specific semantics - // It's useful for singly polymorphic types like arrays, linear types, reference types, etc type Monuple struct { - T hm.Type + T Type } // Apply applies a substitution to the monuple type. -func (t Monuple) Apply(subs hm.Subs) Monuple { - t.T = t.T.Apply(subs).(hm.Type) +func (t Monuple) Apply(subs Subs) Monuple { + t.T = t.T.Apply(subs).(Type) return t } // FreeTypeVar returns the set of free type variables in the monuple. -func (t Monuple) FreeTypeVar() hm.TypeVarSet { return t.T.FreeTypeVar() } +func (t Monuple) FreeTypeVar() TypeVarSet { return t.T.FreeTypeVar() } // Normalize is the method to normalize all type variables -func (t Monuple) Normalize(k, v hm.TypeVarSet) (Monuple, error) { - var t2 hm.Type +func (t Monuple) Normalize(k, v TypeVarSet) (Monuple, error) { + var t2 Type var err error if t2, err = t.T.Normalize(k, v); err != nil { return Monuple{}, err @@ -79,12 +79,12 @@ func (t Monuple) Normalize(k, v hm.TypeVarSet) (Monuple, error) { // Pairer is any type that can be represented by a Pair type Pairer interface { - hm.Type + Type AsPair() *Pair } // Monupler is any type that can be represented by a Monuple type Monupler interface { - hm.Type + Type AsMonuple() Monuple } diff --git a/substitutables_test.go b/substitutables_test.go index 484cdec..aebdbfa 100644 --- a/substitutables_test.go +++ b/substitutables_test.go @@ -25,17 +25,17 @@ func TestConstraints(t *testing.T) { } cs = cs.Apply(sub).(Constraints) - if cs[0].a != neutron { + if cs[0].A != neutron { t.Error("Expected neutron") } - if cs[0].b != proton { + if cs[0].B != proton { t.Error("Expected proton") } - if cs[1].a != TypeVariable('b') { + if cs[1].A != TypeVariable('b') { t.Error("There was nothing to substitute b with") } - if cs[1].b != proton { + if cs[1].B != proton { t.Error("Expected proton") } diff --git a/types/function.go b/types/function.go index a727dd3..217186e 100644 --- a/types/function.go +++ b/types/function.go @@ -7,7 +7,7 @@ import ( ) // Function is a type constructor that builds function types. -type Function Pair +type Function hm.Pair // NewFunction creates a new FunctionType. Functions are by default right associative. This: // NewFunction(a, a, a) @@ -30,8 +30,8 @@ func NewFunction(ts ...hm.Type) *Function { } func (t *Function) Name() string { return "→" } -func (t *Function) Apply(sub hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } -func (t *Function) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).FreeTypeVar() } +func (t *Function) Apply(sub hm.Subs) hm.Substitutable { ((*hm.Pair)(t)).Apply(sub); return t } +func (t *Function) FreeTypeVar() hm.TypeVarSet { return ((*hm.Pair)(t)).FreeTypeVar() } func (t *Function) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v → %v", t.A, t.B) } func (t *Function) String() string { return fmt.Sprintf("%v", t) } func (t *Function) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { @@ -47,7 +47,7 @@ func (t *Function) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { return NewFunction(a, b), nil } -func (t *Function) Types() hm.Types { return ((*Pair)(t)).Types() } +func (t *Function) Types() hm.Types { return ((*hm.Pair)(t)).Types() } func (t *Function) Eq(other hm.Type) bool { if ot, ok := other.(*Function); ok { @@ -99,7 +99,7 @@ func (t *Function) FlatTypes() hm.Types { // Clone implenents cloner func (t *Function) Clone() interface{} { - p := (*Pair)(t) + p := (*hm.Pair)(t) cloned := p.Clone() return (*Function)(cloned) } diff --git a/types/function_test.go b/types/function_test.go index 4e28fa4..b8a973b 100644 --- a/types/function_test.go +++ b/types/function_test.go @@ -93,7 +93,7 @@ func TestFunctionType_Clone(t *testing.T) { fnType := NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b'), hm.TypeVariable('c')) assert.Equal(t, fnType.Clone(), fnType) - rec := NewRecordType("", hm.TypeVariable('a'), NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b')), hm.TypeVariable('c')) + rec := NewTupleType("", hm.TypeVariable('a'), NewFunction(hm.TypeVariable('a'), hm.TypeVariable('b')), hm.TypeVariable('c')) fnType = NewFunction(rec, rec) assert.Equal(t, fnType.Clone(), fnType) } diff --git a/types/monuples.go b/types/monuples.go index 2faded1..e85d196 100644 --- a/types/monuples.go +++ b/types/monuples.go @@ -7,17 +7,17 @@ import ( ) // Slice is the type of a Slice/List -type Slice Monuple +type Slice hm.Monuple func (t Slice) Name() string { return "List" } -func (t Slice) Apply(subs hm.Subs) hm.Substitutable { return Slice(Monuple(t).Apply(subs)) } -func (t Slice) FreeTypeVar() hm.TypeVarSet { return Monuple(t).FreeTypeVar() } +func (t Slice) Apply(subs hm.Subs) hm.Substitutable { return Slice(hm.Monuple(t).Apply(subs)) } +func (t Slice) FreeTypeVar() hm.TypeVarSet { return hm.Monuple(t).FreeTypeVar() } func (t Slice) Format(s fmt.State, c rune) { fmt.Fprintf(s, "[]%v", t.T) } func (t Slice) String() string { return fmt.Sprintf("%v", t) } func (t Slice) Types() hm.Types { return hm.Types{t.T} } func (t Slice) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { - t2, err := Monuple(t).Normalize(k, v) + t2, err := hm.Monuple(t).Normalize(k, v) if err != nil { return nil, err } @@ -31,20 +31,20 @@ func (t Slice) Eq(other hm.Type) bool { return false } -func (t Slice) Monuple() Monuple { return Monuple(t) } +func (t Slice) Monuple() hm.Monuple { return hm.Monuple(t) } // Linear is a linear type (i.e types that can only appear once) -type Linear Monuple +type Linear hm.Monuple func (t Linear) Name() string { return "Linear" } -func (t Linear) Apply(subs hm.Subs) hm.Substitutable { return Linear(Monuple(t).Apply(subs)) } -func (t Linear) FreeTypeVar() hm.TypeVarSet { return Monuple(t).FreeTypeVar() } +func (t Linear) Apply(subs hm.Subs) hm.Substitutable { return Linear(hm.Monuple(t).Apply(subs)) } +func (t Linear) FreeTypeVar() hm.TypeVarSet { return hm.Monuple(t).FreeTypeVar() } func (t Linear) Format(s fmt.State, c rune) { fmt.Fprintf(s, "Linear[%v]", t.T) } func (t Linear) String() string { return fmt.Sprintf("%v", t) } func (t Linear) Types() hm.Types { return hm.Types{t.T} } func (t Linear) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { - t2, err := Monuple(t).Normalize(k, v) + t2, err := hm.Monuple(t).Normalize(k, v) if err != nil { return nil, err } @@ -58,20 +58,20 @@ func (t Linear) Eq(other hm.Type) bool { return false } -func (t Linear) Monuple() Monuple { return Monuple(t) } +func (t Linear) Monuple() hm.Monuple { return hm.Monuple(t) } // Ref is a reference type (think pointers) -type Ref Monuple +type Ref hm.Monuple func (t Ref) Name() string { return "Ref" } -func (t Ref) Apply(subs hm.Subs) hm.Substitutable { return Ref(Monuple(t).Apply(subs)) } -func (t Ref) FreeTypeVar() hm.TypeVarSet { return Monuple(t).FreeTypeVar() } +func (t Ref) Apply(subs hm.Subs) hm.Substitutable { return Ref(hm.Monuple(t).Apply(subs)) } +func (t Ref) FreeTypeVar() hm.TypeVarSet { return hm.Monuple(t).FreeTypeVar() } func (t Ref) Format(s fmt.State, c rune) { fmt.Fprintf(s, "*%v", t.T) } func (t Ref) String() string { return fmt.Sprintf("%v", t) } func (t Ref) Types() hm.Types { return hm.Types{t.T} } func (t Ref) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { - t2, err := Monuple(t).Normalize(k, v) + t2, err := hm.Monuple(t).Normalize(k, v) if err != nil { return nil, err } @@ -85,4 +85,4 @@ func (t Ref) Eq(other hm.Type) bool { return false } -func (t Ref) Monuple() Monuple { return Monuple(t) } +func (t Ref) Monuple() hm.Monuple { return hm.Monuple(t) } diff --git a/types/pairs.go b/types/pairs.go index 13c94f8..eb46bcc 100644 --- a/types/pairs.go +++ b/types/pairs.go @@ -30,11 +30,11 @@ var ( // // The first type is the key of either the vtable or the name mangled table. // The second type is the value of the table. -type Choice Pair +type Choice hm.Pair func (t *Choice) Name() string { return ":" } -func (t *Choice) Apply(sub hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } -func (t *Choice) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).FreeTypeVar() } +func (t *Choice) Apply(sub hm.Subs) hm.Substitutable { ((*hm.Pair)(t)).Apply(sub); return t } +func (t *Choice) FreeTypeVar() hm.TypeVarSet { return ((*hm.Pair)(t)).FreeTypeVar() } func (t *Choice) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v : %v", t.A, t.B) } func (t *Choice) String() string { return fmt.Sprintf("%v", t) } @@ -42,7 +42,7 @@ func (t *Choice) Normalize(k hm.TypeVarSet, v hm.TypeVarSet) (hm.Type, error) { panic("not implemented") } -func (t *Choice) Types() hm.Types { return ((*Pair)(t)).Types() } +func (t *Choice) Types() hm.Types { return ((*hm.Pair)(t)).Types() } func (t *Choice) Eq(other hm.Type) bool { if ot, ok := other.(*Choice); ok { @@ -51,9 +51,9 @@ func (t *Choice) Eq(other hm.Type) bool { return false } -func (t *Choice) Clone() interface{} { return (*Choice)((*Pair)(t).Clone()) } +func (t *Choice) Clone() interface{} { return (*Choice)((*hm.Pair)(t).Clone()) } -func (t *Choice) Pair() *Pair { return (*Pair)(t) } +func (t *Choice) Pair() *hm.Pair { return (*hm.Pair)(t) } // Super is the inverse of Choice. It allows for supertyping functions. // @@ -61,11 +61,11 @@ func (t *Choice) Pair() *Pair { return (*Pair)(t) } // But there needs to be a separate accounting structure to keep account of the types. // // This is where Super comes in. -type Super Pair +type Super hm.Pair func (t *Super) Name() string { return "§" } -func (t *Super) Apply(sub hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } -func (t *Super) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).FreeTypeVar() } +func (t *Super) Apply(sub hm.Subs) hm.Substitutable { ((*hm.Pair)(t)).Apply(sub); return t } +func (t *Super) FreeTypeVar() hm.TypeVarSet { return ((*hm.Pair)(t)).FreeTypeVar() } func (t *Super) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v §: %v", t.A, t.B) } func (t *Super) String() string { return fmt.Sprintf("%v", t) } @@ -73,7 +73,7 @@ func (t *Super) Normalize(k hm.TypeVarSet, v hm.TypeVarSet) (hm.Type, error) { panic("not implemented") } -func (t *Super) Types() hm.Types { return ((*Pair)(t)).Types() } +func (t *Super) Types() hm.Types { return ((*hm.Pair)(t)).Types() } func (t *Super) Eq(other hm.Type) bool { if ot, ok := other.(*Super); ok { @@ -82,9 +82,9 @@ func (t *Super) Eq(other hm.Type) bool { return false } -func (t *Super) Clone() interface{} { return (*Super)((*Pair)(t).Clone()) } +func (t *Super) Clone() interface{} { return (*Super)((*hm.Pair)(t).Clone()) } -func (t *Super) Pair() *Pair { return (*Pair)(t) } +func (t *Super) Pair() *hm.Pair { return (*hm.Pair)(t) } // Application is the pre-unified type for a function application. // In a simple HM system this would not be needed as the type of an @@ -93,11 +93,11 @@ func (t *Super) Pair() *Pair { return (*Pair)(t) } // // In advanced systems where unification may be done concurrently, this would // be required, as a "thunk" of sorts for the type system. -type Application Pair +type Application hm.Pair func (t *Application) Name() string { return "•" } -func (t *Application) Apply(sub hm.Subs) hm.Substitutable { ((*Pair)(t)).Apply(sub); return t } -func (t *Application) FreeTypeVar() hm.TypeVarSet { return ((*Pair)(t)).FreeTypeVar() } +func (t *Application) Apply(sub hm.Subs) hm.Substitutable { ((*hm.Pair)(t)).Apply(sub); return t } +func (t *Application) FreeTypeVar() hm.TypeVarSet { return ((*hm.Pair)(t)).FreeTypeVar() } func (t *Application) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v • %v", t.A, t.B) } func (t *Application) String() string { return fmt.Sprintf("%v", t) } @@ -105,7 +105,7 @@ func (t *Application) Normalize(k hm.TypeVarSet, v hm.TypeVarSet) (hm.Type, erro panic("not implemented") } -func (t *Application) Types() hm.Types { return ((*Pair)(t)).Types() } +func (t *Application) Types() hm.Types { return ((*hm.Pair)(t)).Types() } func (t *Application) Eq(other hm.Type) bool { if ot, ok := other.(*Application); ok { @@ -114,6 +114,6 @@ func (t *Application) Eq(other hm.Type) bool { return false } -func (t *Application) Clone() interface{} { return (*Application)((*Pair)(t).Clone()) } +func (t *Application) Clone() interface{} { return (*Application)((*hm.Pair)(t).Clone()) } -func (t *Application) Pair() *Pair { return (*Pair)(t) } +func (t *Application) Pair() *hm.Pair { return (*hm.Pair)(t) } diff --git a/types/perf.go b/types/perf.go index e682236..5902304 100644 --- a/types/perf.go +++ b/types/perf.go @@ -1,20 +1,13 @@ package hmtypes import ( - "sync" "unsafe" -) - -var pairPool = &sync.Pool{ - New: func() interface{} { return new(Pair) }, -} -func borrowPair() *Pair { - return pairPool.Get().(*Pair) -} + "github.com/chewxy/hm" +) func borrowFn() *Function { - got := pairPool.Get().(*Pair) + got := hm.BorrowPair() return (*Function)(unsafe.Pointer(got)) } @@ -30,6 +23,6 @@ func ReturnFn(fnt *Function) { fnt.A = nil fnt.B = nil - p := (*Pair)(unsafe.Pointer(fnt)) - pairPool.Put(p) + p := (*hm.Pair)(unsafe.Pointer(fnt)) + hm.ReturnPair(p) } From 2560c93009df3ba05a94ee1a8ec6156e0564d126 Mon Sep 17 00:00:00 2001 From: chewxy Date: Fri, 19 Jan 2018 21:14:30 +1100 Subject: [PATCH 14/21] Updated formatting of scheme and normalization that doesn't use letters --- scheme.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scheme.go b/scheme.go index 23aface..c135261 100644 --- a/scheme.go +++ b/scheme.go @@ -52,6 +52,10 @@ func (s *Scheme) Clone() *Scheme { } func (s *Scheme) Format(state fmt.State, c rune) { + if s == nil { + state.Write([]byte("∀[Nil]")) + return + } state.Write([]byte("∀[")) for i, tv := range s.tvs { if i < len(s.tvs)-1 { @@ -82,7 +86,7 @@ func (s *Scheme) Normalize() (err error) { defer ReturnTypeVarSet(tfv) ord := BorrowTypeVarSet(len(tfv)) for i := range tfv { - ord[i] = TypeVariable(letters[i]) + ord[i] = TypeVariable('a' + i) } s.t, err = s.t.Normalize(tfv, ord) From be6073e414721d23469e93ef25754131bfe79059 Mon Sep 17 00:00:00 2001 From: chewxy Date: Fri, 19 Jan 2018 22:49:53 +1100 Subject: [PATCH 15/21] Small bug in solve --- solver.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/solver.go b/solver.go index cc5887f..ca92baf 100644 --- a/solver.go +++ b/solver.go @@ -24,11 +24,12 @@ func (s *solver) solve(cs Constraints) { default: var sub Subs c := cs[0] - sub, s.err = Unify(c.A, c.B) + sub, s.err = Unify(c.B, c.A) defer ReturnSubs(s.sub) s.sub = Compose(sub, s.sub) cs = cs[1:].Apply(s.sub).(Constraints) + s.solve(cs) } From 952b1cd60764d0c9387f3c8b7ed5abb1a3bfddf2 Mon Sep 17 00:00:00 2001 From: chewxy Date: Fri, 19 Jan 2018 22:57:46 +1100 Subject: [PATCH 16/21] Added a test for the subtle solver unification bug --- solver_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/solver_test.go b/solver_test.go index d206fce..71f6f0a 100644 --- a/solver_test.go +++ b/solver_test.go @@ -38,6 +38,27 @@ var solverTest = []struct { }, mSubs{'a': neutron, 'b': proton}, false, }, + + // (a -> a) and (b -> c) + { + Constraints{ + { + NewFnType(TypeVariable('a'), TypeVariable('a')), + NewFnType(TypeVariable('b'), TypeVariable('c')), + }, + }, + mSubs{'b': TypeVariable('a'), 'c': TypeVariable('a')}, false, + }, + + { + Constraints{ + { + NewFnType(TypeVariable('b'), TypeVariable('c')), + NewFnType(TypeVariable('a'), TypeVariable('a')), + }, + }, + mSubs{'b': TypeVariable('c'), 'a': TypeVariable('b')}, false, + }, } func TestSolver(t *testing.T) { From e45c0a96963a1cdf4190755df1e95260b2520c48 Mon Sep 17 00:00:00 2001 From: chewxy Date: Sat, 20 Jan 2018 07:08:34 +1100 Subject: [PATCH 17/21] flipped the order to make it easier to remember according to mnemonic --- hm.go | 2 +- solver.go | 2 +- solver_test.go | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/hm.go b/hm.go index 39098c9..ef03f7a 100644 --- a/hm.go +++ b/hm.go @@ -99,7 +99,7 @@ func (infer *inferer) consGen(expr Expression) (err error) { tv := infer.Fresh() cs := append(fnCs, bodyCs...) - cs = append(cs, Constraint{fnType, NewFnType(bodyType, tv)}) + cs = append(cs, Constraint{NewFnType(bodyType, tv), fnType}) infer.t = tv infer.cs = cs diff --git a/solver.go b/solver.go index ca92baf..8bea50e 100644 --- a/solver.go +++ b/solver.go @@ -24,7 +24,7 @@ func (s *solver) solve(cs Constraints) { default: var sub Subs c := cs[0] - sub, s.err = Unify(c.B, c.A) + sub, s.err = Unify(c.A, c.B) defer ReturnSubs(s.sub) s.sub = Compose(sub, s.sub) diff --git a/solver_test.go b/solver_test.go index 71f6f0a..12a1a17 100644 --- a/solver_test.go +++ b/solver_test.go @@ -43,18 +43,17 @@ var solverTest = []struct { { Constraints{ { - NewFnType(TypeVariable('a'), TypeVariable('a')), NewFnType(TypeVariable('b'), TypeVariable('c')), + NewFnType(TypeVariable('a'), TypeVariable('a')), }, }, mSubs{'b': TypeVariable('a'), 'c': TypeVariable('a')}, false, }, - { Constraints{ { - NewFnType(TypeVariable('b'), TypeVariable('c')), NewFnType(TypeVariable('a'), TypeVariable('a')), + NewFnType(TypeVariable('b'), TypeVariable('c')), }, }, mSubs{'b': TypeVariable('c'), 'a': TypeVariable('b')}, false, From 284b08a05270c0b64ae6128b269853fe0461f22b Mon Sep 17 00:00:00 2001 From: chewxy Date: Wed, 31 Jan 2018 10:14:56 +1100 Subject: [PATCH 18/21] Added nicer formatting for Scheme --- scheme.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scheme.go b/scheme.go index 23aface..e78971c 100644 --- a/scheme.go +++ b/scheme.go @@ -52,6 +52,11 @@ func (s *Scheme) Clone() *Scheme { } func (s *Scheme) Format(state fmt.State, c rune) { + if s == nil { + state.Write("∀[∅].∅") + return + } + state.Write([]byte("∀[")) for i, tv := range s.tvs { if i < len(s.tvs)-1 { From ed7209c437e23a9a88869b526f847e7a204bd167 Mon Sep 17 00:00:00 2001 From: chewxy Date: Wed, 7 Feb 2018 16:33:38 +1100 Subject: [PATCH 19/21] Fix a bug in function types --- scheme.go | 2 +- types/function.go | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/scheme.go b/scheme.go index c91ed60..615ae4c 100644 --- a/scheme.go +++ b/scheme.go @@ -53,7 +53,7 @@ func (s *Scheme) Clone() *Scheme { func (s *Scheme) Format(state fmt.State, c rune) { if s == nil { - state.Write([]byte*"∀[∅].∅") + state.Write([]byte("∀[∅].∅")) return } diff --git a/types/function.go b/types/function.go index 217186e..e68f21b 100644 --- a/types/function.go +++ b/types/function.go @@ -29,11 +29,16 @@ func NewFunction(ts ...hm.Type) *Function { return retVal } -func (t *Function) Name() string { return "→" } -func (t *Function) Apply(sub hm.Subs) hm.Substitutable { ((*hm.Pair)(t)).Apply(sub); return t } -func (t *Function) FreeTypeVar() hm.TypeVarSet { return ((*hm.Pair)(t)).FreeTypeVar() } -func (t *Function) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v → %v", t.A, t.B) } -func (t *Function) String() string { return fmt.Sprintf("%v", t) } +func (t *Function) Name() string { return "→" } +func (t *Function) Apply(sub hm.Subs) hm.Substitutable { + p := (*hm.Pair)(t) + retVal := p.Clone() + retVal.Apply(sub) + return (*Function)(retVal) +} +func (t *Function) FreeTypeVar() hm.TypeVarSet { return ((*hm.Pair)(t)).FreeTypeVar() } +func (t *Function) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v → %v", t.A, t.B) } +func (t *Function) String() string { return fmt.Sprintf("%v", t) } func (t *Function) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { var a, b hm.Type var err error From 6554af6b96745cb531b346f922db57e23a79f9e2 Mon Sep 17 00:00:00 2001 From: chewxy Date: Wed, 7 Feb 2018 17:06:29 +1100 Subject: [PATCH 20/21] Fixed up the Pair.Apply --- struct.go | 9 ++++++++- types/function.go | 15 +++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/struct.go b/struct.go index eb4adc7..0b93860 100644 --- a/struct.go +++ b/struct.go @@ -15,7 +15,14 @@ type Pair struct { } // Apply applies a substitution on both the first and second types of the Pair. -func (t *Pair) Apply(sub Subs) { +func (t *Pair) Apply(sub Subs) *Pair { + retVal := t.Clone() + retVal.UnsafeApply(sub) + return retVal +} + +// UnsafeApply is an unsafe application of the substitution. +func (t *Pair) UnsafeApply(sub Subs) { t.A = t.A.Apply(sub).(Type) t.B = t.B.Apply(sub).(Type) } diff --git a/types/function.go b/types/function.go index e68f21b..27d3942 100644 --- a/types/function.go +++ b/types/function.go @@ -29,16 +29,11 @@ func NewFunction(ts ...hm.Type) *Function { return retVal } -func (t *Function) Name() string { return "→" } -func (t *Function) Apply(sub hm.Subs) hm.Substitutable { - p := (*hm.Pair)(t) - retVal := p.Clone() - retVal.Apply(sub) - return (*Function)(retVal) -} -func (t *Function) FreeTypeVar() hm.TypeVarSet { return ((*hm.Pair)(t)).FreeTypeVar() } -func (t *Function) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v → %v", t.A, t.B) } -func (t *Function) String() string { return fmt.Sprintf("%v", t) } +func (t *Function) Name() string { return "→" } +func (t *Function) Apply(sub hm.Subs) hm.Substitutable { return (*Function)((*hm.Pair)(t).Apply(sub)) } +func (t *Function) FreeTypeVar() hm.TypeVarSet { return ((*hm.Pair)(t)).FreeTypeVar() } +func (t *Function) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%v → %v", t.A, t.B) } +func (t *Function) String() string { return fmt.Sprintf("%v", t) } func (t *Function) Normalize(k, v hm.TypeVarSet) (hm.Type, error) { var a, b hm.Type var err error From 82062404ad5f2614a4b17da1d4c0a7c464e667f4 Mon Sep 17 00:00:00 2001 From: chewxy Date: Thu, 8 Mar 2018 12:03:07 +1100 Subject: [PATCH 21/21] Fixed bug. --- constraint.go | 2 +- debug.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/constraint.go b/constraint.go index a29a923..97c5067 100644 --- a/constraint.go +++ b/constraint.go @@ -5,6 +5,6 @@ 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 Pair -func (c Constraint) Apply(sub Subs) Substitutable { (*Pair)(&c).Apply(sub); return c } +func (c Constraint) Apply(sub Subs) Substitutable { return Constraint(*(*Pair)(&c).Apply(sub)) } func (c Constraint) FreeTypeVar() TypeVarSet { return Pair(c).FreeTypeVar() } func (c Constraint) Format(state fmt.State, r rune) { fmt.Fprintf(state, "{%v = %v}", c.A, c.B) } diff --git a/debug.go b/debug.go index 76fe63c..cbd4fe4 100644 --- a/debug.go +++ b/debug.go @@ -11,7 +11,7 @@ import ( ) // DEBUG returns true when it's in debug mode -const DEBUG = false +const DEBUG = true var tabcount uint32