{"id":15470460,"url":"https://github.com/truqu/cut","last_synced_at":"2025-06-12T13:06:10.601Z","repository":{"id":57487981,"uuid":"99099548","full_name":"truqu/cut","owner":"truqu","description":null,"archived":false,"fork":false,"pushed_at":"2024-02-20T08:00:12.000Z","size":26,"stargazers_count":8,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2025-05-23T16:51:29.404Z","etag":null,"topics":["erlang","erlang-developement","erlang-libraries","erlang-library"],"latest_commit_sha":null,"homepage":null,"language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/truqu.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}},"created_at":"2017-08-02T09:40:33.000Z","updated_at":"2022-06-17T13:29:15.000Z","dependencies_parsed_at":"2025-03-03T16:47:15.145Z","dependency_job_id":null,"html_url":"https://github.com/truqu/cut","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/truqu/cut","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truqu%2Fcut","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truqu%2Fcut/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truqu%2Fcut/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truqu%2Fcut/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/truqu","download_url":"https://codeload.github.com/truqu/cut/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truqu%2Fcut/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259470938,"owners_count":22862995,"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":["erlang","erlang-developement","erlang-libraries","erlang-library"],"created_at":"2024-10-02T02:04:48.336Z","updated_at":"2025-06-12T13:06:10.516Z","avatar_url":"https://github.com/truqu.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cut\n\n## Introduction\n\nA syntax extension (i.e. parse transform) that adds support for _cuts_ to\nErlang. These are inspired by the [Scheme form of cuts](http://srfi.schemers.org/srfi-26/srfi-26.html).\n\nCuts can be thought of as a light-weight form of abstraction, with\nsimilarities to partial application (or currying).\n\n## Use\n\nTo use this parse-transformer, you must add the necessary\n`-compile` attribute to your Erlang source files. For example:\n\n    -module(test).\n    -compile({parse_transform, cut}).\n    ...\n\nThen, when compiling `test.erl`, you must ensure `erlc` can locate `cut.beam`\nby passing the suitable path to `erlc` with a `-pa` or `-pz` argument. For\nexample:\n\n    erlc -Wall +debug_info -I ./include -pa ebin -o ebin  src/cut.erl\n    erlc -Wall +debug_info -I ./include -pa test/ebin -pa ./ebin -o test/ebin test/src/test.erl\n\n*Note*: If you're using QLC, you may find you need to be careful as to the\nplacement of the parse-transformer attributes. For example, I've found that\n`-compile({parse_transform, cut}).` must occur before\n`-include_lib(\"stdlib/include/qlc.hrl\").`\n\n## Motivation\n\nThe *cut* parse-transformer is motivated by the frequency with which simple\nfunction abstractions are used in Erlang, and the relatively noisy\nnature of declaring `fun`s. For example, it's quite common to see code\nlike:\n\n    with_resource(Resource, Fun) -\u003e\n        case lookup_resource(Resource) of\n            {ok, R}          -\u003e Fun(R);\n            {error, _} = Err -\u003e Err\n        end.\n\n    my_fun(A, B, C) -\u003e\n        with_resource(A, fun (Resource) -\u003e\n                             my_resource_modification(Resource, B, C)\n                         end).\n\nThat is, a `fun` is created in order to perform variable capture\nfrom the surrounding scope but to leave holes for further\narguments to be provided. Using a *cut*, the function `my_fun` can be\nrewritten as:\n\n    my_fun(A, B, C) -\u003e\n        with_resource(A, my_resource_modification(_, B, C)).\n\n\n## Definition\n\nNormally, the variable `_` can only occur in patterns: that is, where a\nmatch occurs. This can be in assignment, in cases, and in function\nheads. For example:\n\n    {_, bar} = {foo, bar}.\n\n*Cut* uses `_` in expressions to indicate where abstraction should\noccur. Abstraction from *cut*s is **always** performed on the\n*shallowest* enclosing expression. For example:\n\n    list_to_binary([1, 2, math:pow(2, _)]).\n\nwill create the expression\n\n    list_to_binary([1, 2, fun (X) -\u003e math:pow(2, X) end]).\n\nand not\n\n    fun (X) -\u003e list_to_binary([1, 2, math:pow(2, X)]) end.\n\nIt is fine to use multiple *cut*s in the same expression, and the\narguments to the created abstraction will match the order in which the\n`_` var is found in the expression. For example:\n\n    assert_sum_3(X, Y, Z, Sum) when X + Y + Z == Sum -\u003e ok;\n    assert_sum_3(_X, _Y, _Z, _Sum) -\u003e {error, not_sum}.\n\n    test() -\u003e\n        Equals12 = assert_sum_3(_, _, _, 12),\n        ok = Equals12(9, 2, 1).\n\nIt is perfectly legal to take *cut*s of *cut*s as the abstraction created\nby the *cut* is a normal `fun` expression and thus can be re-*cut* as\nnecessary:\n\n    test() -\u003e\n        Equals12 = assert_sum_3(_, _, _, 12),\n        Equals5 = Equals12(_, _, 7),\n        ok = Equals5(2, 3).\n\nNote that because a simple `fun` is being constructed by the *cut*, the\narguments are evaluated prior to the *cut* function. For example:\n\n    f1(_, _) -\u003e io:format(\"in f1~n\").\n\n    test() -\u003e\n        F = f1(io:format(\"test line 1~n\"), _),\n        F(io:format(\"test line 2~n\")).\n\nwill print out\n\n    test line 2\n    test line 1\n    in f1\n\nThis is because the *cut* creates `fun (X) -\u003e f1(io:format(\"test line\n1~n\"), X) end`. Thus it is clear that `X` must be evaluated first,\nbefore the `fun` can be invoked.\n\nOf course, no one would be crazy enough to have side-effects in\nfunction argument expressions, so this will never cause any issues!\n\n*Cut*s are not limited to function calls. They can be used in any\nexpression where they make sense:\n\n\n### Tuples\n\n    F = {_, 3},\n    {a, 3} = F(a).\n\n\n### Lists\n\n    dbl_cons(List) -\u003e [_, _ | List].\n\n    test() -\u003e\n        F = dbl_cons([33]),\n        [7, 8, 33] = F(7, 8).\n\nNote that if you nest a list as a list tail in Erlang, it's still\ntreated as one expression. For example:\n\n    A = [a, b | [c, d | [e]]]\n\nis exactly the same (right from the Erlang parser onwards) as:\n\n    A = [a, b, c, d, e]\n\nThat is, those sub-lists, when they're in the tail position, **do not**\nform sub-expressions. Thus:\n\n    F = [1, _, _, [_], 5 | [6, [_] | [_]]],\n    %% This is the same as:\n    %%  [1, _, _, [_], 5, 6, [_], _]\n    [1, 2, 3, G, 5, 6, H, 8] = F(2, 3, 8),\n    [4] = G(4),\n    [7] = H(7).\n\nHowever, be very clear about the difference between `,` and `|`: the\ntail of a list is **only** defined following a `|`. Following a `,`,\nyou're just defining another list element.\n\n    F = [_, [_]],\n    %% This is **not** the same as [_, _] or its synonym: [_ | [_]]\n    [a, G] = F(a),\n    [b] = G(b).\n\n\n### Records\n\n    -record(vector, { x, y, z }).\n\n    test() -\u003e\n        GetZ = _#vector.z,\n        7 = GetZ(#vector { z = 7 }),\n        SetX = _#vector{x = _},\n        V = #vector{ x = 5, y = 4 } = SetX(#vector{ y = 4 }, 5).\n\n\n### Case\n\n    F = case _ of\n            N when is_integer(N) -\u003e N + N;\n            N                    -\u003e N\n        end,\n    10 = F(5),\n    ok = F(ok).\n\n\nSee\n[test_cut.erl](https://github.com/truqu/cut/blob/master/test/src/test_cut.erl)\nfor more examples, including the use of *cut*s in list comprehensions and\nbinary construction.\n\nNote that *cut*s are not allowed where the result of the *cut* can only be\nuseful by interacting with the evaluation scope. For example:\n\n    F = begin _, _, _ end.\n\nThis is not allowed, because the arguments to `F` would have to be\nevaluated before the invocation of its body, which would then have no\neffect, as they're already fully evaluated by that point.\n\n## License\n\n(The MPL)\n\nSoftware distributed under the License is distributed on an \"AS IS\"\nbasis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See\nthe License for the specific language governing rights and limitations\nunder the License.\n\nThe Original Code is Erlando.\n\nThe Initial Developer of the Original Code is VMware, Inc.\nCopyright (c) 2011-2013 VMware, Inc.  All rights reserved.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftruqu%2Fcut","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftruqu%2Fcut","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftruqu%2Fcut/lists"}