Skip to content

Commit

Permalink
Merge 8206240 into 61efb32
Browse files Browse the repository at this point in the history
  • Loading branch information
chewxy committed Mar 8, 2018
2 parents 61efb32 + 8206240 commit 4ee989c
Show file tree
Hide file tree
Showing 24 changed files with 915 additions and 50 deletions.
23 changes: 4 additions & 19 deletions constraint.go
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
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
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
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
@@ -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
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
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
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
@@ -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
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
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
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
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

0 comments on commit 4ee989c

Please sign in to comment.