-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
636 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package hmtypes | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/chewxy/hm" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestFunctionTypeBasics(t *testing.T) { | ||
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()) | ||
} | ||
|
||
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(NewFunction(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 = NewFunction(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).(*Function) | ||
if !fn.Eq(fas.expected) { | ||
t.Errorf("Expected %v. Got %v instead", fas.expected, fn) | ||
} | ||
} | ||
|
||
// bad shit | ||
f := func() { | ||
NewFunction(hm.TypeVariable('a')) | ||
} | ||
assert.Panics(t, f) | ||
} | ||
|
||
var fnApplyTests = []struct { | ||
fn *Function | ||
sub hm.Subs | ||
|
||
expected *Function | ||
}{ | ||
{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) | ||
{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 | ||
{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 := 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 := 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 := 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')) | ||
fnType = NewFunction(rec, rec) | ||
assert.Equal(t, fnType.Clone(), fnType) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package hmtypes | ||
|
||
type Cloner interface { | ||
Clone() interface{} | ||
} |
Oops, something went wrong.