Monad, Functional Programming features for Golang
For Generics version(>=go1.18):generics
go get github.com/TeaEntityLab/fpGo/v2For NonGenerics version(<=go1.17):non-generics
go get github.com/TeaEntityLab/fpGoI 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
Queue (LinkedListQueue/ChannelQueue/BufferedChannelQueue/ConcurrentQueue)
PythonicGenerator-like Coroutine(yield/yieldFrom)
Akka/Erlang-like Actor model(send/receive/spawn/states)
network/SimpleHTTP inspired by Retrofit
worker/WorkerPool inspired by JavaExecutorService & goroutine pool libs
- 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)
varmMaybeDef[interface{}]
varorValintvarboolValbool// IsPresent(), IsNil()m=Maybe.Just(1)
boolVal=m.IsPresent() // trueboolVal=m.IsNil() // falsem=Maybe.Just(nil)
boolVal=m.IsPresent() // falseboolVal=m.IsNil() // true// Or()m=Maybe.Just(1)
fmt.Println((m.Or(3))) // 1m=Maybe.Just(nil)
fmt.Println((m.Or(3))) // 3// Let()varletValintletVal=1m=Maybe.Just(1)
m.Let(func() {
letVal=2
})
fmt.Println(letVal) // letVal would be 2letVal=1m=Maybe.Just(nil)
m.Let(func() {
letVal=3
})
fmt.Println(letVal) // letVal would be still 1Example:
varm*MonadIODef[interface{}]
varactualIntintm=MonadIO.Just(1)
actualInt=0m.Subscribe(Subscription[interface{}]{
OnNext: func(ininterface{}) {
actualInt, _=Maybe.Just(in).ToInt()
},
})
fmt.Println(actualInt) // actualInt would be 1m=MonadIO.Just(1).FlatMap(func(ininterface{}) *MonadIODef[interface{}] {
v, _:=Maybe.Just(in).ToInt()
returnMonadIO.Just(v+1)
})
actualInt=0m.Subscribe(Subscription[interface{}]{
OnNext: func(ininterface{}) {
actualInt, _=Maybe.Just(in).ToInt()
},
})
fmt.Println(actualInt) // actualInt would be 2Example(Generics):
vars*StreamDef[int]
vartempString=""s=StreamFromArray([]int{}).Append(1, 1).Extend(StreamFromArray([]int{2, 3, 4}))
tempString=""for_, v:=ranges.ToArray() {
tempString+=Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString would be "11234"s=s.Distinct()
tempString=""for_, v:=ranges.ToArray() {
tempString+=Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString would be "1234"Example(Non-Generics/interface{}):
vars*StreamForInterfaceDefvartempStringstrings=StreamForInterface.FromArrayInt([]int{}).Append(1, 1).Extend(StreamForInterface.FromArrayInt([]int{2, 3, 4})).Extend(StreamForInterface.FromArray([]interface{}{nil})).Extend(nil)
tempString=""for_, v:=ranges.ToArray() {
tempString+=Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString would be "11234<nil>"s=s.Distinct()
tempString=""for_, v:=ranges.ToArray() {
tempString+=Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString would be "1234<nil>"s=s.FilterNotNil()
tempString=""for_, v:=ranges.ToArray() {
tempString+=Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString would be "1234"Queue(LinkedListQueue/ChannelQueue/BufferedChannelQueue/ConcurrentQueue) (inspired by Collection libs)
Example:
varqueueQueue[int]
varstackStack[int]
varerrerrorvarresultintlinkedListQueue:=NewLinkedListQueue[int]()
queue=linkedListQueuestack=linkedListQueueconcurrentQueue:=NewConcurrentQueue[int](queue)
// As a Queue, Put(val) in the TAIL and Take() in the HEADerr=queue.Offer(1)
err=queue.Offer(2)
err=queue.Offer(3)
result, err=queue.Poll() // Result should be 1result, err=queue.Poll() // Result should be 2result, err=queue.Poll() // Result should be 3result, err=queue.Poll() // Err: ErrQueueIsEmpty// As a Stack, Push(val) & Pop() in the TAIL.err=stack.Push(1)
err=stack.Push(2)
err=stack.Push(3)
result, err=stack.Pop() // Result should be 3result, err=stack.Pop() // Result should be 2result, err=stack.Pop() // Result should be 1result, err=stack.Pop() // Err: ErrStackIsEmptyExample:
varerrerrorvarresultintvartimeout time.DurationbufferedChannelQueue:=NewBufferedChannelQueue[int](3, 10000, 100).
SetLoadFromPoolDuration(time.Millisecond/10).
SetFreeNodeHookPoolIntervalDuration(1*time.Millisecond)
err=queue.Offer(1)
err=queue.Offer(2)
err=queue.Offer(3)
timeout=1*time.Millisecondresult, err=bufferedChannelQueue.TakeWithTimeout(timeout) // Result should be 1result, err=bufferedChannelQueue.TakeWithTimeout(timeout) // Result should be 2result, err=bufferedChannelQueue.TakeWithTimeout(timeout) // Result should be 3Example:
actual:=0// Channel for resultsresultChannel:=make(chaninterface{}, 1)
// Message CMDscmdSpawn:="spawn"cmdShutdown:="shutdown"// TesteeactorRoot:=Actor.New(func(self*ActorDef[interface{}], inputinterface{}) {
// SPAWN: for ROOTifinput==cmdSpawn {
self.Spawn(func(self*ActorDef[interface{}], inputinterface{}) {
// SHUTDOWN: for Childrenifinput==cmdShutdown {
self.Close()
return
}
// INT cases: Childrenval, _:=Maybe.Just(input).ToInt()
resultChannel<-val*10
})
return
}
// SHUTDOWN: for ROOTifinput==cmdShutdown {
for_, child:=rangeself.children {
child.Send(cmdShutdown)
}
self.Close()
close(resultChannel)
return
}
// INT cases: ROOTintVal, _:=Maybe.Just(input).ToInt()
ifintVal>0 {
for_, child:=rangeself.children {
child.Send(intVal)
}
}
})
// Sequential Send messages(async)gofunc() {
actorRoot.Send(cmdSpawn)
actorRoot.Send(10)
actorRoot.Send(cmdSpawn)
actorRoot.Send(20)
actorRoot.Send(cmdSpawn)
actorRoot.Send(30)
}()
i:=0forval:=rangeresultChannel {
intVal, _:=Maybe.Just(val).ToInt()
actual+=intVali++ifi==5 {
goactorRoot.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{}], inputinterface{}) {
// Ask cases: ROOTswitchval:=input.(type) {
case*AskDef[interface{}, int]:
intVal, _:=Maybe.Just(val.Message).ToInt()
// NOTE If negative, hanging for testing Ask.timeoutifintVal<0 {
break
}
val.Reply(intVal*10)
break
}
})
// var timer *time.Timervartimeout time.Durationtimeout=10*time.Millisecond// Normal cases// Result would be 10actual=AskNewGenerics[interface{}, int](1).AskOnce(actorRoot)
// Ask with Timeout// Result would be 20actual, _=AskNewGenerics[interface{}, int](2).AskOnceWithTimeout(actorRoot, timeout)
// Ask channel// Result would be 30ch:=AskNewGenerics[interface{}, int](3).AskChannel(actorRoot)
actual=<-chclose(ch)
// Timeout cases// Result would be 0 (zero value, timeout)actual, err=AskNewGenerics[interface{}, int](-1).AskOnceWithTimeout(actorRoot, timeout)Example:
varfn01=func(args...int) []int {
val:=args[0]
returnSliceOf(val+1)
}
varfn02=func(args...int) []int {
val:=args[0]
returnSliceOf(val+2)
}
varfn03=func(args...int) []int {
val:=args[0]
returnSliceOf(val+3)
}
// Result would be 6result:=Compose(fn01, fn02, fn03)((0))[0]