Skip to content

Commit

Permalink
Merge pull request #1 from repeale/feat/option-implementation
Browse files Browse the repository at this point in the history
feat: Option implementation
  • Loading branch information
repeale authored May 2, 2022
2 parents 173a9f6 + c0f8d57 commit b221c67
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 0 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Fp-go is a collection of Functional Programming helpers powered by Golang [1.18]
- [Compose](#compose)
- [Pipe](#pipe)
- [Curry](#curry)
- [Structs](#structs)
- [Option](#option)

## Install

Expand Down Expand Up @@ -232,3 +234,12 @@ Variations `Curry2`, `Curry3` and `Curry4` stating the number of params will be
curryedSum := Curry2(func(a int, b int) int { return a + b })
curryedSum(1)(2)
```

## Structs

#### Option

Option represents encapsulation of an optional value, it might be used as the return type of functions which may or may not return a meaningful value when they are applied.
You could instanciate an `opt.Option[T]` with a value with `opt.Some(val)`. If the value is missing you can use `opt.None[T]()`.

Option exports `Some`, `None`, `IsSome`, `IsNone`, `GetOrElse`, `Match`, `Map`, `Chain`.
7 changes: 7 additions & 0 deletions function.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package fp

// Callback function that returns a specific value type
type Lazy[T any] func() T

// Callback function that takes an argument and return a value of the same type
type LazyVal[T any] func(x T) T
79 changes: 79 additions & 0 deletions option/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package opt

import (
"github.com/repeale/fp-go"
)

// Base option struct
type Option[T any] struct {
value T
hasValue bool
}

// Constructor for Option with a value
func Some[T any](value T) Option[T] {
return Option[T]{value, true}
}

// Constructor for Option without a value
func None[T any]() Option[T] {
return Option[T]{}
}

// Helper to check if the Option has a value
func IsSome[T any](option Option[T]) bool {
return option.hasValue
}

// Helper to check if the Option is missing the value
func IsNone[T any](option Option[T]) bool {
return !option.hasValue
}

// Extracts the value out of the Option, if it exists. Otherwise returns the function with a default value
func GetOrElse[T any](onNone fp.Lazy[T]) func(Option[T]) T {
return func(option Option[T]) T {

if IsNone(option) {
return onNone()
}

return option.value
}
}

// Extracts the value out of the Option, if it exists, with a function. Otherwise returns the function with a default value
func Match[T any](onNone fp.Lazy[T], onSome fp.LazyVal[T]) func(Option[T]) T {
return func(option Option[T]) T {

if IsNone(option) {
return onNone()
}

return onSome(option.value)
}
}

// Execute the function on the Option value if it exists. Otherwise return the empty Option itself
func Map[T any](fn fp.LazyVal[T]) func(o Option[T]) Option[T] {
return func(option Option[T]) Option[T] {

if IsNone(option) {
return None[T]()
}

return Some(fn(option.value))
}
}

// Execute a function that returns an Option on the Option value if it exists. Otherwise return the empty Option itself
func Chain[A any, B any](fn func(a A) Option[B]) func(Option[A]) Option[B] {
return func(a Option[A]) Option[B] {

if IsNone(a) {
return None[B]()
}

return fn(a.value)
}
}
100 changes: 100 additions & 0 deletions option/option_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package opt

import (
"testing"
)

func TestSome(t *testing.T) {
res := Some("val")
if res.hasValue != true {
t.Error("Some should return a struct with hasValue set to true. Received:", res.hasValue)
}
}

func TestNone(t *testing.T) {
res := None[string]()
if res.hasValue != false {
t.Error("None should return a struct with hasValue set to false. Received:", res.hasValue)
}
}

func TestIsSome_Some(t *testing.T) {
res := IsSome(Some("value"))
if res != true {
t.Error("IsSome should return true. Received:", res)
}
}
func TestIsSome_None(t *testing.T) {
res := IsSome(None[string]())
if res != false {
t.Error("IsSome should return false. Received:", res)
}
}

func TestIsNone_Some(t *testing.T) {
res := IsNone(None[string]())
if res != true {
t.Error("IsNone should return true. Received:", res)
}
}
func TestIsNone_None(t *testing.T) {
res := IsNone(Some("value"))
if res != false {
t.Error("IsNone should return false. Received:", res)
}
}

func TestGetOrElse_Some(t *testing.T) {
res := GetOrElse(func() string { return "fail" })(Some("val"))
if res != "val" {
t.Error("GetOrElse should return the Some value. Received:", res)
}
}

func TestGetOrElse_None(t *testing.T) {
res := GetOrElse(func() string { return "elseValue" })(None[string]())
if res != "elseValue" {
t.Error("GetOrElse should return the onNone() value. Received:", res)
}
}

func TestMatch_onSome(t *testing.T) {
res := Match(func() string { return "onNone" }, func(x string) string { return x + x })(Some("val"))
if res != "valval" {
t.Error("Match should return the onSome() value. Received:", res)
}
}

func TestMatch_onNone(t *testing.T) {
res := Match(func() string { return "onNone" }, func(x string) string { return x + x })(None[string]())
if res != "onNone" {
t.Error("Match should return the onNone() return value. Received:", res)
}
}

func TestMap_Some(t *testing.T) {
res := Map(func(x string) string { return x + x })(Some("val"))
if res.value != "valval" {
t.Error("Map should return the result of the callback function. Received:", res.value)
}
}

func TestMap_None(t *testing.T) {
res := Map(func(x string) string { return x + x })(None[string]())
if res.hasValue != false {
t.Error("Map should return a None value. Received:", res.value)
}
}

func TestChain_Some(t *testing.T) {
res := Chain(func(x string) Option[string] { return Some(x + x) })(Some("val"))
if res.hasValue != true {
t.Error("Chain should return a Some of string. Received:", res.value)
}
}
func TestChain_None(t *testing.T) {
res := Chain(func(x string) Option[string] { return Some(x + x) })(None[string]())
if res.hasValue != false {
t.Error("Chain should return a None value. Received:", res.value)
}
}

0 comments on commit b221c67

Please sign in to comment.