Skip to content

Commit

Permalink
Merge 0327ec5 into 61efb32
Browse files Browse the repository at this point in the history
  • Loading branch information
chewxy committed Jan 4, 2018
2 parents 61efb32 + 0327ec5 commit 23a6a4d
Show file tree
Hide file tree
Showing 17 changed files with 754 additions and 18 deletions.
14 changes: 8 additions & 6 deletions hm.go
Original file line number Diff line number Diff line change
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
2 changes: 1 addition & 1 deletion solver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

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
16 changes: 11 additions & 5 deletions typeVariable.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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", rune(t))
return
}
fmt.Fprintf(s, "<%d>", rune(t))
}
func (t TypeVariable) Eq(other Type) bool { return other == t }
90 changes: 90 additions & 0 deletions types/commonutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
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.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
}

// 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)
}

// 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()) }

// 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 {
T hm.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)
return t
}

// FreeTypeVar returns the set of free type variables in the monuple.
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 Monuple{}, err
}
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
}
105 changes: 105 additions & 0 deletions types/function.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package hmtypes

import (
"fmt"

"github.com/chewxy/hm"
)

// Function is a type constructor that builds function types.
type Function Pair

// NewFunction creates a new FunctionType. Functions are by default right associative. This:
// NewFunction(a, a, a)
// is short hand for this:
// NewFunction(a, NewFunction(a, a))
func NewFunction(ts ...hm.Type) *Function {
if len(ts) < 2 {
panic("Expected at least 2 input types")
}

retVal := borrowFn()
retVal.A = ts[0]

if len(ts) > 2 {
retVal.B = NewFunction(ts[1:]...)
} else {
retVal.B = ts[1]
}
return retVal
}

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 {
return nil, err
}

if b, err = t.B.Normalize(k, v); err != nil {
return nil, err
}

return NewFunction(a, b), nil
}
func (t *Function) Types() hm.Types { return ((*Pair)(t)).Types() }

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
}

// Other methods (accessors mainly)

// Arg returns the type of the function argument
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 *Function) Ret(recursive bool) hm.Type {
if !recursive {
return t.B
}

if fnt, ok := t.B.(*Function); 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 *Function) FlatTypes() hm.Types {
retVal := hm.BorrowTypes(8) // start with 8. Can always grow
retVal = retVal[:0]

if a, ok := t.A.(*Function); ok {
ft := a.FlatTypes()
retVal = append(retVal, ft...)
hm.ReturnTypes(ft)
} else {
retVal = append(retVal, t.A)
}

if b, ok := t.B.(*Function); ok {
ft := b.FlatTypes()
retVal = append(retVal, ft...)
hm.ReturnTypes(ft)
} else {
retVal = append(retVal, t.B)
}
return retVal
}

// Clone implenents cloner
func (t *Function) Clone() interface{} {
p := (*Pair)(t)
cloned := p.Clone()
return (*Function)(cloned)
}
Loading

0 comments on commit 23a6a4d

Please sign in to comment.