cn

package
v0.0.0-...-2d5dbfb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2021 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AssertType

func AssertType(v interface{}, t reflect.Type) reflect.Value

AssertType panics with a `TypeError` if `v` does not have type `t`. Otherwise, it returns the `reflect.Value` of `v`.

Types

type A

type A TypeVariable

type B

type B TypeVariable

type C

type C TypeVariable

type D

type D TypeVariable

type E

type E TypeVariable

type F

type F TypeVariable

type G

type G TypeVariable

type TypeError

type TypeError string

TypeError corresponds to any error reported by the `Check` function. Since `Check` panics, if you want to run `Check` safely, it is appropriate to recover and use a type switch to discover a `TypeError` value.

func (TypeError) Error

func (te TypeError) Error() string

type TypeVariable

type TypeVariable struct {
	// contains filtered or unexported fields
}

TypeVariable is the underlying type of every type variable used in parametric types. It should not be used directly. Instead, use

type myOwnTypeVariable TypeVariable

to create your own type variable. For your convenience, this package defines some type variables for you. (e.g., `A`, `B`, `C`, ...)

type Typed

type Typed struct {
	// In correspondence with the `as` parameter to `Check`.
	Args []reflect.Value

	// In correspondence with the return types of `f` in `Check`.
	Returns []reflect.Type

	// The type environment generated via unification in `Check`.
	// (Its usefulness in the public API is questionable.)
	TypeEnv map[string]reflect.Type
}

Typed corresponds to the information returned by `Check`.

func Check

func Check(f interface{}, as ...interface{}) *Typed

Check accepts a function `f`, which may have a parametric type, along with a number of arguments in correspondence with the arguments to `f`, and returns inferred Go type information. This type information includes a list of `reflect.Value` in correspondence with `as`, a list of `reflect.Type` in correspondence with the return types of `f` and a type environment mapping type variables to `reflect.Type`.

The power of `Check` comes from the following invariant: if `Check` returns, then the types of the arguments corresponding to `as` are consistent with the parametric type of `f`, *and* the parametric return types of `f` were made into valid Go types that are not parametric. Otherwise, there is a bug in `Check`.

More concretely, consider a simple parametric function `Map`, which transforms a list of elements by applying a function to each element in order to generate a new list. Such a function constructed only for integers might have a type like

func Map(func(int) int, []int) []int

But the parametric type of `Map` could be given with

func Map(func(A) B, []A) []B

which in English reads, "Given a function from any type `A` to any type `B` and a slice of `A`, `Map` returns a slice of `B`."

To write a parametric function like `Map`, one can pass a pointer to a nil function of the desired parametric type to get the reflection information:

func Map(f, xs interface{}) interface{} {
	// Given the parametric type and the arguments, Check will
	// return all the reflection information you need to write `Map`.
	uni := ty.Check(
		new(func(func(ty.A) ty.B, []ty.A) []ty.B),
		f, xs)

	// `vf` and `vxs` are `reflect.Value`s of `f` and `xs`.
	vf, vxs := uni.Args[0], uni.Args[1]

	// `tys` is a `reflect.Type` of `[]ty.B` where `ty.B` is replaced
	// with the return type of the given function `f`.
	tys := uni.Returns[0]

	// Given the promise of `Check`, we now know that `vf` has
	// type `func(ty.A) ty.B` and `vxs` has type `[]ty.A`.
	xsLen := vxs.Len()

	// Constructs a new slice which will have type `[]ty.B`.
	vys := reflect.MakeSlice(tys, xsLen, xsLen)

	// Actually perform the `Map` operation, but in the world of
	// reflection.
	for i := 0; i < xsLen; i++ {
		vy := vf.Call([]reflect.Value{vxs.Index(i)})[0]
		vys.Index(i).Set(vy)
	}

	// The `reflect.Value.Interface` method is how we exit the world of
	// reflection. The onus is now on the caller to type assert it to
	// the appropriate type.
	return vys.Interface()
}

Working in the reflection world is certainly more inconvenient than writing regular Go code, but the information and invariants held by `Check` provide a more convenient experience than how one normally works with reflection. (Notice that there is no error-prone type switching or boiler plate to construct new types, since `Check` guarantees the types are consistent with the inputs for us.)

And while writing such functions is still not so convenient, invoking them is simple:

square := func(x int) int { return x * x }
squared := Map(square, []int{1, 2, 3, 4, 5}).([]int)

Restrictions

There are a few restrictions imposed on the parametric return types of `f`: type variables may only be found in types that can be composed by the `reflect` package. This *only* includes channels, maps, pointers and slices. If a type variable is found in an array, function or struct, `Check` will panic.

Also, type variables inside of structs are ignored in the types of the arguments `as`. This restriction may be lifted in the future.

To be clear: type variables *may* appear in arrays or functions in the types of the arguments `as`.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL