{"id":22929035,"url":"https://github.com/teaentitylab/fpgo","last_synced_at":"2025-05-16T10:09:03.550Z","repository":{"id":41844313,"uuid":"134690350","full_name":"TeaEntityLab/fpGo","owner":"TeaEntityLab","description":"Monad, Functional Programming features for Golang","archived":false,"fork":false,"pushed_at":"2024-10-28T02:26:16.000Z","size":315,"stargazers_count":351,"open_issues_count":0,"forks_count":22,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-05-16T10:08:57.130Z","etag":null,"topics":["actor-model","collection","curry","currying","functional-programming","functional-reactive-programming","golang","golang-library","monad","monads","optional","optional-implementations","pattern-matching","publisher-subscriber","publisher-subscriber-pattern","pubsub","reactive","reactive-programming","stream","sum-types"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TeaEntityLab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-05-24T09:08:45.000Z","updated_at":"2025-05-09T16:52:02.000Z","dependencies_parsed_at":"2024-01-08T15:02:52.041Z","dependency_job_id":"0c32d73c-17f6-4c78-94fd-81d8bdec15e8","html_url":"https://github.com/TeaEntityLab/fpGo","commit_stats":null,"previous_names":[],"tags_count":66,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TeaEntityLab%2FfpGo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TeaEntityLab%2FfpGo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TeaEntityLab%2FfpGo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TeaEntityLab%2FfpGo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TeaEntityLab","download_url":"https://codeload.github.com/TeaEntityLab/fpGo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254509475,"owners_count":22082892,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actor-model","collection","curry","currying","functional-programming","functional-reactive-programming","golang","golang-library","monad","monads","optional","optional-implementations","pattern-matching","publisher-subscriber","publisher-subscriber-pattern","pubsub","reactive","reactive-programming","stream","sum-types"],"created_at":"2024-12-14T09:28:55.450Z","updated_at":"2025-05-16T10:08:58.530Z","avatar_url":"https://github.com/TeaEntityLab.png","language":"Go","readme":"# fpGo\n\n[![tag](https://img.shields.io/github/tag/TeaEntityLab/fpGo.svg)](https://github.com/TeaEntityLab/fpGo)\n[![Go Report Card](https://goreportcard.com/badge/github.com/TeaEntityLab/fpGo/v2)](https://goreportcard.com/report/github.com/TeaEntityLab/fpGo/v2)\n[![codecov](https://codecov.io/gh/TeaEntityLab/fpGo/branch/master/graph/badge.svg)](https://codecov.io/gh/TeaEntityLab/fpGo)\n[![Travis CI Build Status](https://travis-ci.com/TeaEntityLab/fpGo.svg?branch=master)](https://travis-ci.com/TeaEntityLab/fpGo)\n[![GoDoc](https://godoc.org/github.com/TeaEntityLab/fpGo?status.svg)](https://godoc.org/github.com/TeaEntityLab/fpGo)\n\n[![license](https://img.shields.io/github/license/TeaEntityLab/fpGo.svg?style=social\u0026label=License)](https://github.com/TeaEntityLab/fpGo)\n[![stars](https://img.shields.io/github/stars/TeaEntityLab/fpGo.svg?style=social\u0026label=Stars)](https://github.com/TeaEntityLab/fpGo)\n[![forks](https://img.shields.io/github/forks/TeaEntityLab/fpGo.svg?style=social\u0026label=Fork)](https://github.com/TeaEntityLab/fpGo)\n\nMonad, Functional Programming features for Golang\n\n# Active Branches:\n\nFor *Generics* version(*`\u003e=go1.18`*):[generics](https://github.com/TeaEntityLab/fpGo/tree/generics)\n\n```bash\ngo get github.com/TeaEntityLab/fpGo/v2\n```\n\nFor *NonGenerics* version(*`\u003c=go1.17`*):[non-generics](https://github.com/TeaEntityLab/fpGo/tree/non_generic)\n\n```bash\ngo get github.com/TeaEntityLab/fpGo\n```\n\n# Why\n\nI love functional programing, Rx-style coding, and Optional usages.\n\nHowever it's hard to implement them in Golang, and there're few libraries to achieve parts of them.\n\nThus I implemented fpGo. I hope you would like it :)\n\n# Features\n\n* Optional/Maybe\n\n* Monad, Rx-like\n\n* Publisher\n\n\n\n* Pattern matching\n\n* Fp functions\n\n\n\n* Java8Stream-like Collection\n\n* Queue (LinkedListQueue/ChannelQueue/BufferedChannelQueue/ConcurrentQueue)\n\n* PythonicGenerator-like Coroutine(yield/yieldFrom)\n\n* Akka/Erlang-like Actor model(send/receive/spawn/states)\n\n* **network/SimpleHTTP** inspired by [Retrofit](https://github.com/square/retrofit)\n\n* **worker/WorkerPool** inspired by JavaExecutorService \u0026 goroutine pool libs\n\n# Special thanks\n* 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)\n  *\tCredit: https://github.com/logic-building/functional-go\n  * Credit: https://github.com/achannarasappa/pneumatic\n\n# Usage\n\n## Optional (IsPresent/IsNil, Or, Let)\n\n```go\nvar m MaybeDef[interface{}]\nvar orVal int\nvar boolVal bool\n\n// IsPresent(), IsNil()\nm = Maybe.Just(1)\nboolVal = m.IsPresent() // true\nboolVal = m.IsNil() // false\nm = Maybe.Just(nil)\nboolVal = m.IsPresent() // false\nboolVal = m.IsNil() // true\n\n// Or()\nm = Maybe.Just(1)\nfmt.Println((m.Or(3))) // 1\nm = Maybe.Just(nil)\nfmt.Println((m.Or(3))) // 3\n\n// Let()\nvar letVal int\nletVal = 1\nm = Maybe.Just(1)\nm.Let(func() {\n  letVal = 2\n})\nfmt.Println(letVal) // letVal would be 2\n\nletVal = 1\nm = Maybe.Just(nil)\nm.Let(func() {\n  letVal = 3\n})\nfmt.Println(letVal) // letVal would be still 1\n```\n\n## MonadIO (RxObserver-like)\n\nExample:\n```go\nvar m *MonadIODef[interface{}]\nvar actualInt int\n\nm = MonadIO.Just(1)\nactualInt = 0\nm.Subscribe(Subscription[interface{}]{\n  OnNext: func(in interface{}) {\n    actualInt, _ = Maybe.Just(in).ToInt()\n  },\n})\nfmt.Println(actualInt) // actualInt would be 1\n\nm = MonadIO.Just(1).FlatMap(func(in interface{}) *MonadIODef[interface{}] {\n  v, _ := Maybe.Just(in).ToInt()\n  return MonadIO.Just(v + 1)\n})\nactualInt = 0\nm.Subscribe(Subscription[interface{}]{\n  OnNext: func(in interface{}) {\n    actualInt, _ = Maybe.Just(in).ToInt()\n  },\n})\nfmt.Println(actualInt) // actualInt would be 2\n```\n\n## Stream (inspired by Collection libs)\n\nExample(Generics):\n```go\nvar s *StreamDef[int]\nvar tempString = \"\"\n\ns = StreamFromArray([]int{}).Append(1, 1).Extend(StreamFromArray([]int{2, 3, 4}))\ntempString = \"\"\nfor _, v := range s.ToArray() {\n  tempString += Maybe.Just(v).ToMaybe().ToString()\n}\nfmt.Println(tempString) // tempString would be \"11234\"\ns = s.Distinct()\ntempString = \"\"\nfor _, v := range s.ToArray() {\n  tempString += Maybe.Just(v).ToMaybe().ToString()\n}\nfmt.Println(tempString) // tempString would be \"1234\"\n```\n\nExample(Non-Generics/interface{}):\n```go\nvar s *StreamForInterfaceDef\nvar tempString string\n\ns = StreamForInterface.FromArrayInt([]int{}).Append(1, 1).Extend(StreamForInterface.FromArrayInt([]int{2, 3, 4})).Extend(StreamForInterface.FromArray([]interface{}{nil})).Extend(nil)\ntempString = \"\"\nfor _, v := range s.ToArray() {\n  tempString += Maybe.Just(v).ToMaybe().ToString()\n}\nfmt.Println(tempString) // tempString would be \"11234\u003cnil\u003e\"\ns = s.Distinct()\ntempString = \"\"\nfor _, v := range s.ToArray() {\n  tempString += Maybe.Just(v).ToMaybe().ToString()\n}\nfmt.Println(tempString) // tempString would be \"1234\u003cnil\u003e\"\ns = s.FilterNotNil()\ntempString = \"\"\nfor _, v := range s.ToArray() {\n  tempString += Maybe.Just(v).ToMaybe().ToString()\n}\nfmt.Println(tempString) // tempString would be \"1234\"\n```\n\n## Queue(LinkedListQueue/ChannelQueue/BufferedChannelQueue/ConcurrentQueue) (inspired by Collection libs)\n\n### LinkedListQueue(Shift/Unshift/Push/Pop), ConcurrentQueue(inspired by Java)\n\nExample:\n\n```go\nvar queue Queue[int]\nvar stack Stack[int]\nvar err error\nvar result int\n\nlinkedListQueue := NewLinkedListQueue[int]()\nqueue = linkedListQueue\nstack = linkedListQueue\nconcurrentQueue := NewConcurrentQueue[int](queue)\n\n// As a Queue, Put(val) in the TAIL and Take() in the HEAD\nerr = queue.Offer(1)\nerr = queue.Offer(2)\nerr = queue.Offer(3)\nresult, err = queue.Poll() // Result should be 1\nresult, err = queue.Poll() // Result should be 2\nresult, err = queue.Poll() // Result should be 3\nresult, err = queue.Poll() // Err: ErrQueueIsEmpty\n\n// As a Stack, Push(val) \u0026 Pop() in the TAIL.\nerr = stack.Push(1)\nerr = stack.Push(2)\nerr = stack.Push(3)\nresult, err = stack.Pop() // Result should be 3\nresult, err = stack.Pop() // Result should be 2\nresult, err = stack.Pop() // Result should be 1\nresult, err = stack.Pop() // Err: ErrStackIsEmpty\n```\n\n### BufferedChannelQueue(Offer/Take/TakeWithTimeout)\n\nExample:\n\n```go\nvar err error\nvar result int\nvar timeout time.Duration\n\nbufferedChannelQueue := NewBufferedChannelQueue[int](3, 10000, 100).\n  SetLoadFromPoolDuration(time.Millisecond / 10).\n  SetFreeNodeHookPoolIntervalDuration(1 * time.Millisecond)\n\nerr = queue.Offer(1)\nerr = queue.Offer(2)\nerr = queue.Offer(3)\ntimeout = 1 * time.Millisecond\nresult, err = bufferedChannelQueue.TakeWithTimeout(timeout) // Result should be 1\nresult, err = bufferedChannelQueue.TakeWithTimeout(timeout) // Result should be 2\nresult, err = bufferedChannelQueue.TakeWithTimeout(timeout) // Result should be 3\n```\n\n## Actor (inspired by Akka/Erlang)\n\n### Actor common(send/receive/spawn/states)\n\nExample:\n\n```go\nactual := 0\n// Channel for results\nresultChannel := make(chan interface{}, 1)\n// Message CMDs\ncmdSpawn := \"spawn\"\ncmdShutdown := \"shutdown\"\n// Testee\nactorRoot := Actor.New(func(self *ActorDef[interface{}], input interface{}) {\n  // SPAWN: for ROOT\n  if input == cmdSpawn {\n    self.Spawn(func(self *ActorDef[interface{}], input interface{}) {\n      // SHUTDOWN: for Children\n      if input == cmdShutdown {\n        self.Close()\n        return\n      }\n\n      // INT cases: Children\n      val, _ := Maybe.Just(input).ToInt()\n      resultChannel \u003c- val * 10\n    })\n    return\n  }\n  // SHUTDOWN: for ROOT\n  if input == cmdShutdown {\n    for _, child := range self.children {\n      child.Send(cmdShutdown)\n    }\n    self.Close()\n\n    close(resultChannel)\n    return\n  }\n\n  // INT cases: ROOT\n  intVal, _ := Maybe.Just(input).ToInt()\n  if intVal \u003e 0 {\n    for _, child := range self.children {\n      child.Send(intVal)\n    }\n  }\n})\n\n// Sequential Send messages(async)\ngo func() {\n  actorRoot.Send(cmdSpawn)\n  actorRoot.Send(10)\n  actorRoot.Send(cmdSpawn)\n  actorRoot.Send(20)\n  actorRoot.Send(cmdSpawn)\n  actorRoot.Send(30)\n}()\n\ni := 0\nfor val := range resultChannel {\n  intVal, _ := Maybe.Just(val).ToInt()\n  actual += intVal\n\n  i++\n  if i == 5 {\n    go actorRoot.Send(cmdShutdown)\n  }\n}\n\n// Result would be 1400 (=10*10+20*10+20*10+30*10+30*10+30*10)\nfmt.Println(actual)\n```\n\n### Actor Ask (inspired by Akka/Erlang)\n\n```go\nactorRoot := Actor.New(func(self *ActorDef[interface{}], input interface{}) {\n    // Ask cases: ROOT\n    switch val := input.(type) {\n    case *AskDef[interface{}, int]:\n        intVal, _ := Maybe.Just(val.Message).ToInt()\n\n        // NOTE If negative, hanging for testing Ask.timeout\n        if intVal \u003c 0 {\n            break\n        }\n\n        val.Reply(intVal * 10)\n        break\n    }\n})\n\n// var timer *time.Timer\nvar timeout time.Duration\ntimeout = 10 * time.Millisecond\n\n// Normal cases\n// Result would be 10\nactual = AskNewGenerics[interface{}, int](1).AskOnce(actorRoot)\n// Ask with Timeout\n// Result would be 20\nactual, _ = AskNewGenerics[interface{}, int](2).AskOnceWithTimeout(actorRoot, timeout)\n// Ask channel\n// Result would be 30\nch := AskNewGenerics[interface{}, int](3).AskChannel(actorRoot)\nactual = \u003c- ch\nclose(ch)\n\n// Timeout cases\n// Result would be 0 (zero value, timeout)\nactual, err = AskNewGenerics[interface{}, int](-1).AskOnceWithTimeout(actorRoot, timeout)\n```\n\n## Compose\n\nExample:\n\n```go\nvar fn01 = func(args ...int) []int {\n  val := args[0]\n  return SliceOf(val + 1)\n}\nvar fn02 = func(args ...int) []int {\n  val := args[0]\n  return SliceOf(val + 2)\n}\nvar fn03 = func(args ...int) []int {\n  val := args[0]\n  return SliceOf(val + 3)\n}\n\n// Result would be 6\nresult := Compose(fn01, fn02, fn03)((0))[0]\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteaentitylab%2Ffpgo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteaentitylab%2Ffpgo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteaentitylab%2Ffpgo/lists"}