{"id":26866329,"url":"https://github.com/jkone27/cat","last_synced_at":"2025-06-12T19:36:19.421Z","repository":{"id":87061630,"uuid":"143125632","full_name":"jkone27/cat","owner":"jkone27","description":"C# abstract types","archived":false,"fork":false,"pushed_at":"2019-06-13T21:16:20.000Z","size":77,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-12T19:36:18.505Z","etag":null,"topics":["either","either-monad","maybe","maybe-monad","net45","netstandard","option","railway-oriented-programming","result","rop"],"latest_commit_sha":null,"homepage":"","language":"C#","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/jkone27.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-08-01T08:17:56.000Z","updated_at":"2018-08-03T15:14:29.000Z","dependencies_parsed_at":"2023-06-09T00:45:15.565Z","dependency_job_id":null,"html_url":"https://github.com/jkone27/cat","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jkone27/cat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkone27%2Fcat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkone27%2Fcat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkone27%2Fcat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkone27%2Fcat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jkone27","download_url":"https://codeload.github.com/jkone27/cat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkone27%2Fcat/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259520276,"owners_count":22870412,"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":["either","either-monad","maybe","maybe-monad","net45","netstandard","option","railway-oriented-programming","result","rop"],"created_at":"2025-03-31T04:54:17.104Z","updated_at":"2025-06-12T19:36:19.409Z","avatar_url":"https://github.com/jkone27.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CAT  \r\n[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/jkone27/cat/issues)  \r\nC# abstract types - dotnetstandard 1.0  \r\n  \r\n*- Inspired by FP, F# lang and [ROP](https://fsharpforfunandprofit.com/rop/) -*  \r\n\r\n\r\n![](https://raw.githubusercontent.com/jkone27/cat/master/Pics/Cat01.PNG)\r\n\r\n## Why Cat?\r\n\r\nThis small library with no dependencies at all (except dotnetstandard) was born from the interest of having maybe/option and\r\nresult/choice types in C#, without having to reference bigger library, and in a sort of \"simplified\" way. So it's just some interfaces, implementations and a few handy extension methods, you are welcome for contributions and filing/spotting issues!\r\n\r\n## Option\r\nSimulates a **Maybe/Option** type with generics and interface inheritance. `IOption\u003cT\u003e` exposes bool property `.IsSome` .\r\n`Some\u003cT\u003e` and `None\u003cT\u003e` both implement `IOption\u003cT\u003e`. I kept the generic constraint on `None\u003cT\u003e` because \r\nI think it can anyway help the compiler figure out and not mix up different \"nones\".\r\n\r\n## Result\r\nSimulates **Choice/Either** type via generics and interface inheritance. \r\n\r\n### Result.Strict\r\nStrict version has more static \"strictness\", meaning you need to provide more type constraints, but then type inference will work its magic out when possible. `IResult\u003cTOk,TError\u003e` exposes `bool .IsSuccess`, `TOk .AsSuccess` and `TError .Error`.  \r\nYou will get a runtime exception if you try to access `.Error` on a `Success\u003cTOk,TError\u003e` and `.AsSuccess` on a `Failure\u003cTOk,TError\u003e` instance . \r\n\r\n### Result.Loose\r\nLoose is more dynamic, skipping some typechecks, it's a light-weight version,\r\nbut be aware of your types, it is more likely to throw at runtime for invalid casts.  \r\n`IResult` exposes `bool .IsSuccess` and is implemented by `Success\u003cT\u003e` and `Failure\u003cT\u003e`. You will need to cast or make use of the \"as\" operator when converting back to the concrete type from `IResult`, thus providing the generic type constraint.\r\n\r\n## Then\r\nBoth option and result types were given a `.Then` extension method, which in a way (for the little I know) emulates the monadic\r\n**bind** operator, making available the \"unwrapped\" result to the next computation, or just returning to the end of the \"pipeline\" if\r\nthe option is `None`, or if the result is `Failure`.\r\n\r\n## ThenLift\r\nResults types supports `.ThenLift` method for cases where the next step in the pipeline might want to return an `IResult/IResult\u003cTOk,TError\u003e` too, it's not wrapped internally, that's why I named it Lift: is going from a type `T` to a lifted-wrapped type `W\u003cT\u003e`. If you try to pass an `IResult` returning function to a `.Then` method for the Loose type, as a continuation, it will throw you an `ArgumentException` \"use ThenLift instead!\".\r\n\r\n### Status\r\n[![build status](https://img.shields.io/travis/jkone27/cat.svg)](https://travis-ci.org/jkone27/cat)\r\n\r\n#### CatCat\r\nhttps://www.nuget.org/packages/CatCat/\r\n\r\n\r\n## Usage\r\n\r\n### Option\r\n\r\nHere is some basic usage of the `IOption\u003cT\u003e` type. Please note that `null` references, as well as `Nullable\u003cT\u003e` where `T` is a value type and `null` strings, are all converted into `None\u003cT\u003e`.\r\n  \r\n```cs\r\nvar r = \"not null\".ToOption()\r\n              .Then(z =\u003e \"hello\")\r\n              .Then(x =\u003e \"1\")\r\n              .Then(x =\u003e Int32.Parse(x) as int?)\r\n              .Then(x =\u003e DateTime.Now)\r\n              .Then(x =\u003e null as string);\r\n\r\nif (!r.IsSome)\r\n{\r\n    Console.WriteLine(\"r is None\");\r\n}\r\nelse\r\n{\r\n    Console.WriteLine($\"r is Some!: {r.UnwrapSome()}\");\r\n}\r\n\r\n```\r\n\r\nlet's now consider a service which might fail with an exception during invokation, \r\nit will come handy when considering the use of the Result types.\r\n\r\n```cs\r\npublic class ServiceWhichMayFail\r\n{\r\n    public string CallWhichMayFail(bool fail)\r\n    {\r\n        if (fail)\r\n            throw new Exception(\"fail\");\r\n\r\n        else return \"ok\";\r\n    }\r\n}\r\n```\r\n\r\n### Loose Result\r\n\r\nHere is a wrapped version of that same service first:\r\n\r\n```cs\r\npublic class WrappedService\r\n{\r\n    private readonly ServiceWhichMayFail service;\r\n\r\n    public WrappedService(ServiceWhichMayFail service)\r\n    {\r\n        this.service = service;\r\n    }\r\n    public IResult CallWhichMightFail(bool fail)\r\n    {\r\n        try\r\n        {\r\n            return service.CallWhichMayFail(fail).ToSuccess();\r\n        }\r\n        catch (Exception ex)\r\n        {\r\n            return ex.ToFailure();\r\n        }\r\n    }\r\n}\r\n```\r\nand we can then proceed to use the `IResult` and `.Then/ThenLift` extensions as follow:\r\n```cs\r\nvar x = new WrappedService(new ServiceWhichMayFail());\r\n\r\nvar result = x.CallWhichMightFail(false)\r\n    .Then\u003cstring, int\u003e(s =\u003e 3)\r\n    .ThenLift\u003cint\u003e(z =\u003e x.CallWhichMightFail(false))\r\n    .ThenLift\u003cstring\u003e(z =\u003e x.CallWhichMightFail(true))\r\n    .ThenLift\u003cstring\u003e(z =\u003e x.CallWhichMightFail(false));\r\n\r\nif (result.IsSuccess)\r\n{\r\n    var r = ((Success\u003cstring\u003e) result).Data;\r\n    Console.WriteLine($\"success: {r}\");\r\n}\r\nelse\r\n{\r\n    var ex = ((Failure\u003cException\u003e) result).Error;\r\n    Console.WriteLine($\"failure: {ex}\");\r\n}\r\n```\r\n\r\n### Strict Result\r\nand now let's see the different behaviour of strict result, once we wrap again our \"could-be\" failing service,\r\nwrapped with a strict `IResult`, which has more static type checking, thus providing better type inference in method chains:  \r\n```cs\r\npublic class WrappedServiceStrict\r\n{\r\n    private readonly ServiceWhichMayFail service;\r\n\r\n    public WrappedServiceStrict(ServiceWhichMayFail service)\r\n    {\r\n        this.service = service;\r\n    }\r\n    public IResult\u003cstring,Exception\u003e CallWhichMightFail(bool fail)\r\n    {\r\n        try\r\n        {\r\n            return service.CallWhichMayFail(fail).ToSuccess\u003cstring,Exception\u003e();\r\n        }\r\n        catch (Exception ex)\r\n        {\r\n            return ex.ToFailure\u003cstring,Exception\u003e();\r\n        }\r\n    }\r\n}\r\n\r\n```\r\nand now the usage, you can see that is no more necessary to declare the type constraints in `.Then` calls (type inference works):  \r\n\r\n```cs\r\n var x = new WrappedServiceStrict(new ServiceWhichMayFail());\r\n\r\nvar result = x.CallWhichMightFail(false)\r\n    .Then(s =\u003e 3)\r\n    .ThenLift(z =\u003e x.CallWhichMightFail(false))\r\n    .ThenLift(z =\u003e x.CallWhichMightFail(false))\r\n    .ThenLift(z =\u003e x.CallWhichMightFail(false))\r\n    .Then(_ =\u003e 43);\r\n\r\nif (result.IsSuccess)\r\n{\r\n    var r = result.AsSuccess;\r\n    Console.WriteLine($\"success: {r}\");\r\n}\r\nelse\r\n{\r\n    var ex = result.Error;\r\n    Console.WriteLine($\"failure: {ex}\");\r\n}\r\n```\r\nLet me know any improvements/issues, feel free to pr.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkone27%2Fcat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjkone27%2Fcat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkone27%2Fcat/lists"}