diff --git a/option.go b/option.go index 4719baa..04c68b0 100644 --- a/option.go +++ b/option.go @@ -21,13 +21,23 @@ func (o Option[T]) IsNone() bool { return !o.IsSome() } -// Take a value out of the option if it exists. -func (o Option[T]) Take() (*T, error) { +// Yank will pull a value out of an option, panicking if it doesn't exist. +func (o Option[T]) Yank() T { if o.IsNone() { - return nil, ErrOptionIsNone + panic("gonads: Yank on None Option") + } + + return *o.val +} + +// Take a value out of the option if it exists. +func (o Option[T]) Take() (T, error) { + if o.IsNone() { + var zero T + return zero, ErrOptionIsNone } - return o.val, nil + return *o.val, nil } // Set the value of the option to a given value. diff --git a/thunk.go b/thunk.go new file mode 100644 index 0000000..6a9c97e --- /dev/null +++ b/thunk.go @@ -0,0 +1,31 @@ +package gonads + +// Thunk contains a partially computed value. This thunk is also lazy because +// it will only run the underlying thunked action _once_ and then return the +// result of that action, caching it for further use. +// +// If this is confusing to you, consider the following JavaScript: +// +// let add = (x, y) => x + y; +// let addCurr = (x) => (y) => x + y; +// console.log(add(2, 2)); // 4 +// console.log(addCurr(2)(2)); // 4 +// let addTwo = addCurr(2); // (y) => 2 + y; +// +// In this example, `addTwo` is a thunk that contains a partially applied addCurr +// invocation. +type Thunk[T any] struct { + doer func(T) T // action being thunked + o Option[T] // cache for complete thunk data +} + +// Force evaluates a Thunk's action if it needs to, otherwise it returns the +// previously evaluated result. +func (t Thunk[T]) Force(inp T) T { + if t.o.IsSome() { + return t.o.Yank() + } + + t.o.Set(t.doer(inp)) + return t.o.Yank() +} diff --git a/thunk_test.go b/thunk_test.go new file mode 100644 index 0000000..3014dfa --- /dev/null +++ b/thunk_test.go @@ -0,0 +1,32 @@ +package gonads + +import ( + "testing" +) + +func Fib(n int) int { + if n <= 1 { + return n + } + return Fib(n-1) + Fib(n-2) +} + +func TestRecurFib(t *testing.T) { + t.Log(Fib(40)) +} + +func TestThunkFib(t *testing.T) { + cache := make([]Thunk[int], 41) + cache[0].o.Set(0) + cache[1].o.Set(1) + + fib := func(n int) int { + return cache[n-1].Force(n-1) + cache[n-2].Force(n-2) + } + + for i := range cache { + cache[i].doer = fib + } + + t.Log(cache[40].Force(40)) +}