{"id":17157072,"url":"https://github.com/teo-tsirpanis/recursiont","last_synced_at":"2025-07-18T08:02:54.671Z","repository":{"id":62908137,"uuid":"532070500","full_name":"teo-tsirpanis/Recursiont","owner":"teo-tsirpanis","description":"Infinite recursion without blowing up the stack.","archived":false,"fork":false,"pushed_at":"2025-07-01T22:16:52.000Z","size":114,"stargazers_count":20,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"mainstream","last_synced_at":"2025-07-07T19:51:07.689Z","etag":null,"topics":["csharp","dotnet","hacktoberfest","recursion"],"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/teo-tsirpanis.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":"2022-09-02T20:30:19.000Z","updated_at":"2025-06-01T22:21:00.000Z","dependencies_parsed_at":"2024-05-06T01:42:38.859Z","dependency_job_id":"403e71a3-77c0-4b5b-8ece-1925bb660ff0","html_url":"https://github.com/teo-tsirpanis/Recursiont","commit_stats":{"total_commits":113,"total_committers":2,"mean_commits":56.5,"dds":"0.48672566371681414","last_synced_commit":"cf7fdbd2b8f8453ec53df642925c2071e522755e"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/teo-tsirpanis/Recursiont","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teo-tsirpanis%2FRecursiont","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teo-tsirpanis%2FRecursiont/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teo-tsirpanis%2FRecursiont/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teo-tsirpanis%2FRecursiont/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/teo-tsirpanis","download_url":"https://codeload.github.com/teo-tsirpanis/Recursiont/tar.gz/refs/heads/mainstream","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teo-tsirpanis%2FRecursiont/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265724159,"owners_count":23817741,"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":["csharp","dotnet","hacktoberfest","recursion"],"created_at":"2024-10-14T22:08:09.633Z","updated_at":"2025-07-18T08:02:54.628Z","avatar_url":"https://github.com/teo-tsirpanis.png","language":"C#","readme":"![Licensed under the MIT License](https://img.shields.io/github/license/teo-tsirpanis/recursiont.svg)\n[![NuGet](https://img.shields.io/nuget/v/Recursiont.svg)](https://nuget.org/packages/Recursiont)\n[![Test](https://github.com/teo-tsirpanis/Recursiont/actions/workflows/ci.yml/badge.svg?branch=mainstream\u0026event=push)](https://github.com/teo-tsirpanis/Recursiont/actions/workflows/ci.yml)\n\n# Recursion't\n\nRecursion't is a C# library that allows efficiently implementing recursive algorithms without worrying for stack overflows. It achieves this by creatively using `async` method builders.\n\n## Features\n\n* __Lightweight__ If recursion in a function does not go too deep, Recursion't will not allocate.\n* __Single-threaded__ A common way to tackle infinite recursion is by keeping recursing in a different thread. Recursion't runs entirely in one thread.\n* __Memory-efficient__ Recursion't uses techniques like object pooling to keep steady-state memory allocations low.\n\n## How to use\n\nYou can convert a recursive function to use Recursion't by following these steps:\n\n1. Add `using Recursiont;` to your code.\n2. Make the function `async`.\n3. Change the function's return type to `RecursiveOp` if it returns `void`, or to `RecursiveOp\u003cT\u003e` if it returns `T`.\n4. `await` the recursive calls.\n5. Create a helper function that uses `RecursiveRunner.Run` that serves as the entry point for your recursive function.\n\nTo see an example, imagine the following recursive function that computes the [Ackermann function](https://en.wikipedia.org/wiki/Ackermann_function):\n\n```csharp\nstatic uint Ackermann(uint m, uint n)\n{\n    if (m == 0)\n    {\n        return n + 1;\n    }\n    if (n == 0)\n    {\n        return Ackermann(m - 1, 1);\n    }\n    return Ackermann(m - 1, Ackermann(m, n - 1));\n}\n```\n\nIf you call `Ackermann(4, 1)` your code will crash with a stack overflow. Here's the same function, rewritten to use Recursion't:\n\n```csharp\nusing Recursiont;\n\nstatic uint Ackermann(uint m, uint n)\n{\n    return RecursiveRunner.Run(AckermannImpl, m, n);\n\n    static async RecursiveOp\u003cuint\u003e AckermannImpl(uint m, uint n)\n    {\n        if (m == 0)\n        {\n            return n + 1;\n        }\n        if (n == 0)\n        {\n            return await AckermannImpl(m - 1, 1);\n        }\n        return await AckermannImpl(m - 1, await AckermannImpl(m, n - 1));\n    }\n}\n```\n\nNow, calling `Ackermann(4, 1)` will not cause stack overflows but will still take an extraordinary amount of time to complete.\n\n`RecursiveRunner.Run` has overloads that accept functions with up to six parameters. If your function has more you can create a lambda that accepts a tuple:\n\n```csharp\nRecursiveRunner.Run(static ((int X1, int X2, int X3, int X4, int X5, int X6, int X7) x) =\u003e\n    F(x.X1, x.X2, x.X3, x.X4, x.X5, x.X6, x.X7), (0, 0, 0, 0, 0, 0, 0));\n\nstatic async RecursiveOp F(int x1, int x2, int x3, int x4, int x5, int x6, int x7)\n{\n    // ...\n}\n```\n\n## The rules\n\nUsing Recursion't comes with a set of simple rules that you have to follow.\n\n\u003e The term \"recursive functions\" refers to methods that return `RecursiveOp` or `RecursiveOp\u003cT\u003e`.\n\n*\n    __Immediately `await` after calling recursive functions.__ Recursion't is not a coroutine library. The following code might either fail or produce unexpected results:\n\n    ```csharp\n    static async RecursiveOp Foo()\n    {\n        RecursiveOp op1 = Bar();\n        RecursiveOp op2 = Bar();\n        await op1;\n        await op2;\n    }\n    ```\n\n*\n    __Do not call recursive functions outside of other recursive functions.__ The only way to call a recursive function from a non-recursive one is by passing a delegate to it in `RecursiveRunner.Run`. Consider the following code:\n\n    ```csharp\n    // ❌ Wrong\n    F();\n    // ✅ Correct\n    RecursiveRunner.Run(F);\n\n    static async RecursiveOp F()\n    {\n        // ...\n    }\n    ```\n\n*\n    __Exercise caution when using `RecursiveRunner.Run` inside recursive functions.__ Let's say you have the following two functions:\n\n    ```csharp\n    static void A()\n    {\n        RecursiveRunner.Run(AImpl);\n\n        static async RecursiveOp AImpl()\n        {\n            // ...\n        }\n    }\n\n    static void B()\n    {\n        RecursiveRunner.Run(BImpl);\n\n        static async RecursiveOp BImpl()\n        {\n            // ...\n            A();\n            // ...\n        }\n    }\n    ```\n\n    `B` is inefficient and -if `A` and `B` are mutually recursive- downright dangerous and prone to stack overflows. The way Recursion't avoids stack overflows is by storing the stack to the heap when it goes too deep, up to the last point where `RecursiveRunner.Run` was called. Using it in recursive functions limits how many stack frames can be moved to the heap.\n\n    What you can do is to make `A()` directly return `RecursiveOp` and call and `await` it from B.\n\n    ```csharp\n    static async RecursiveOp A()\n    {\n        // ...\n    }\n\n    static void B()\n    {\n        RecursiveRunner.Run(BImpl);\n\n        static async RecursiveOp BImpl()\n        {\n            // ...\n            await A();\n            // ...\n        }\n    }\n    ```\n\n    \u003e Unless you are doing things like recursive user callbacks, you don't have to expose recursive functions in a public API. You can create a public wrapper function that calls `RecursiveRunner.Run` and keep Recursion't as an implementation detail.\n\n## Maintainer(s)\n\n- [@teo-tsirpanis](https://github.com/teo-tsirpanis)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteo-tsirpanis%2Frecursiont","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteo-tsirpanis%2Frecursiont","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteo-tsirpanis%2Frecursiont/lists"}