{"id":13545081,"url":"https://kag0.github.io/tail","last_synced_at":"2025-04-02T15:30:53.940Z","repository":{"id":151477498,"uuid":"246714603","full_name":"nrktkt/tail","owner":"nrktkt","description":"simple tail call optimization and stack safety for Java","archived":false,"fork":false,"pushed_at":"2023-03-22T17:10:04.000Z","size":33,"stargazers_count":25,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-03T12:31:59.407Z","etag":null,"topics":["java","tail-call-optimization","trampolines"],"latest_commit_sha":null,"homepage":"https://nrktkt.github.io/tail/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nrktkt.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}},"created_at":"2020-03-12T01:15:52.000Z","updated_at":"2024-09-26T17:16:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"556e2ca8-90fb-4b99-9d65-46f287f63325","html_url":"https://github.com/nrktkt/tail","commit_stats":null,"previous_names":["kag0/tail"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nrktkt%2Ftail","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nrktkt%2Ftail/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nrktkt%2Ftail/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nrktkt%2Ftail/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nrktkt","download_url":"https://codeload.github.com/nrktkt/tail/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246841563,"owners_count":20842615,"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":["java","tail-call-optimization","trampolines"],"created_at":"2024-08-01T11:00:57.517Z","updated_at":"2025-04-02T15:30:53.647Z","avatar_url":"https://github.com/nrktkt.png","language":"Java","funding_links":[],"categories":["Projects"],"sub_categories":["Development"],"readme":"# com.github.kag0.tail\n\nsimple tail call optimization for Java\n\nenables infinitely deep [tail recursive calls](https://en.wikipedia.org/wiki/Tail_call) without throwing a `StackOverflowError` \n\nno transitive dependencies\n\n## Install\n[![](https://jitpack.io/v/nrktkt/tail.svg)](https://jitpack.io/#nrktkt/tail)\n\nadd the jitpack repository\n```xml\n\u003crepositories\u003e\n...\n  \u003crepository\u003e\n    \u003cid\u003ejitpack.io\u003c/id\u003e\n    \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n  \u003c/repository\u003e\n...\n\u003c/repositories\u003e\n```\nadd the dependency\n```xml\n\u003cdependencies\u003e\n...\n  \u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.nrktkt\u003c/groupId\u003e\n    \u003cartifactId\u003etail\u003c/artifactId\u003e\n    \u003cversion\u003eTag\u003c/version\u003e\n  \u003c/dependency\u003e\n...\n\u003c/dependencies\u003e\n```\n\n## Use\n\n```java\nimport com.github.kag0.tail.Tail;\nimport static com.github.kag0.tail.Tail.*;\n\nTail\u003cVoid\u003e infiniteLoop(int i) {\n  System.out.println(\"Loop \" + i + \", stack still intact!\");\n  return call(() -\u003e infiniteLoop(i + 1));\n}\n\ninfiniteLoop(0).evaluate();\n```\n\n### Example: tail optimizing factorial computation\n\n#### un-optimized first version\n\nlet's start with a simple recursive method to compute the `n`th factorial. \nthis code will throw a `StackOverflowError` for large values of `n`.\n\n```java\nlong factorial(long n) {\n  if(n == 1) return 1;\n  else return n * factorial(n - 1);\n}\n```\n\n#### move the recursive call into the tail position\n\nthe tail position is just another way of saying \n\"the last thing you do before the `return`\".\n\n```java\nlong factorial(long fact, long n) {\n  if(n.equals(1)) return fact;\n  return factorial(fact * n, n - 1);\n}\n```\n\nthis may require a slight refactor, \nusually to add an additional parameter to accumulate progress.\n\n#### wrap the return type in `Tail`\n\nthis will enforce that the recursive call is in the tail position.\n\n```java\nTail\u003cLong\u003e factorial(long fact, long n)\n```\n\n#### wrap base cases with `done`\n\n```java\nif(n.equals(0)) return done(fact);\n```\n\n#### wrap recursive calls with `call`\n\n```java\nreturn call(() -\u003e factorial(fact * n, n - 1));\n```\n\n#### profit\n\ncall `.evaluate()` on the invocation of your method.\n\n```java\nfactorial(1, Long.MAX_VALUE).evaluate();\n```\n\nrecursive methods no longer blow the stack.  \nnote that if you skip the 'move the recursive call into the tail position' \nstep, the code will not compile because the method is not tail recursive \nand therefore not stack safe. thanks to `Tail` that is covered by type safety.\n\n### making safe recursive calls outside the tail position\n\nin addition to making tail recursion safe, \nwe can also use trampolining to enable recursive methods \nthat would otherwise be tricky to make tail recursive.\n\nto do this, just use `.flatMap` to chain two `call`s together.  \nfor example\n\n```java\nTail\u003cInteger\u003e ackermann(int m, int n) {\n  if(m == 0) \n    return done(n + 1);\n  if(m \u003e 0 \u0026\u0026 n == 0) \n    return call(() -\u003e ackermann(m - 1, 1));\n  if(m \u003e 0 \u0026\u0026 n \u003e 0) \n    return call(() -\u003e ackermann(m, n - 1)).flatMap(nn -\u003e ackermann(m - 1, nn));\n  throw new IllegalArgumentException();\n}\n```\n\n## [Benchmarks](benchmark.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/kag0.github.io%2Ftail","html_url":"https://awesome.ecosyste.ms/projects/kag0.github.io%2Ftail","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/kag0.github.io%2Ftail/lists"}