Skip to content

Commit

Permalink
monady
Browse files Browse the repository at this point in the history
  • Loading branch information
rushsteve1 committed Nov 5, 2024
1 parent 67e56ce commit d6c90a4
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 82 deletions.
22 changes: 14 additions & 8 deletions fun/curry.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,27 @@ import (
"reflect"
)

func Curry2[A, B, Out any](f func(A, B) Out, b B) func(A) Out {
return func(a A) Out {
// The set of currying functions takes a series of functions and returns
// a new function that takes only the first argument of the first function.
//
// Because of a limitation of Go and the design of this library, all curried
// functions take only a single argument.

func Curry2[In, B, Out any](f func(In, B) Out, b B) func(In) Out {
return func(a In) Out {
return f(a, b)
}
}

func Curry3[A, B, C, Out any](f func(A, B, C) Out, b B, c C) func(A) Out {
return func(a A) Out {
return Curry2(Curry32(f, c), b)(a)
func Curry3[In, B, C, Out any](f func(In, B, C) Out, b B, c C) func(In) Out {
return func(a In) Out {
return f(a, b, c)
}
}

func Curry32[A, B, C, Out any](f func(A, B, C) Out, c C) func(A, B) Out {
return func(a A, b B) Out {
return f(a, b, c)
func Curry4[In, B, C, D, Out any](f func(In, B, C, D) Out, b B, c C, d D) func(In) Out {
return func(a In) Out {
return f(a, b, c, d)
}
}

Expand Down
5 changes: 3 additions & 2 deletions fun/fun.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package fun

import "github.com/rushsteve1/fp"

// Identity returns what it is passed
func Identity[T any](t T) T {
return t
}


// Errorless takes a function that can error and returns a new function that
// wraps the provided one in [fp.Must]
// It's better to use [monads.FuncWrap]
func Errorless[T, U any](f func(T) (U, error)) func(T) U {
return func(t T) U {
return fp.Must(f(t))
Expand All @@ -20,4 +21,4 @@ func Discard[T, U any](f func(T) U) func(T) {
return func(t T) {
_ = f(t)
}
}
}
22 changes: 15 additions & 7 deletions generators/generators.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import (
"time"

. "github.com/rushsteve1/fp"
"github.com/rushsteve1/fp/monads"
)

// A generator is any function that returns a new sequence

// Empty returns an infinite empty sequence that never yields any values
func Empty[T any]() Seq[T] {
return SeqFunc[T](func(func(T) bool){})
return SeqFunc[T](func(func(T) bool) {})
}

// Generate returns an iterator that repeatedly calls the provided function,
Expand All @@ -25,9 +26,16 @@ func Generate[T any](f func() T) Seq[T] {
})
}

// Once returns a sequence that yields the provided value once
func Once[T any](v T) Seq[T] {
return SeqFunc[T](func(yield func(T) bool) {
yield(v)
})
}

// Forever returns an infinite sequence of the provided value
func Forever[T any](v T) Seq[T] {
return SeqFunc[T](func(yield func(T) bool){
return SeqFunc[T](func(yield func(T) bool) {
if !yield(v) {
return
}
Expand Down Expand Up @@ -71,11 +79,11 @@ func Chan[T any](c chan T) Seq[T] {

// Reader reads from the passed [io.Reader] turning into a sequence of byte arrays.
// See its counterpart [transducers.Writer]
func Reader(r io.Reader) Seq[[]byte] {
return SeqFunc[[]byte](func(yield func([]byte) bool) {
func Reader(r io.Reader) Seq[monads.Result[[]byte]] {
return SeqFunc[monads.Result[[]byte]](func(yield func(monads.Result[[]byte]) bool) {
var buf []byte
Must(r.Read(buf))
if !yield(buf) {
_, err := r.Read(buf)
if !yield(monads.Wrap(buf, err)) {
return
}
})
Expand All @@ -93,4 +101,4 @@ func Accept(l net.Listener) Seq[net.Conn] {
}
}
})
}
}
9 changes: 4 additions & 5 deletions generators/generators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package generators_test
import (
"testing"
"time"


"github.com/rushsteve1/fp"
. "github.com/rushsteve1/fp/generators"
)

Expand Down Expand Up @@ -33,8 +34,6 @@ func TestChan(t *testing.T) {
t.Log(v)
i++
}

if i != 5 {
t.Fail()
}

fp.AssertEq(t, i, 5)
}
30 changes: 25 additions & 5 deletions magic.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ var GlobalErrorHandler = func(err error) bool {
panic(err)
}

// Must is the first function anyone wants in Go
func Must[T any](t T, err error) T {
Check(err)
return t
}

// Check is the second
func Check(err error) {
if err != nil {
if !GlobalErrorHandler(err) {
Expand All @@ -32,10 +39,23 @@ func Check(err error) {
}
}

// Must is the first function anyone wants in Go
func Must[T any](t T, err error) T {
Check(err)
return t
// Ptr returns a pointer of its argument
func Ptr[T any](t T) *T {
return &t
}

// DerefOr dereferences the passed pointer otherwise returns the or value
func DerefOr[T any](ref *T, or T) T {
if ref != nil {
return *ref
}
return or
}

// DerefZero is like [DerefOr] but returns the zero value if nil
func DerefZero[T any](ref *T) T {
var t T
return DerefOr(ref, t)
}

func Clamp[T cmp.Ordered](x T, lo T, hi T) T {
Expand Down Expand Up @@ -77,4 +97,4 @@ func AssertSliceEq[S ~[]E, E comparable](t *testing.T, a, b S) {
if !slices.Equal(a, b) {
t.Errorf("Assertion failure: %+v != %+v", a, b)
}
}
}
6 changes: 5 additions & 1 deletion monads/monads.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package monads
import "github.com/rushsteve1/fp"

// Monad is a context that an operation took place in.
// You can apply additional operations to
// You can apply additional operations within the monad using transducers
type Monad[T any] interface {
// All monads are sequences and can be manipulated using transducers
fp.Seq[T]
// Ok returns true if this monad is "ok"
// The actual meaning of that value depends on the monad
Ok() bool
// Get tries to retrieve the inner value of the monad
Get() (T, error)
}
31 changes: 28 additions & 3 deletions monads/monads_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,35 @@
package monads
package monads_test

import "testing"
import (
"testing"

func TestMonadInterface(t *testing.T) {
"github.com/rushsteve1/fp"
. "github.com/rushsteve1/fp/fun"
. "github.com/rushsteve1/fp/monads"
. "github.com/rushsteve1/fp/reducers"
. "github.com/rushsteve1/fp/transducers"
)

func TestOptionMonadInterface(t *testing.T) {
v := Some(1)
// If this cast succedes the test does
m := Monad[int](v)
t.Log(m)
}

func TestResultMonadInterface(t *testing.T) {
v := Wrap(1, nil)
// If this cast succedes the test does
m := Monad[int](v)
t.Log(m)
}

func TestMonadTransducer(t *testing.T) {
a := Transduce(
Some(10),
Curry2(Map, func(x int) int { return x * 2 }),
Chain2(First[int], Some),
)

fp.AssertEq(t, a, Some(20))
}
51 changes: 37 additions & 14 deletions monads/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,29 @@ import (
var ErrUnwrapInvalid = errors.New("Unwrapped invalid Option")

type Option[T any] struct {
// I have no idea why I bothered implementing it this way
// but while we're re-interpeting the stdlib might as well
sql.Null[T]
}

func (o Option[T]) Ok() bool {
return o.Valid
}

func (o Option[T]) Get() (T, error) {
return o.V, fp.Ternary(o.Valid, nil, ErrUnwrapInvalid)
}

func (o Option[T]) Seq(yield func(T) bool) {
if o.Valid {
yield(o.V)
}
}

func (o Option[T]) Ptr() *T {
return fp.Ternary(o.Valid, &o.V, nil)
}

func Some[T any](v T) Option[T] {
return Option[T]{
sql.Null[T]{
Expand All @@ -22,6 +42,23 @@ func Some[T any](v T) Option[T] {
}
}

func TrySome[T any](v *T) Option[T] {
if v == nil {
return Option[T]{
sql.Null[T]{
V: *v,
Valid: true,
},
}
}

return Option[T]{
sql.Null[T]{
Valid: false,
},
}
}

func None[T any]() Option[T] {
var v T
return Option[T]{
Expand All @@ -31,17 +68,3 @@ func None[T any]() Option[T] {
},
}
}

func (o Option[T]) Ok() bool {
return o.Valid
}

func (o Option[T]) Get() (T, error) {
return o.V, fp.Ternary(o.Valid, nil, ErrUnwrapInvalid)
}

func (o Option[T]) Seq(yield func(T) bool) {
if o.Valid {
yield(o.V)
}
}
20 changes: 13 additions & 7 deletions monads/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ type Result[T any] struct {
Err error
}

func Wrap[T any](v T, err error) Result[T] {
return Result[T]{
V: v,
Err: err,
}
}

func (o Result[T]) Ok() bool {
return o.Err != nil
}
Expand All @@ -25,3 +18,16 @@ func (r Result[T]) Seq(yield func(T) bool) {
yield(r.V)
}
}

func Wrap[T any](v T, err error) Result[T] {
return Result[T]{
V: v,
Err: err,
}
}

func FuncWrap[In, Out any](f func (In) (Out, error)) func(In) Result[Out] {
return func(a In) Result[Out] {
return Wrap(f(a))
}
}
Loading

0 comments on commit d6c90a4

Please sign in to comment.