Add cmp/internal/function package (#35)

Unify the common logic for identifying the type of function.
This logic is helpful since Go lacks generics, so it is hard
to query whether a function is of the signature: func(T, T) bool
diff --git a/cmp/cmpopts/sort.go b/cmp/cmpopts/sort.go
index e155467..a566d24 100644
--- a/cmp/cmpopts/sort.go
+++ b/cmp/cmpopts/sort.go
@@ -9,6 +9,7 @@
 	"reflect"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/internal/function"
 )
 
 // SortSlices returns a Transformer option that sorts all []V.
@@ -26,7 +27,7 @@
 // SortSlices can be used in conjuction with EquateEmpty.
 func SortSlices(less interface{}) cmp.Option {
 	vf := reflect.ValueOf(less)
-	if !isTTBoolFunc(vf.Type()) || vf.IsNil() {
+	if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
 		panic(fmt.Sprintf("invalid less function: %T", less))
 	}
 	ss := sliceSorter{vf.Type().In(0), vf}
@@ -97,7 +98,7 @@
 // SortMaps can be used in conjuction with EquateEmpty.
 func SortMaps(less interface{}) cmp.Option {
 	vf := reflect.ValueOf(less)
-	if !isTTBoolFunc(vf.Type()) || vf.IsNil() {
+	if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
 		panic(fmt.Sprintf("invalid less function: %T", less))
 	}
 	ms := mapSorter{vf.Type().In(0), vf}
@@ -143,13 +144,3 @@
 	}
 	return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool()
 }
-
-var boolType = reflect.TypeOf(true)
-
-// isTTBoolFunc reports whether f is of the form: func(T, T) bool.
-func isTTBoolFunc(t reflect.Type) bool {
-	if t == nil || t.Kind() != reflect.Func || t.IsVariadic() {
-		return false
-	}
-	return t.NumIn() == 2 && t.NumOut() == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType
-}
diff --git a/cmp/compare.go b/cmp/compare.go
index 77b9269..177fc10 100644
--- a/cmp/compare.go
+++ b/cmp/compare.go
@@ -31,6 +31,7 @@
 	"reflect"
 
 	"github.com/google/go-cmp/cmp/internal/diff"
+	"github.com/google/go-cmp/cmp/internal/function"
 	"github.com/google/go-cmp/cmp/internal/value"
 )
 
@@ -372,8 +373,7 @@
 func (s *state) tryMethod(vx, vy reflect.Value, t reflect.Type) bool {
 	// Check if this type even has an Equal method.
 	m, ok := t.MethodByName("Equal")
-	ft := functionType(m.Type)
-	if !ok || (ft != equalFunc && ft != equalIfaceFunc) {
+	if !ok || !function.IsType(m.Type, function.EqualAssignable) {
 		return false
 	}
 
@@ -588,33 +588,3 @@
 	vc.Set(v)
 	return vc
 }
-
-type funcType int
-
-const (
-	invalidFunc     funcType    = iota
-	equalFunc                   // func(T, T) bool
-	equalIfaceFunc              // func(T, I) bool
-	transformFunc               // func(T) R
-	valueFilterFunc = equalFunc // func(T, T) bool
-)
-
-var boolType = reflect.TypeOf(true)
-
-// functionType identifies which type of function signature this is.
-func functionType(t reflect.Type) funcType {
-	if t == nil || t.Kind() != reflect.Func || t.IsVariadic() {
-		return invalidFunc
-	}
-	ni, no := t.NumIn(), t.NumOut()
-	switch {
-	case ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType:
-		return equalFunc // or valueFilterFunc
-	case ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType:
-		return equalIfaceFunc
-	case ni == 1 && no == 1:
-		return transformFunc
-	default:
-		return invalidFunc
-	}
-}
diff --git a/cmp/internal/function/func.go b/cmp/internal/function/func.go
new file mode 100644
index 0000000..4c35ff1
--- /dev/null
+++ b/cmp/internal/function/func.go
@@ -0,0 +1,49 @@
+// Copyright 2017, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+// Package function identifies function types.
+package function
+
+import "reflect"
+
+type funcType int
+
+const (
+	_ funcType = iota
+
+	ttbFunc // func(T, T) bool
+	tibFunc // func(T, I) bool
+	trFunc  // func(T) R
+
+	Equal           = ttbFunc // func(T, T) bool
+	EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool
+	Transformer     = trFunc  // func(T) R
+	ValueFilter     = ttbFunc // func(T, T) bool
+	Less            = ttbFunc // func(T, T) bool
+)
+
+var boolType = reflect.TypeOf(true)
+
+// IsType reports whether the reflect.Type is of the specified function type.
+func IsType(t reflect.Type, ft funcType) bool {
+	if t == nil || t.Kind() != reflect.Func || t.IsVariadic() {
+		return false
+	}
+	ni, no := t.NumIn(), t.NumOut()
+	switch ft {
+	case ttbFunc: // func(T, T) bool
+		if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType {
+			return true
+		}
+	case tibFunc: // func(T, I) bool
+		if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType {
+			return true
+		}
+	case trFunc: // func(T) R
+		if ni == 1 && no == 1 {
+			return true
+		}
+	}
+	return false
+}
diff --git a/cmp/options.go b/cmp/options.go
index 7b65baa..0498a55 100644
--- a/cmp/options.go
+++ b/cmp/options.go
@@ -9,6 +9,8 @@
 	"reflect"
 	"runtime"
 	"strings"
+
+	"github.com/google/go-cmp/cmp/internal/function"
 )
 
 // Option configures for specific behavior of Equal and Diff. In particular,
@@ -150,7 +152,7 @@
 // a previously filtered Option.
 func FilterValues(f interface{}, opt Option) Option {
 	v := reflect.ValueOf(f)
-	if functionType(v.Type()) != valueFilterFunc || v.IsNil() {
+	if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() {
 		panic(fmt.Sprintf("invalid values filter function: %T", f))
 	}
 	switch opt := opt.(type) {
@@ -191,7 +193,7 @@
 // transformation PathStep. If empty, an arbitrary name is used.
 func Transformer(name string, f interface{}) Option {
 	v := reflect.ValueOf(f)
-	if functionType(v.Type()) != transformFunc || v.IsNil() {
+	if !function.IsType(v.Type(), function.Transformer) || v.IsNil() {
 		panic(fmt.Sprintf("invalid transformer function: %T", f))
 	}
 	if name == "" {
@@ -226,7 +228,7 @@
 //	• Pure: equal(x, y) does not modify x or y
 func Comparer(f interface{}) Option {
 	v := reflect.ValueOf(f)
-	if functionType(v.Type()) != equalFunc || v.IsNil() {
+	if !function.IsType(v.Type(), function.Equal) || v.IsNil() {
 		panic(fmt.Sprintf("invalid comparer function: %T", f))
 	}
 	opt := option{op: &comparer{v}}