dyn

package module
v0.0.0-...-75977ad Latest Latest
Warning

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

Go to latest
Published: Dec 19, 2023 License: 0BSD Imports: 3 Imported by: 44

README

Dyn

GoDoc Build Status

This package provides helpers for late binding of functions and methods for the go programming language. It encapsulates the reflection library in a nicer interface and allows generic behavior over a wide range of types. It is not compile time type safe and therefore should be used only when necessary.

All the functions allow for types to implement mechanisms to override go's default semantics. As one will see in the provided examples this is rather powerful.

Getting started

go get jsouthworth.net/go/dyn

Usage

The full documentation is available at jsouthworth.net/go/dyn

License

This project is licensed under the 0BSD License - see LICENSE

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

Examples

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.

type Tuple

type Tuple []interface{}

Tuple is the return type for multiple return functions. This is used to differentiate between a function returning []interface{} and one where the library creates a []interface{} for the returned arguments.

Jump to

Keyboard shortcuts

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