{"id":16287479,"url":"https://github.com/tolikpylypchuk/matchmaker","last_synced_at":"2025-03-20T03:30:25.105Z","repository":{"id":130743943,"uuid":"252962355","full_name":"TolikPylypchuk/Matchmaker","owner":"TolikPylypchuk","description":"A library which enables more powerful pattern matching in C#","archived":false,"fork":false,"pushed_at":"2024-09-03T21:07:03.000Z","size":1623,"stargazers_count":13,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-28T22:47:44.489Z","etag":null,"topics":["c-sharp","functional-programming","net-standard","pattern-matching"],"latest_commit_sha":null,"homepage":"https://matchmaker.tolik.io","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/TolikPylypchuk.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2020-04-04T10:02:03.000Z","updated_at":"2024-10-30T16:29:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"280f3c01-aca2-422d-a3ef-a11bdd8198e2","html_url":"https://github.com/TolikPylypchuk/Matchmaker","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TolikPylypchuk%2FMatchmaker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TolikPylypchuk%2FMatchmaker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TolikPylypchuk%2FMatchmaker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TolikPylypchuk%2FMatchmaker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TolikPylypchuk","download_url":"https://codeload.github.com/TolikPylypchuk/Matchmaker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244047544,"owners_count":20389203,"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":["c-sharp","functional-programming","net-standard","pattern-matching"],"created_at":"2024-10-10T19:45:15.232Z","updated_at":"2025-03-20T03:30:25.099Z","avatar_url":"https://github.com/TolikPylypchuk.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Matchmaker\n\n[![NuGet](https://img.shields.io/nuget/v/Matchmaker.svg)](https://www.nuget.org/packages/Matchmaker/)\n\nA library which enables more powerful pattern matching than is currently available in the C#'s `switch`\nstatement/expression.\n\nThis library is a successor of [PatternMatching](https://github.com/TolikPylypchuk/PatternMatching).\nVersion 1.x can be found there. This repository contains version 2+.\n\n## Installation\n\nIf your platform supports .NET Standard 2.1, you can install the latest version:\n\n```\ndotnet add package Matchmaker --version 3.0.1\n```\n\nIf it doesn't, then stick to versions 2.x:\n\n```\ndotnet add package Matchmaker --version 2.1.0\n```\n\n## A Simple Example\n\nThis is what the simplest match expression looks like:\n\n```\nusing static Matchmaker.Patterns.Pattern;\n\n// ...\n\nstring result =\n    Match.Create\u003cint, string\u003e()\n        .Case(EqualTo(1), _ =\u003e \"one\")\n        .Case(EqualTo(2), _ =\u003e \"two\")\n        .Case(EqualTo(3), _ =\u003e \"three\")\n        .Case(EqualTo(4), _ =\u003e \"four\")\n        .Case(Any\u003cint\u003e(), i =\u003e i.ToString())\n        .ExecuteOn(5);\n```\n\n`EqualTo` is a predefined pattern.\n\nThis is what an equivalent `switch` statement looks like (pre-C# 8):\n\n```\nstring result;\nint i = 5;\n\nswitch (i)\n{\n    case 1:\n        result = \"one\";\n        break;\n    case 2:\n        result = \"two\";\n        break;\n    case 3:\n        result = \"three\";\n        break;\n    case 4:\n        result = \"four\";\n        break;\n    default:\n        result = i.ToString();\n        break;\n}\n```\n\nWhile this example doesn't show the full power of pattern matching, there are a few things to note here:\n\n- The match expression yields a result. We don't have to assign the result explicitly in each case.\n\n- The input of the match expression is specified _after_ all the cases. This allows us to save the match expression\nin an object, and use it multiple times on different input values.\n\n- The default case is a pattern, just like any other. It's called `Any` and is always matched successfully.\n\n- Like in `switch` the patterns are tried out sequentially. This means that the `Any` pattern should always\ncome last.\n\nC# 8 included a new way to write `switch` expressions which yield a value, and C# 9 extended it quite a bit. This\ndrastically reduced the need for external libraries like this one for pattern matching. However, this library lets the\nuser define arbitrary patterns, which makes this library more powerful than the `switch` expressions.\n\nHere's what the equivalent switch expression looks like in C# 8:\n\n```\nint i = 5;\n\nstring result = i switch\n{\n    1 =\u003e \"one\",\n    2 =\u003e \"two\",\n    3 =\u003e \"three\",\n    4 =\u003e \"four\",\n    _ =\u003e i.ToString()\n};\n```\n\nOK, this is much shorter and cleaner than the previous two examples. But this library shines when the patterns are\nmore complex. While C# allowes various kinds of patterns, this library allows anything you can think about.\n\n## Another Example\n\nLet's define a simple list, implemented as [cons cells](https://en.wikipedia.org/wiki/Cons). This list is not\ngeneric for simplicity.\n\n```\npublic abstract class ConsList\n{\n    private protected ConsList()\n    { }\n\n    public static ConsList Cell(int head, ConsList tail) =\u003e\n        new ConsCell(head, tail);\n\n    public static ConsList Empty =\u003e\n        new Empty();\n}\n\npublic sealed class ConsCell : ConsList\n{\n    public int Head { get; }\n    public ConsList Tail { get; }\n\n    internal ConsCell(int head, ConsList tail)\n    {\n        this.Head = head;\n        this.Tail = tail;\n    }\n}\n\npublic sealed class Empty : ConsList\n{\n    internal Empty()\n    { }\n}\n```\n\nNow let's look what pattern matching on the list would look like. Let's create\na function which finds the sum of all items of the list.\n\n```\npublic int Sum(ConsList list) =\u003e\n    Match.Create\u003cConsList, int\u003e()\n        .Case\u003cConsCell\u003e(cell =\u003e cell.Head + Sum(cell.Tail))\n        .Case\u003cEmpty\u003e(_ =\u003e 0)\n        .ExecuteOn(list);\n```\n\n`Case\u003cTType\u003e(...)` is the same as `Case(Pattern.Type\u003cTInput, TType\u003e(), ...)`.\n\nHere is the equivalent function implemented using the `switch` statement (pre-C# 8):\n\n```\npublic int Sum(ConsList list)\n{\n    switch (list)\n    {\n        case ConsCell cell:\n            return cell.Head + Sum(cell.Tail);\n        case Empty _:\n            return 0;\n    }\n\n    throw new MatchException(\"This will never happen, but C# can't know that.\");\n}\n```\n\nAs you can see, we have to throw an exception in the `switch` version, because C# can't know that `ConsCell`\nand `Empty` are the only possible subclasses of `ConsList`. And for that reason, if we forget to define one\nof the cases in `switch` or in a match, we'll get an exception. In F# a warning is issued when the match is\nincomplete, but C# doesn't have the notion of complete or incomplete matches.\n\nWith C# 8 there's a better way to do this, but we still have to explicitly throw an exception\nin the default case (which we know won't happen):\n\n```\npublic int Sum(ConsList list) =\u003e\n    list switch\n    {\n        ConsCell cell =\u003e cell.Head + Sum(cell.Tail),\n        Empty _ =\u003e 0,\n        _ =\u003e throw new MatchException(\"This will never happen, but C# can't know that.\");\n    };\n}\n```\n\n## Matching with Fall-through\n\nC, C++ and, Java support fall-through in `switch` statements. So does this library, although it works differently here.\nYou can read more [here](https://matchmaker.tolik.io/v3.0.1/articles/expressions.html#matching-with-fall-through).\n\nHere's an implementation of the famous fizz-buzz program which uses matching with fall-through:\n\n```\nusing System.Linq;\n\nusing Matchmaker;\nusing Matchmaker.Linq;\n\nusing static Matchmaker.Patterns.Pattern;\n\n// ...\n\nIPattern\u003cint, int\u003e DivisibleBy(int n) =\u003e\n    CreatePattern\u003cint\u003e(input =\u003e input % n == 0);\n\nvar result = Enumerable.Range(0, 15)\n    .Select(Match.Create\u003cint, string\u003e(fallthroughByDefault: true)\n        .Case(DivisibleBy(3), _ =\u003e \"Fizz\")\n        .Case(DivisibleBy(5), _ =\u003e \"Buzz\")\n        .Case(Not(DivisibleBy(3).Or(DivisibleBy(5))), n =\u003e n.ToString())\n        .ToFunctionWithFallthrough())\n    .Select(items =\u003e items.Aggregate(String.Concat))\n    .ToList();\n\n// The result is (\"FizzBuzz\", \"1\", \"2\", \"Fizz\", \"4\", \"Buzz\", \"Fizz\", \"7\", \"8\", \"Fizz\", \"Buzz\", \"11\", \"Fizz\", \"13\", \"14\", \"FizzBuzz\");\n```\n\n## Static Match Expressions\n\nOne pain point of match expressions is that whenever a method which contains a match expression is executed, the match\nexpression is initialized from scratch every time. This can be solved with static match expressions. Take a look at\nthe revised simple example:\n\n```\nstring result = Match.CreateStatic\u003cint, string\u003e(match =\u003e match\n        .Case(EqualTo(1), _ =\u003e \"one\")\n        .Case(EqualTo(2), _ =\u003e \"two\")\n        .Case(EqualTo(3), _ =\u003e \"three\")\n        .Case(EqualTo(4), _ =\u003e \"four\")\n        .Case(Any\u003cint\u003e(), i =\u003e i.ToString()))\n    .ExecuteOn(5);\n```\n\nNow this match expression will be initialized only once even if its containing method is executed multiple times.\nYou can read more [here](https://matchmaker.tolik.io/v3.0.1/articles/expressions.html#static-match-expressions).\n\n## More Info\n\nIf you want to learn how to use this library, you should read the [documentation](https://matchmaker.tolik.io/v3.0.1).\nThe articles provide everything you need to know to use this library.\n\nIf you need extensive information, go to the [API reference](https://matchmaker.tolik.io/v3.0.1/api/index.html).\n\nIf you need even more info about this library, you can go through the\n[tests](https://github.com/TolikPylypchuk/Matchmaker/tree/v3.0.1/Matchmaker.Tests). They are property-based and as such\nthey describe every aspect of the classes and their members.\n\nThe documentation can be found here:\n\n- Version 3.0.1: https://matchmaker.tolik.io/v3.0.1\n- Version 3.0.0: https://matchmaker.tolik.io/v3.0.0\n- Version 2.1.0: https://matchmaker.tolik.io/v2.1.0\n- Version 2.0.0: https://matchmaker.tolik.io/v2.0.0\n- Older versions: https://github.com/TolikPylypchuk/PatternMatching\n\n## Is This Library Still Maintained?\n\nI'm not planning on writing new versions beyond 3.0 (or maybe 3.1 if some stuff needs fixing). To be fair, I thought\nthe same thing after releasing version 1.1 and yet here we are. This time I do believe that this library has enough\nfeatures (probably more than enough). Maybe one day I'll revisit this decision, but for now (January 2022) this is it;\nthis is as good as it gets.\n\nThat said, if you report a bug or request a new feature, I'll definitely look into it. I'm not giving up on this\nlibrary any time soon.\n\n## License\n\n[MIT License](https://github.com/TolikPylypchuk/Matchmaker/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftolikpylypchuk%2Fmatchmaker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftolikpylypchuk%2Fmatchmaker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftolikpylypchuk%2Fmatchmaker/lists"}