{"id":37485304,"url":"https://github.com/awsxdr/func","last_synced_at":"2026-01-16T07:32:01.373Z","repository":{"id":52370031,"uuid":"252954588","full_name":"awsxdr/func","owner":"awsxdr","description":"Library to extend C#'s functional programming capabilities","archived":false,"fork":false,"pushed_at":"2024-02-16T15:56:55.000Z","size":144,"stargazers_count":8,"open_issues_count":2,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-30T20:33:15.346Z","etag":null,"topics":["csharp","functional-programming","railway-oriented-programming"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/Func/","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/awsxdr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-04T09:15:28.000Z","updated_at":"2023-10-18T15:35:20.000Z","dependencies_parsed_at":"2022-09-13T17:01:08.402Z","dependency_job_id":null,"html_url":"https://github.com/awsxdr/func","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/awsxdr/func","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsxdr%2Ffunc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsxdr%2Ffunc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsxdr%2Ffunc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsxdr%2Ffunc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awsxdr","download_url":"https://codeload.github.com/awsxdr/func/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsxdr%2Ffunc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28478047,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T06:30:42.265Z","status":"ssl_error","status_checked_at":"2026-01-16T06:30:16.248Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["csharp","functional-programming","railway-oriented-programming"],"created_at":"2026-01-16T07:32:00.575Z","updated_at":"2026-01-16T07:32:01.365Z","avatar_url":"https://github.com/awsxdr.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Func\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/awsxdr/Func/blob/master/LICENSE)\n![Release](https://github.com/awsxdr/func/workflows/Release/badge.svg)\n![CI](https://github.com/awsxdr/func/workflows/CI/badge.svg?branch=develop)\n\nFunc is a library designed to expand C#'s functional programming capabilities.\n\n## Contents\n\n* [Usage](#usage)\n  * [Map \u0026 Tee](#map-and-tee)\n  * [Options](#options)\n  * [Railway-oriented programming](#railway-oriented-programming-result)\n\n## Usage\n\n### Map and Tee\n\n`Map` and `Tee` are methods used to chain method calls. They can be thought of as equivalent to `Select` and `ForEach` but operating on single items.\n\n`Map` takes in a value, executes a given function on it and returns the result. This is similar to the behaviour of the `|\u003e` operator in F#.\n\n`Tee` takes in a value, executes a given method, and returns the original value.\n\n```csharp\nusing Func;\n\npublic class Example\n{\n    private readonly ILogger _logger;\n\n    public Example(ILogger logger) =\u003e _logger = logger;\n\n    public string GetWelcomeMessage() =\u003e\n        GetCurrentUserId()\n        .Tee(id =\u003e _logger.LogInformation(\"Generating welcome message for user {0}\", id))\n        .Map(GetUserNameFromId)\n        .Map(GetWelcomeMessageForUserName);\n\n    private int GetCurrentUserId() =\u003e 123;\n    private string GetUserNameFromId(int id) =\u003e \"Test User\";\n    private string GetWelcomeMessageForUserName(string userName) =\u003e $\"Hello, {userName}\";\n}\n```\n\n#### Asynchronous operations\n\n`Map` and `Tee` are designed to work happily with asynchronous functions returning `Task` objects. Methods returning tasks which are chained together will return a single unified task.\n\n```csharp\nusing Func;\n\npublic class Example\n{\n    private readonly ILogger _logger;\n\n    public Example(ILogger logger) =\u003e _logger = logger;\n\n    public async Task\u003cstring\u003e GetWelcomeMessage() =\u003e\n        await GetCurrentUserId()\n        .Tee((int id) =\u003e _logger.LogInformation(\"Generating welcome message for user {0}\", id))\n        .Map(GetUserNameFromId)\n        .Map(GetWelcomeMessageForUserName);\n\n    private Task\u003cint\u003e GetCurrentUserId() =\u003e Task.FromResult(123);\n    private Task\u003cstring\u003e GetUserNameFromId(int id) =\u003e Task.FromResult(\"Test User\");\n    private string GetWelcomeMessageForUserName(string userName) =\u003e $\"Hello, {userName}\";\n}\n```\n\nHere, several methods are chained together; some of them returning tasks, some not. A single `Task` is returned which performs all of the chained actions.\n\nOf note here is the use of `int` on `Tee`. Generally the compiler will be able to infer the types being used, but in cases like this where the argument is of type `object` a type needs specifying. If you encounter these sort of errors you can provide the types or, perhaps preferably, provide a more strongly typed method to call.\n\n#### Multiple arguments\n\n`Map` and `Tee` support methods with up to 15 arguments. Additional arguments are provided as parameters to `Map` and `Tee` following the method. The value that `Map` or `Tee` is acting on is always passed as the last argument.\n\nIn the following example, we call `string.Join` which has the signature `string Join(string separator, IEnumerable\u003cstring\u003e values)`. `Map` is called on the string array and the separator is passed as an argument to `Map`.\n\n```csharp\npublic class Example\n{\n    public void OutputUserNames() =\u003e\n        GetUserNames()\n        .Map(string.Join, \", \")\n        .Tee(Console.WriteLine);\n\n    private IEnumerable\u003cstring\u003e GetUserNames() =\u003e new[] { \"Anne\", \"Brian\", \"Claire\", \"Daniel\" };\n}\n```\n\n### Option\n\nOptions represent a way of marking a value as optional. Optional items offer benefits over nullable items because they enforce checking for a value and thus remove the possibility of ending up with the dreaded `NullReferenceException`s.\n\n```csharp\nusing static Func.Option;\n\npublic class Example\n{\n    public static string GetDescription(Option\u003cint\u003e value) =\u003e\n        value switch\n        {\n            Some\u003cint\u003e x when x.Value \u003e 10 =\u003e \"Huge\",\n            Some\u003cint\u003e x when x.Value \u003e 5 =\u003e \"Big\",\n            Some\u003cint\u003e x when x.Value \u003c 1 =\u003e \"Tiny\",\n            Some\u003cint\u003e x when x.Value \u003c 5 =\u003e \"Small\",\n            Some\u003cint\u003e _ =\u003e \"Average\",\n            None _ =\u003e \"Empty\",\n            _ =\u003e \"Unexpected!\"\n        };\n\n    public void Test()\n    {\n        _ = GetDescription(Some(11));    // \"Huge\"\n        _ = GetDescription(Some(3));     // \"Small\"\n        _ = GetDescription(None\u003cint\u003e()); // \"Empty\"\n    }\n}\n```\n\nThe above example shows a function which expects either an integer or nothing. The `Value` property can only be retrieved once the `Option` has been cast to `Some`.\n\nAlso supported is passing values when the type isn't specified. In the below example, any type can be passed in.\n\n```csharp\nusing static Func.Option;\n\npublic class Example\n{\n    public static string GetDescription(Option value) =\u003e\n        value switch\n        {\n            Some\u003cint\u003e _ =\u003e \"Number\",\n            Some\u003cdouble\u003e _ =\u003e \"Number\",\n            Some\u003cstring\u003e _ =\u003e \"Text\",\n            Some _ =\u003e \"Something else\",\n            None _ =\u003e \"Empty\",\n            _ =\u003e \"Unexpected!\"\n        };\n\n    public void Test()\n    {\n        GetDescription(Some(11));      // \"Number\"\n        GetDescription(Some(8.42));    // \"Number\"\n        GetDescription(Some(\"Hello\")); // \"Text\"\n        GetDescription(Some(false));   // \"Something else\"\n        GetDescription(None());        // \"Empty\"\n    }\n}\n```\n\n### Result\n\nFunc supports result types which can be returned from functions to indicate success or failure. Functions which return a `Result` object can be chained together with calls to `Then`, `And`, or `Else`. If any method in the chain fails then the chain stops executing and a fail is returned. The concept is similar to Javascript's promises or Rust's `Result` type.\n\nThe aim of results is to prevent the use of exceptions for program flow. Methods can fail on non-exceptional errors to avoid continuing without the overhead of throwing an exception.\n\n```csharp\nusing System;\nusing System.Threading.Tasks;\nusing Func;\nusing static Func.Result;\n\npublic class Example\n{\n    private readonly ILogger _logger;\n\n    public Example(ILogger logger) =\u003e _logger = logger;\n\n    public async Task\u003cstring\u003e GetWelcomeMessage(string username) =\u003e\n        await\n            GetCurrentUserId(username)\n            .Then(GetUserFullNameFromId)\n            .Then(GetWelcomeMessageForUserFullName)\n            .OnSuccess(() =\u003e _logger.LogInformation($\"User {username} found\"))\n            .OnError((UserNotFoundError e) =\u003e { _logger.LogWarn($\"User {username} not found\"); })\n            .OnError(() =\u003e _logger.LogWarn(\"Something went very wrong!\"))\n        switch\n        {\n            Success\u003cstring\u003e s =\u003e s.Value,\n            Failure\u003cUserNotFoundError\u003e _ =\u003e \"User could not be found\",\n            Failure _ =\u003e throw new Exception(\"Unexpected error occurred getting welcome message for user\")\n        };\n\n    private Task\u003cResult\u003cint\u003e\u003e GetCurrentUserId(string username) =\u003e\n        (username == \"test\"\n            ? Succeed(123)\n            : Result\u003cint\u003e.Fail(new UserNotFoundError())\n        ).ToTask();\n\n    private Task\u003cResult\u003cstring\u003e\u003e GetUserFullNameFromId(int id) =\u003e\n        Succeed(\"Test User\").ToTask();\n\n    private Result\u003cstring\u003e GetWelcomeMessageForUserFullName(string userFullName) =\u003e\n        Succeed($\"Hello, {userFullName}\");\n}\n\npublic class UserNotFoundError : ResultError { }\n```\n\nNote here that the call to `Fail` requires the result types to be passed. This is because the types can't be inferred.\n\nAlso note that the error type is specified in the lambda for `OnError`. If not specified here then the method would require both the error type and result type specified in angular brackets.\n\n### Union\n\nA `Union` is used to represent a value which can be one of several types. To determine what type is held by the union, either use the `Is\u003c\u003e` method, or use the `is` keyword on the `Value` property.\n\nThis has a lot of similarities to `Option` but constrains the types which are stored.\n\nThe following example is to show how unions are used. However, it would likely be better to use overloaded methods for this specific case.\n\n```csharp\nusing static Func.Result;\n\npublic class Example\n{\n    public static Result\u003cstring\u003e GetDescription(Union\u003cint, double, string\u003e value) =\u003e\n        (value.Value switch\n        {\n            Some\u003cint\u003e x =\u003e Succeed(x.Value),\n            Some\u003cdouble\u003e x =\u003e Succeed((int)x.Value),\n            Some\u003cstring\u003e x =\u003e\n                int.TryParse(x.Value, out var i)\n                    ? Succeed(i)\n                    : Result\u003cint\u003e.Fail\u003cFormatError\u003e(),\n            _ =\u003e throw new Exception(\"Unexpected type\")\n        })\n        .ThenMap(x =\u003e\n            x \u003e 10 ? \"Huge\"\n            : x \u003e 5 ? \"Big\"\n            : x\u003c 1 ? \"Tiny\"\n            : x\u003c 5 ? \"Small\"\n            : \"Average\"\n        );\n\n    public void Test()\n    {\n        GetDescription(11);      // Success - \"Huge\"\n        GetDescription(8.42);    // Success - \"Big\"\n        GetDescription(\"2\");     // Success - \"Small\"\n        GetDescription(\"Hello\"); // Failure\n    }\n\n    public class FormatError : ResultError {}\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawsxdr%2Ffunc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawsxdr%2Ffunc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawsxdr%2Ffunc/lists"}