Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

V1.1.0 working #2

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
374f355
Added function to create Subs
chewxy Dec 27, 2017
dfec7d2
Added a built-in types package, basically refactoring out bits and pi…
chewxy Dec 27, 2017
6b26671
Updated variable naming scheme
chewxy Dec 27, 2017
f3d6673
Added Monuples
chewxy Dec 27, 2017
0317cce
Added pair types and quantified types
chewxy Dec 28, 2017
71799ab
Added Normalize to the monuples
chewxy Dec 28, 2017
cc8b3e0
exported a few internal functions - they're useful in other unificati…
chewxy Dec 28, 2017
88fd6bd
Added Application, added comments to MakeSub
chewxy Dec 28, 2017
44a2cf0
Implemented Type for the Pair types. Except for Normalize
chewxy Dec 28, 2017
0327ec5
Added a few methods and a couple of interfaces for Monuples and Pairs
chewxy Jan 4, 2018
ff24766
Added the separation between Tuple and Record
chewxy Jan 11, 2018
1457e7f
forgot to save
chewxy Jan 11, 2018
5162b54
Moved Pairs and Monuples back into package hm
chewxy Jan 12, 2018
2560c93
Updated formatting of scheme and normalization that doesn't use letters
chewxy Jan 19, 2018
be6073e
Small bug in solve
chewxy Jan 19, 2018
952b1cd
Added a test for the subtle solver unification bug
chewxy Jan 19, 2018
e45c0a9
flipped the order to make it easier to remember according to mnemonic
chewxy Jan 19, 2018
284b08a
Added nicer formatting for Scheme
chewxy Jan 30, 2018
78fa444
Merge remote-tracking branch 'origin/v1.1.0-working' into v1.1.0-working
chewxy Jan 30, 2018
ed7209c
Fix a bug in function types
chewxy Feb 7, 2018
6554af6
Fixed up the Pair.Apply
chewxy Feb 7, 2018
8206240
Fixed bug.
chewxy Mar 8, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 4 additions & 19 deletions constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 { 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) }
8 changes: 4 additions & 4 deletions constraint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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)
}
}
2 changes: 1 addition & 1 deletion debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

// DEBUG returns true when it's in debug mode
const DEBUG = false
const DEBUG = true

var tabcount uint32

Expand Down
16 changes: 9 additions & 7 deletions hm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)

Expand Down
20 changes: 19 additions & 1 deletion perf.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package hm

import "sync"
import (
"sync"
)

const (
poolSize = 4
Expand Down Expand Up @@ -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)
}
7 changes: 6 additions & 1 deletion scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ func (s *Scheme) Clone() *Scheme {
}

func (s *Scheme) Format(state fmt.State, c rune) {
if s == nil {
state.Write([]byte("∀[∅].∅"))
return
}

state.Write([]byte("∀["))
for i, tv := range s.tvs {
if i < len(s.tvs)-1 {
Expand Down Expand Up @@ -82,7 +87,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)
Expand Down
5 changes: 3 additions & 2 deletions solver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.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)

}
Expand Down
20 changes: 20 additions & 0 deletions solver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ var solverTest = []struct {
},
mSubs{'a': neutron, 'b': proton}, false,
},

// (a -> a) and (b -> c)
{
Constraints{
{
NewFnType(TypeVariable('b'), TypeVariable('c')),
NewFnType(TypeVariable('a'), TypeVariable('a')),
},
},
mSubs{'b': TypeVariable('a'), 'c': TypeVariable('a')}, false,
},
{
Constraints{
{
NewFnType(TypeVariable('a'), TypeVariable('a')),
NewFnType(TypeVariable('b'), TypeVariable('c')),
},
},
mSubs{'b': TypeVariable('c'), 'a': TypeVariable('b')}, false,
},
}

func TestSolver(t *testing.T) {
Expand Down
97 changes: 97 additions & 0 deletions struct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package 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 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 Type
}

// Apply applies a substitution on both the first and second types of the Pair.
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)
}

// Types returns all the types of the Pair's constituents
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() 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().(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
}

// Monuple is a convenient structural abstraction for types that are composed of one type.
//
// 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 Type
}

// Apply applies a substitution to the monuple 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() TypeVarSet { return t.T.FreeTypeVar() }

// Normalize is the method to normalize all type variables
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
}
t.T = t2
return t, nil
}

// Pairer is any type that can be represented by a Pair
type Pairer interface {
Type
AsPair() *Pair
}

// Monupler is any type that can be represented by a Monuple
type Monupler interface {
Type
AsMonuple() Monuple
}
8 changes: 4 additions & 4 deletions substitutables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

Expand Down
12 changes: 11 additions & 1 deletion substitutions.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ 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)
}
return newSliceSubs(n)
}

// A Substitution is a tuple representing the TypeVariable and the replacement Type
type Substitution struct {
Tv TypeVariable
Expand Down Expand Up @@ -116,7 +125,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
}
Expand Down
2 changes: 1 addition & 1 deletion substitutions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading