- Notifications
You must be signed in to change notification settings - Fork22
Monad, Functional Programming features for Golang
License
TeaEntityLab/fpGo
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Monad, Functional Programming features for Golang
ForGenerics version(>=go1.18
):generics
go get github.com/TeaEntityLab/fpGo/v2
ForNonGenerics version(<=go1.17
):non-generics
go get github.com/TeaEntityLab/fpGo
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
Queue (LinkedListQueue/ChannelQueue/BufferedChannelQueue/ConcurrentQueue)
PythonicGenerator-like Coroutine(yield/yieldFrom)
Akka/Erlang-like Actor model(send/receive/spawn/states)
network/SimpleHTTP inspired byRetrofit
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 1
Example:
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 2
Example(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: ErrStackIsEmpty
Example:
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 3
Example:
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]
About
Monad, Functional Programming features for Golang
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.