{"id":23230925,"url":"https://github.com/misterkaiou/go-functional","last_synced_at":"2025-07-23T14:05:10.410Z","repository":{"id":52235659,"uuid":"519813531","full_name":"MisterKaiou/go-functional","owner":"MisterKaiou","description":"Small library with some known monads used in functional programming","archived":false,"fork":false,"pushed_at":"2022-10-08T02:08:37.000Z","size":65,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-06-20T19:23:36.426Z","etag":null,"topics":["fp","functional","generics","go","golang","monads","option","option-type","result","result-type"],"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/MisterKaiou.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}},"created_at":"2022-07-31T15:27:09.000Z","updated_at":"2024-04-15T19:21:16.000Z","dependencies_parsed_at":"2022-08-25T12:40:23.365Z","dependency_job_id":null,"html_url":"https://github.com/MisterKaiou/go-functional","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MisterKaiou%2Fgo-functional","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MisterKaiou%2Fgo-functional/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MisterKaiou%2Fgo-functional/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MisterKaiou%2Fgo-functional/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MisterKaiou","download_url":"https://codeload.github.com/MisterKaiou/go-functional/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230363976,"owners_count":18214717,"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":["fp","functional","generics","go","golang","monads","option","option-type","result","result-type"],"created_at":"2024-12-19T02:12:35.925Z","updated_at":"2024-12-19T02:12:36.562Z","avatar_url":"https://github.com/MisterKaiou.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-functional\n\nLanguages: [PT](README_pt.md), **EN**\n\n## What is it?\n\nIt is a small library in Go that contains the implementation of common monads in functional programming that makes use of [generics](https://go.dev/doc/tutorial/generics) support.\n\n## How to install\n\n```sh\ngo get github.com/MisterKaiou/go-functional\n```\n\n## Why?\n\nThe current way of returning a value or an error, with a ` ([value], error) ` tuple, is not very expandable. Since you would have to resort to the classic ` if err != nil { do() } ` to handle the error, which quickly gets tiresome.\n\nOf course, in functional languages this would hardly be a problem as the entire language and its APIs are created with monads in mind. We don't have this on Go's API, but even if you do it only in your code, it's a drastic change that will require, at most, getting used to reading `result.Map(** parameters **)`, which in my opinion , it's not much to ask considering how clean the code can get without the countless lines of `if err != nil`.\n\n## Behavior\n\n### Result\n- The value of *Ok* for an error `Result` is `nil`;\n- The value of *Ok* for a successful `Result` is the value it holds;\n- The *error* value for an error `Result` is the error that was used to create it;\n- The *error* value for a successful `Result` is `nil`.\n\n### Option\n- The value of *Some* for an `Option` representing nothing is `nil`;\n- The value of *Some* for an `Option` that represents something is the value it holds;\n- The value of *None* for an `Option` representing nothing is a pointer to a newly created instance of `None`\n- The value of *None* for an `Option` that represents something is `nil`\n\nYou don't have access to the internal values, and this is by design, you really shouldn't do this or the purpose of these types would be in vain. `Unwrap` is a *convenience* and I still discourage its use.\n\n\u003cbr/\u003e\n\n## Types\n\nIn order not to prolong the description too much, examples of how to handle *error* or *None* cases will be shown only once. Since, the same behavior happens in all types and functions.\n\n\u003cbr/\u003e\n\n### **Result[Of any]**\n****\n**Result[Of any]** is a struct that represents a result that contains *`Of`* or *`error`*. Since we have an interface to represent errors within the language itself, I chose to use it.\n\n\u003cbr/\u003e\n\n### Examples:\n\u003cbr/\u003e\n\n**String:** *`Result`* implements the `stringer` interface, and the value returned when calling the `String` method depends on its internal value.\n- If **Ok**: `String` will return `fmt.String` applied to the internal value. Ex:\n```go\nstr := result.Ok(42).String() // Same as fmt.String(42)\nprint(str) // Prints: \"42\"\n```\n- If it is **Error**: String will return `error.Error()` in the internal value. Ex:\n```go\nstr := result.Error[int](errors.New(\"error\")).String() // Same as err.Error()\nprint(str) // Prints: error\n```\n\u003cbr/\u003e\n\n**Map:** Given a *`Result`*, it accepts a function that takes its value and returns another value. Finally, the returned value is stored inside a new *`Result`*\n\n**Ok:**\n```go\nres := result.Ok(42) // *Result[int]\nmappedRes := result.Map(res, func(val int) bool { return val == 42 }) // *Result[bool]\n\nprintf(\"The value of mappedRes is: %s\", mappedRes)\n// Prints: The value of mappedRes is: true\"\n```\n**Erro:**\n\n```go\nres := result.Error[int](errors.New(\"error\")) // *Result[int]\nmappedRes := result.Map(res, func(val int) bool { return val == 0 }) //*Result[bool]\n\nprintf(\"The value of mappedRes is: %s\", mappedRes) \n// Prints: The value of mappedRes is: error\"\n```\n\u003cbr/\u003e\n\n**MapError:** Given a *`Result`*, it accepts a function that receives its error and returns another error. Finally, the returned value is stored in a new *`Result`*.\n\n```go\nres := Error[int](errors.New(\"something failed\"))\n\nmapped := MapError(res, func(err error) error { return errors.New(fmt.Sprint(\"oh no \", err)) })\nprintln(mapped.String()) // Prints: oh no something failed\n```\n\n\u003cbr/\u003e\n\n**Bind:** Given a *`Result`*, it accepts a function that takes its value and returns another *`Result`*.\n\n```go\nres := result.Ok(42) // *Result[int]\nbRes := result.Bind(res, func(val int) bool { return result.Ok(val == 42) }) // *Result[bool]\n\nprintf(\"The value of bRes is: %s\", bRes)\n// Prints: The value of bRes is: true\"\n```\n\u003cbr/\u003e\n\n**Match:** Given a *`Result`*, it accepts two functions that return a value of the same type, but the first one receives the result contained in it and the second one receives the error.\n\n```go\nres := result.Ok(42)\n\nmatchedRes := result.Match(res,\n\tfunc(ok int) string { return \"All fine around here\" },\n\tfunc(err error) string { return err.Error() })\n\n\nprintf(\"The value of matchedRes is: %s\", matchedRes)\n// Prints: The value of matchedRes is: All fine around here\"\n```\n\u003cbr/\u003e\n\n**Unwrap:** It is a *`Result`* method. Returns the value contained in it or calls `panic`.\n\n*Note: Prefer to use `Match` instead of `Unwrap`*\n```go\nres := result.Ok(69).Unwrap() // res = 69\n// Ou\nres := result.Error[bool](errors.New(\"error\")).Unwrap() // Panic\n```\n\u003cbr/\u003e\n\n### **Option[Of any]**\n****\n**Option[Of any]** is a struct that represents either *something* or *nothing*.\n\n\u003cbr/\u003e\n\n### Examples:\n\u003cbr/\u003e\n\n**String:** *`Option`* implements the `stringer` interface, and the value when calling the `String` method depends on its internal value.\n- If **Some**: `String` will return the value of `fmt.String` applied to the internal value. Ex:\n```go\nstr := option.Some(42).String() // Same as fmt.String(42)\nprint(str) // Prints: \"42\"\n```\n- If **None**: String will return `\"None\"`. Ex:\n```go\nstr := option.None[int]().String()\nprint(str) // Prints: None\n```\n\u003cbr/\u003e\n\n**Map:** Given an *`Option`*, it accepts a function that takes its value and returns another value. Finally, the returned value is stored inside a new *`Option`*\n\n**Ok:**\n```go\nopt := option.Some(42) // *Option[int]\nmappedOpt := option.Map(opt, func(val int) bool { return val == 42 }) // *Option[bool]\n\nprintf(\"The value of mappedOpt is: %s\", mappedOpt)\n// Prints: The value of mappedOpt is: true\"\n```\n**Erro:**\n\n```go\nopt := option.None[int]() // *Option[int]\nmappedOpt := option.Map(opt, func(val int) bool { return val == 0 }) //*Option[bool]\n\nprintf(\"The value of mappedOpt is: %s\", mappedOpt) \n// Prints: The value of mappedOpt is: None\"\n```\n\u003cbr/\u003e\n\n**Bind:** Given an *`Option`*, it accepts a function that takes its value and returns another *`Option`*.\n\n```go\nopt := option.Some(42) // *Option[int]\nbRes := option.Bind(opt, func(val int) bool { return option.Some(val == 42) }) // *Option[bool]\n\nprintf(\"The value of bRes is: %s\", bRes)\n// Prints: The value of bRes is: true\"\n```\n\u003cbr/\u003e\n\n**Match:** Given an *`Option`*, accepts two functions that return a value of the same type. The first receives the result contained in it and the second does not receive parameters.\n\n```go\nopt := option.Some(42)\n\nmatchedOpt := option.Match(opt,\n\tfunc(ok int) string { return \"Something here\" },\n\tfunc() string { return \"Nothing here\" })\n\nprintf(\"The value of matchedOpt is: %s\", matchedOpt)\n// Prints: The value of matchedOpt is: Something here\"\n```\n\u003cbr/\u003e\n\n**Unwrap:** It is a *`Option`* method. Returns the value contained in it or calls `panic`.\n\n*Note: Prefer to use `Match` instead of `Unwrap`*\n```go\nres := option.Some(69).Unwrap() // res = 69\n// Ou\nres := option.None[bool]().Unwrap() // Panic\n```\n\n\u003cbr/\u003e\n\n### [*More info on the docs!*](https://pkg.go.dev/github.com/MisterKaiou/go-functional)\n\n\u003cbr/\u003e\n\n## Real Example\n\nThe code snippet below was taken from a real application of mine using this library together with [gin](https://github.com/gin-gonic/gin):\n```go\nfunc (h *Handler) Login() response.HttpResult {\n\tnonce := h.NonceGenerator()\n\tstateStr := r.Bind(nonce, func(it auth.Nonce) *r.Result[string] {\n\t\tsaved := h.NonceStore.Save(it)\n\t\treturn r.Map(saved, func(_ unit.Unit) string {\n\t\t\treturn it.String()\n\t\t})\n\t})\n\tauthUrl := r.Bind(stateStr, auth.GenerateAuthenticationURL)\n\treturn response.Redirect(authUrl)\n}\n```\nApart from the abstractions I created, like `response.HttpResult` and `response.Redirect`, notice that every call to `Bind` or `Map` would be `if err != nil { return/panic }`. A method that just creates the OAuth login URL would be way longer than these 9 lines if we weren't using this code style.\n\nAdding the `r` alias to the `import` helps make reading less tiring.\n\n## Future plans\n\nAdd other monads like:\n- IO\n- Either\n- State (The need for this in Go is debatable as we don't have immutability unless we consider *Pass by Value* something close to that)\n\nUse cases for these are kind of rare, so it's not included in the library as it is now. But if it is a requirement for at least someone, it will be implemented.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisterkaiou%2Fgo-functional","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmisterkaiou%2Fgo-functional","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisterkaiou%2Fgo-functional/lists"}