Monad, Functional Programming features for Golang
For Generics version(>=go1.18
):generics
For NonGenerics version(<=go1.17
):non-generics
I love functional programing, Rx-style coding, and Optional usages.
However it's hard to implement them in Golang, and there're few libraries to achieve parts of them.
Thus I implemented fpGo. I hope you would like it :)
-
Optional/Maybe
-
Monad, Rx-like
-
Publisher
-
Pattern matching
-
Fp functions
-
Java8Stream-like Collection
-
PythonicGenerator-like Coroutine(yield/yieldFrom)
-
Akka/Erlang-like Actor model(send/receive/spawn/states)
- fp functions(Dedupe/Difference/Distinct/IsDistinct/DropEq/Drop/DropLast/DropWhile/IsEqual/IsEqualMap/Every/Exists/Intersection/Keys/Values/Max/Min/MinMax/Merge/IsNeg/IsPos/PMap/Range/Reverse/Set/Some/IsSubset/IsSuperset/Take/TakeLast/Union/IsZero/Zip/GroupBy/UniqBy/Flatten/Prepend/Partition/Tail/Head/SplitEvery)
var m MaybeDef[interface{}]
var orVal int
var boolVal bool
// IsPresent(), IsNil()
m = Maybe.Just(1)
boolVal = m.IsPresent() // true
boolVal = m.IsNil() // false
m = Maybe.Just(nil)
boolVal = m.IsPresent() // false
boolVal = m.IsNil() // true
// Or()
m = Maybe.Just(1)
fmt.Println((m.Or(3))) // 1
m = Maybe.Just(nil)
fmt.Println((m.Or(3))) // 3
// Let()
var letVal int
letVal = 1
m = Maybe.Just(1)
m.Let(func() {
letVal = 2
})
fmt.Println(letVal) // letVal would be 2
letVal = 1
m = Maybe.Just(nil)
m.Let(func() {
letVal = 3
})
fmt.Println(letVal) // letVal would be still 1
Example:
var m *MonadIODef[interface{}]
var actualInt int
m = MonadIO.Just(1)
actualInt = 0
m.Subscribe(Subscription[interface{}]{
OnNext: func(in interface{}) {
actualInt, _ = Maybe.Just(in).ToInt()
},
})
fmt.Println(actualInt) // actualInt would be 1
m = MonadIO.Just(1).FlatMap(func(in interface{}) *MonadIODef[interface{}] {
v, _ := Maybe.Just(in).ToInt()
return MonadIO.Just(v + 1)
})
actualInt = 0
m.Subscribe(Subscription[interface{}]{
OnNext: func(in interface{}) {
actualInt, _ = Maybe.Just(in).ToInt()
},
})
fmt.Println(actualInt) // actualInt would be 2
Example:
var s *StreamDef[interface{}]
var tempString = ""
s = StreamFromArray([]interface{}{}).Append(1).Extend(StreamFromInterface(2, 3, 4)).Extend(StreamFromArray([]interface{}{nil}))
tempString = ""
for _, v := range s.ToArray() {
tempString += Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString would be "1234<nil>"
s = s.Distinct()
tempString = ""
for _, v := range s.ToArray() {
tempString += Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString would be "1234"
Example:
actual := 0
// Channel for results
resultChannel := make(chan interface{}, 1)
// Message CMDs
cmdSpawn := "spawn"
cmdShutdown := "shutdown"
// Testee
actorRoot := Actor.New(func(self *ActorDef[interface{}], input interface{}) {
// SPAWN: for ROOT
if input == cmdSpawn {
self.Spawn(func(self *ActorDef[interface{}], input interface{}) {
// SHUTDOWN: for Children
if input == cmdShutdown {
self.Close()
return
}
// INT cases: Children
val, _ := Maybe.Just(input).ToInt()
resultChannel <- val * 10
})
return
}
// SHUTDOWN: for ROOT
if input == cmdShutdown {
for _, child := range self.children {
child.Send(cmdShutdown)
}
self.Close()
close(resultChannel)
return
}
// INT cases: ROOT
intVal, _ := Maybe.Just(input).ToInt()
if intVal > 0 {
for _, child := range self.children {
child.Send(intVal)
}
}
})
// Sequential Send messages(async)
go func() {
actorRoot.Send(cmdSpawn)
actorRoot.Send(10)
actorRoot.Send(cmdSpawn)
actorRoot.Send(20)
actorRoot.Send(cmdSpawn)
actorRoot.Send(30)
}()
i := 0
for val := range resultChannel {
intVal, _ := Maybe.Just(val).ToInt()
actual += intVal
i++
if i == 5 {
go actorRoot.Send(cmdShutdown)
}
}
// Result would be 1400 (=10*10+20*10+20*10+30*10+30*10+30*10)
fmt.Println(actual)
actorRoot := Actor.New(func(self *ActorDef[interface{}], input interface{}) {
// Ask cases: ROOT
switch val := input.(type) {
case *AskDef[interface{}, int]:
intVal, _ := Maybe.Just(val.Message).ToInt()
// NOTE If negative, hanging for testing Ask.timeout
if intVal < 0 {
break
}
val.Reply(intVal * 10)
break
}
})
// var timer *time.Timer
var timeout time.Duration
timeout = 10 * time.Millisecond
// Normal cases
// Result would be 10
actual, _ = AskNewGenerics[interface{}, int](1).AskOnce(actorRoot, nil)
// Ask with Timeout
// Result would be 20
actual, _ = AskNewGenerics[interface{}, int](2).AskOnce(actorRoot, &timeout)
// Ask channel
// Result would be 30
ch := AskNewGenerics[interface{}, int](3).AskChannel(actorRoot)
actual = <- *ch
close(*ch)
// Timeout cases
// Result would be 0 (zero value, timeout)
actual, err = AskNewGenerics[interface{}, int](-1).AskOnce(actorRoot, &timeout)
Example:
var fn01 = func(args ...int) []int {
val := args[0]
return SliceOf(val + 1)
}
var fn02 = func(args ...int) []int {
val := args[0]
return SliceOf(val + 2)
}
var fn03 = func(args ...int) []int {
val := args[0]
return SliceOf(val + 3)
}
// Result would be 6
result := Compose(fn01, fn02, fn03)((0))[0]