master
Jordan Orelli 3 years ago
parent 301da04f9b
commit b3ee3e6a2d

@ -0,0 +1,46 @@
package opt
// Val is an optional value
type Val[T any] struct {
val T
ok bool
}
// New creates an optional value
func New[T any](v T, ok bool) Val[T] {
if ok {
return Some(v)
}
return None[T]()
}
// None creates an empty optional value
func None[T any]() Val[T] { return Val[T]{} }
// NoneOf is the same as none, but it takes a parameter that it throws away.
// This allows the type T to be inferred.
func NoneOf[T any](v T) Val[T] { return Val[T]{} }
// Some creates a filled optional value
func Some[T any](v T) Val[T] {
return Val[T]{
val: v,
ok: true,
}
}
// Open retrives the contents of our optional value
func (v Val[T]) Open() (T, bool) {
return v.val, v.ok
}
// Bind takes a function that doesn't understand optionals and gives you
// another function that does
func Bind[X, Y any](f func(X) Y) func(Val[X]) Val[Y] {
return func(mx Val[X]) Val[Y] {
if x, ok := mx.Open(); ok {
return Some(f(x))
}
return None[Y]()
}
}

@ -0,0 +1,68 @@
package opt
import (
"testing"
"unicode/utf8"
)
func TestNone(t *testing.T) {
s := None[string]()
if s.ok {
t.Error("should not be ok")
}
if _, ok := s.Open(); ok {
t.Error("should not be ok")
}
s2 := NoneOf("poop")
if s2.ok {
t.Error("should not be ok")
}
if _, ok := s2.Open(); ok {
t.Error("should not be ok")
}
}
func TestSome(t *testing.T) {
s := Some("poop")
if !s.ok {
t.Error("should be ok")
}
v, ok := s.Open()
if !ok {
t.Fatal("should be ok")
}
if v != "poop" {
t.Error("should be poop")
}
}
func TestBind(t *testing.T) {
count := Bind(utf8.RuneCountInString)
t.Run("bind some", func(t *testing.T) {
m := count(Some("poop"))
n, ok := m.Open()
if !ok {
t.Fatal("should be ok")
}
if n != 4 {
t.Errorf("wanted 4 but got %d instead", n)
}
})
t.Run("bind non", func(t *testing.T) {
m := count(None[string]())
n, ok := m.Open()
if ok {
t.Fatal("should not be ok")
}
if n != 0 {
t.Errorf("wanted 0 but got %d instead", n)
}
})
}
Loading…
Cancel
Save