Documentation ¶
Overview ¶
Package dyn provides late binding helpers for go. It uses reflection to implement these functions and provides interfaces that a user may use to override the default go behaviors. This allows for one to build late bound language extensions for go.
Index ¶
- func Apply(f interface{}, args ...interface{}) interface{}
- func At(assocObj interface{}, selector interface{}) interface{}
- func Bind(fn interface{}, args ...interface{}) func() interface{}
- func Compare(one, two interface{}) int
- func Compose(fns ...interface{}) interface{}
- func DoesNotUnderstand(o interface{}, message ...interface{}) error
- func Equal(one, two interface{}) bool
- func EqualNonComparable(one, two interface{}) bool
- func Find(assocObj interface{}, selector interface{}) (interface{}, bool)
- func PrependArg(arg interface{}, args ...interface{}) []interface{}
- func Send(rcvr interface{}, message ...interface{}) interface{}
- type Applier
- type Comparer
- type Equaler
- type ErrDoesNotUnderstand
- type Finder
- type MessageReceiver
- type Tuple
Examples ¶
- Apply
- Apply (Applier)
- Apply (Map)
- Apply (MultipleArgs)
- Apply (MultipleReturns)
- Apply (NilArg)
- Apply (NoReturn)
- Apply (Slice)
- Apply (Struct)
- Apply (Variadic)
- At (Map)
- At (Slice)
- At (Struct)
- Compare (Comparer)
- Compare (GoNative)
- Compose
- Equal (Equaler)
- Equal (GoNative)
- Equal (OneEqualer)
- PrependArg
- Send
- Send (Inheritance)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
func Apply(f interface{}, args ...interface{}) interface{}
Apply will apply the type with the supplied arguments. If the type is an Applier then Apply will be called. Otherwise if the type is a go function type then it will be called with reflect in the standard way. If Apply is called with a map or a slice, the arguments are treated as a selector and the selector is looked up in the collection. This makes maps behave like declarative functions. Any other type will panic.
Example ¶
fmt.Println(Apply(func(x int) int { return x * x }, 10))
Output: 100
Example (Applier) ¶
fmt.Println(Apply(annotate{name: "square", fn: func(x int) int { return x * x }}, 10))
Output: square([10])->100
Example (Map) ¶
fmt.Println(Apply(map[string]int{"a": 10, "b": 20, "c": 30}, "a"))
Output: 10
Example (MultipleArgs) ¶
fmt.Println(Apply(func(x int, y string) string { return fmt.Sprintf("%s:%d", y, x) }, 10, "foo"))
Output: foo:10
Example (MultipleReturns) ¶
out := Apply(func(x int) (int, int) { return x, x + 1 }, 10) fmt.Println(At(out, 0), At(out, 1))
Output: 10 11
Example (NilArg) ¶
fmt.Println(Apply(func(err error) error { if err == nil { return errors.New("oops!") } return err }, nil))
Output: oops!
Example (NoReturn) ¶
fmt.Println(Apply(func(x int) { fmt.Println(x) }, 10))
Output: 10 <nil>
Example (Slice) ¶
fmt.Println(Apply([]string{"foo", "bar", "baz"}, 1))
Output: bar
Example (Struct) ¶
type example struct { Foo, Bar, Baz string } fmt.Println(Apply(example{"foo", "bar", "baz"}, "Foo")) fmt.Println(Apply(example{"foo", "bar", "baz"}, 1))
Output: foo bar
Example (Variadic) ¶
fn := func(args ...interface{}) interface{} { fmt.Println(args...) return nil } Apply(fn, "foo", "bar", "baz")
Output: foo bar baz
func At ¶
func At(assocObj interface{}, selector interface{}) interface{}
At uses Find to retieve an object but ignores whether the value was found.
Example (Map) ¶
fmt.Println(At(map[string]string{"foo": "hello"}, "foo"))
Output: hello
Example (Slice) ¶
fmt.Println(At([]string{"hello"}, 0))
Output: hello
Example (Struct) ¶
type test struct { Foo string } fmt.Println(At(&test{Foo: "hello1"}, "Foo")) fmt.Println(At(test{Foo: "hello2"}, "Foo")) fmt.Println(At(test{Foo: "hello3"}, 0))
Output: hello1 hello2 hello3
func Bind ¶
func Bind(fn interface{}, args ...interface{}) func() interface{}
Bind will create a context in which the function application is deferrred. When the returned context is called the function is applied and the result returned.
func Compare ¶
func Compare(one, two interface{}) int
Compare compares all native comparable go types and any type that is a Comparer. It returns a negative value if one < two, a positive value if one > two or zero if they are equivalent.
Example (Comparer) ¶
fmt.Println(Compare(invertedCompare(1), invertedCompare(2)))
Output: 1
Example (GoNative) ¶
fmt.Println(Compare("a", "b"))
Output: -1
func Compose ¶
func Compose(fns ...interface{}) interface{}
Compose implements generic function composition Compose(f, g)(x) is equivalent to f(g(x))
Example ¶
square := func(x int) int { return x * x } negate := func(x int) int { return -x } fmt.Println(Apply(Compose(negate, square), 5))
Output: -25
func DoesNotUnderstand ¶
func DoesNotUnderstand(o interface{}, message ...interface{}) error
DoesNotUnderstand is a constructor for an ErrDoesNotUnderstand error.
func Equal ¶
func Equal(one, two interface{}) bool
Equal compares two arbitrary values. If one of the values is an Equaler then Equal is called on that type with the other as the argument. Otherwise go's standard equality operator is used.
Example (Equaler) ¶
a := &equalExample{m: map[string]int{"a": 10, "b": 20, "c": 30}} b := &equalExample{m: map[string]int{"a": 10, "b": 20, "c": 30}} fmt.Println(Equal(a, b))
Output: true
Example (GoNative) ¶
fmt.Println(Equal(1, 2))
Output: false
Example (OneEqualer) ¶
a := &equalExample{m: map[string]int{"a": 10, "b": 20, "c": 30}} b := map[string]int{"a": 10, "b": 20, "c": 30} fmt.Println(Equal(b, a))
Output: false
func EqualNonComparable ¶
func EqualNonComparable(one, two interface{}) bool
EqualNonComparable is a version of Equal that is safe for non-comparable types. If the type of the value passed in is non-comparable then the function always returns false.
func Find ¶
func Find(assocObj interface{}, selector interface{}) (interface{}, bool)
Find looks up a value in an associative object. If the type of the assocObj is a Finder then Find will be called and the value returned. Otherwise reflection will be used to do a lookup on native go types. If the type is a struct it may be indexed by an integer or a string, any other index type will panic. If the type is a map then the selector will be treated as a key, if the key is of the wrong type then Find will panic. If the type is a slice then the selector must be an int, if the index is in the slice then a value is returned, otherwise nil and false will be returned. If the type is a pointer to any of the above then the pointer will be dereferenced and then the above semantics will hold.
func PrependArg ¶
func PrependArg(arg interface{}, args ...interface{}) []interface{}
PrependArg imprements a common pattern when calling aribitrary functions. Sometimes one needs all the passed in args and one more at the beginning to accomodate this, this function encapsulates the clumsy pattern of append([]interface{arg}, args...).
Example ¶
args := []interface{}{"b", "c", "d", "e"} args = PrependArg("a", args...) fmt.Println(args)
Output: [a b c d e]
func Send ¶
func Send(rcvr interface{}, message ...interface{}) interface{}
Send provides a late binding way to call methods on objects. It abstracts the method call semantics and allows user defined types to implement their own message semantics. Send will send a message to a receiver and return the returned value. If the receiver is a MessageReceiver then Receive will be called. For any other type the go method will be looked up by name based on the first element of message and the method will be applied with the rest of the message.
Example ¶
fmt.Println(Send(&receiver{}, "String"))
Output: rcvr!
Example (Inheritance) ¶
// OK, this is a little perverse but it shows the power // of this stuff... Even this is more fixed than it needs // to be but it is just an example. fooCl := newClass(nil, map[string]interface{}{ "string": func(self *object) interface{} { return At(self, "a") }, "other": func(self *object) interface{} { return Send(self, "string") }, }, []string{"a"}) barCl := newClass(fooCl, map[string]interface{}{ "string": func(self *object) interface{} { return At(self, "b") }, }, []string{"b"}) foo := fooCl.New("foo") fmt.Println(Send(foo, "other")) bar := barCl.New("bar", "quux") fmt.Println(Send(bar, "other"))
Output: foo quux
Types ¶
type Applier ¶
type Applier interface {
Apply(args ...interface{}) interface{}
}
Applier is any type that knows how to apply arguments to its self and return a value.
type Comparer ¶
type Comparer interface {
Compare(other interface{}) int
}
Comparer is any type that knows how to compare its self to another value. Compare must return negative value when the other argument is larger than it is, positive when the other argument is greater, and zero if they are equivalent.
type Equaler ¶
type Equaler interface {
Equal(other interface{}) bool
}
Equaler is a type that knows how to compare its self to another value using the Equal function. If the other value should be considered the same as the value then true must be returned, false otherwise.
type ErrDoesNotUnderstand ¶
type ErrDoesNotUnderstand struct {
// contains filtered or unexported fields
}
ErrDoesNotUnderstand is returned when a method can not be located for a message.
func (ErrDoesNotUnderstand) Error ¶
func (e ErrDoesNotUnderstand) Error() string
type Finder ¶
type Finder interface {
Find(interface{}) (interface{}, bool)
}
Finder is any type that can index its self and return a value it contains and whether a value was at that index.
type MessageReceiver ¶
type MessageReceiver interface {
Receive(message ...interface{}) interface{}
}
MessageReceivers are any object that implements its own messaging semantics.