{"id":13801201,"url":"https://github.com/travelping/erlando","last_synced_at":"2025-05-13T11:30:54.954Z","repository":{"id":12193533,"uuid":"14798015","full_name":"travelping/erlando","owner":"travelping","description":"Erlando","archived":false,"fork":true,"pushed_at":"2024-02-20T08:04:49.000Z","size":208,"stargazers_count":1,"open_issues_count":0,"forks_count":5,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-11-18T16:57:17.537Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://hg.rabbitmq.com/erlando/","language":"Erlang","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"Phonebooth/erlando","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/travelping.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-11-29T10:02:25.000Z","updated_at":"2024-06-02T14:23:37.000Z","dependencies_parsed_at":"2023-01-16T21:16:00.593Z","dependency_job_id":null,"html_url":"https://github.com/travelping/erlando","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travelping%2Ferlando","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travelping%2Ferlando/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travelping%2Ferlando/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/travelping%2Ferlando/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/travelping","download_url":"https://codeload.github.com/travelping/erlando/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253932775,"owners_count":21986447,"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":[],"created_at":"2024-08-04T00:01:20.507Z","updated_at":"2025-05-13T11:30:53.617Z","avatar_url":"https://github.com/travelping.png","language":"Erlang","funding_links":[],"categories":["Algorithms and Datastructures"],"sub_categories":[],"readme":"# Erlando\n\n\n\n## Introduction\n\nErlando is a set of syntax extensions for Erlang. Currently it\nconsists of three syntax extensions, all of which take the form of\n[parse-transformers](http://www.erlang.org/doc/man/erl_id_trans.html).\n\n* **Cut**: This adds support for *cut*s to Erlang. These are\n  inspired by the\n  [Scheme form of cuts](http://srfi.schemers.org/srfi-26/srfi-26.html). *Cut*s\n  can be thought of as a light-weight form of abstraction, with\n  similarities to partial application (or currying).\n\n* **Do**: This adds support for *do*-syntax and monads to\n  Erlang. These are heavily inspired by [Haskell](http://haskell.org),\n  and the monads and libraries are near-mechanical translations from\n  the Haskell GHC libraries.\n\n* **Import As**: This adds support for importing remote functions to\n  the current module namespace with explicit control of the local\n  function names.\n\n\n\n## Use\n\nTo use any of these parse-transformers, you must add the necessary\n`-compile` attributes to your Erlang source files. For example:\n\n    -module(test).\n    -compile({parse_transform, cut}).\n    -compile({parse_transform, do}).\n    -compile({parse_transform, import_as}).\n    ...\n\nThen, when compiling `test.erl`, you must ensure `erlc` can locate\n`cut.beam` or `do.beam` or `import_as.beam` by passing the suitable\npath to `erlc` with a `-pa` or `-pz` argument. For example:\n\n    erlc -Wall +debug_info -I ./include -pa ebin -o ebin  src/cut.erl\n    erlc -Wall +debug_info -I ./include -pa ebin -o ebin  src/do.erl\n    erlc -Wall +debug_info -I ./include -pa ebin -o ebin  src/import_as.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\nthe placement of the parse-transformer attributes. For example, I've\nfound that `-compile({parse_transform, cut}).` must occur before\n`-include_lib(\"stdlib/include/qlc.hrl\").`\n\n\n\n## Cut\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#### Maps\n\n    test() -\u003e\n        GetZ = maps:get(z, _),\n        7    = GetZ(#{ z =\u003e 7 }),\n        SetX = _#{x =\u003e _},\n        V    = #{ x := 5, y := 4 } = SetX(#{ y =\u003e 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](http://hg.rabbitmq.com/erlando/file/default/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\n\n## Do\n\nThe *do* parse-transformer permits Haskell-style *do-notation* in\nErlang, which makes using monads, and monad transformers possible and\neasy. (Without *do-notation*, monads tend to look like a lot of line\nnoise.)\n\n\n### The Inevitable Monad Tutorial\n\n#### The Mechanics of a Comma\n\nWhat follows is a brief and mechanical introduction to monads. It\ndiffers from a lot of the Haskell monad tutorials, because they tend\nto view monads as a means of achieving sequencing of operations in\nHaskell, which is challenging because Haskell is a lazy\nlanguage. Erlang is not a lazy language, but the abstractions\npossible from using monads are still worthwhile.\n\nLet's say we have the three lines of code:\n\n    A = foo(),\n    B = bar(A, dog),\n    ok.\n\nThey are three, simple statements, which are evaluated\nconsecutively. What a monad gives you is control over what happens\nbetween the statements: in Erlang, it is a programmatic comma.\n\nIf you wanted to implement a programmatic comma, how would you do it?\nYou might start with something like:\n\n    A = foo(),\n    comma(),\n    B = bar(A, dog),\n    comma(),\n    ok.\n\nBut that's not quite powerful enough, because unless `comma/0` throws\nsome sort of exception, it can't actually stop the subsequent\nexpression from being evaluated. Most of the time we'd probably like\nthe `comma/0` function to be able to act on some variables which are\ncurrently in scope, and that's not possible here either. So we should\nextend the function `comma/0` so that it takes the result of the\npreceding expression, and can choose whether or not the subsequent\nexpressions should be evaluated:\n\n    comma(foo(),\n          fun (A) -\u003e comma(bar(A, dog),\n                           fun (B) -\u003e ok end)\n          end).\n\nThus the function `comma/2` takes all results from the previous\nexpression, and controls how and whether they are passed to the next\nexpression.\n\nAs defined, the `comma/2` function is the monadic function `'\u003e\u003e='/2`.\n\nNow it's pretty difficult to read the program with the `comma/2`\nfunction (especially as Erlang annoyingly doesn't allow us to define\nnew *infix* functions), which is why some special syntax is\ndesirable. Haskell has its *do-notation*, and so we've borrowed from\nthat and abused Erlang's list comprehensions. Haskell also has lovely\ntype-classes, which we've sort of faked specifically for monads. So,\nwith the *do* parse-transformer, you can write in Erlang:\n\n    do([Monad ||\n        A \u003c- foo(),\n        B \u003c- bar(A, dog),\n        ok]).\n\nwhich is readable and straightforward, and this is transformed into:\n\n    Monad:'\u003e\u003e='(foo(),\n                fun (A) -\u003e Monad:'\u003e\u003e='(bar(A, dog),\n                                       fun (B) -\u003e ok end)\n                end).\n\nThere is no intention that this latter form is any more readable than\nthe `comma/2` form - it is not. However, it should be clear that the\nfunction `Monad:'\u003e\u003e='/2` now has *complete* control over what happens:\nwhether the `fun` on the right hand side ever gets invoked (and how often);\nand if so, with what parameter values.\n\n\n#### Lots of different types of Monads\n\nSo now that we have some relatively nice syntax for using monads, what\ncan we do with them? Also, in the code\n\n    do([Monad ||\n        A \u003c- foo(),\n        B \u003c- bar(A, dog),\n        ok]).\n\nwhat are the possible values of `Monad`?\n\nThe answer to the former question is *almost anything*; and to the\nlatter question, is *any module name that implements the monad\nbehaviour*.\n\nAbove, we covered one of the three monadic operators, `'\u003e\u003e='/2`. The\nothers are:\n\n* `return/1`: This *lifts* a value into the monad. We'll see examples\n  of this shortly.\n\n* `fail/1`: This takes a term describing the error encountered, and\n  informs whichever monad currently in use that some sort of error has\n  occurred.\n\nNote that within *do-notation*, any function call to functions named\n`return` or `fail`, are automatically rewritten to invoke `return` or\n`fail` within the current monad.\n\n\u003e Some people familiar with Haskell's monads may be expecting to see a\nfourth operator, `'\u003e\u003e'/2`. Interestingly, it turns out that you can't\nimplement `'\u003e\u003e'/2` in a strict language unless all your monad types are\nbuilt on functions. This is because in a strict language,\narguments to functions are evaluated before the function is\ninvoked. For `'\u003e\u003e='/2`, the second argument is only reduced to a function\nprior to invocation of `'\u003e\u003e='/2`. But the second argument to `'\u003e\u003e'/2` is not\na function, and so in strict languages, will be fully reduced prior to\n`'\u003e\u003e'/2` being invoked. This is problematic because the `'\u003e\u003e'/2` operator\nis meant to be in control of whether or not subsequent expressions are\nevaluated. The only solution here would be to make the basic monad\ntype a function, which would then mean that the second argument to\n`'\u003e\u003e='/2` would become a function to a function to a result!\n\n\u003e However, it is required that `'\u003e\u003e'(A, B)` behaves identically to\n`'\u003e\u003e='(A, fun (_) -\u003e B end)`, and so that is what we do: whenever we come to a\n`do([Monad || A, B ])`, we rewrite it to `'\u003e\u003e='(A, fun (_) -\u003e B end)`\nrather than `'\u003e\u003e'(A, B)`. There is no `'\u003e\u003e'/2` operator in our Erlang monads.\n\nThe simplest monad possible is the Identity-monad:\n\n    -module(identity_m).\n    -behaviour(monad).\n    -export(['\u003e\u003e='/2, return/1, fail/1]).\n\n    '\u003e\u003e='(X, Fun) -\u003e Fun(X).\n    return(X)     -\u003e X.\n    fail(X)       -\u003e throw({error, X}).\n\nThis makes our programmatic comma behave just like Erlang's comma\nnormally does. The **bind** operator (that's the Haskell term for the\n`'\u003e\u003e='/2` monadic operator) does not inspect the\nvalues passed to it, and always invokes the subsequent expression function.\n\nWhat could we do if we did inspect the values passed to the sequencing\ncombinators? One possibility results in the Maybe-monad:\n\n    -module(maybe_m).\n    -behaviour(monad).\n    -export(['\u003e\u003e='/2, return/1, fail/1]).\n    \n    '\u003e\u003e='({just, X}, Fun) -\u003e Fun(X);\n    '\u003e\u003e='(nothing,  _Fun) -\u003e nothing.\n    \n    return(X) -\u003e {just, X}.\n    fail(_X)  -\u003e nothing.\n\nThus if the result of the preceding expression is `nothing`, the\nsubsequent expressions are *not* evaluated. This means that we can write\nvery neat looking code which immediately stops should any failure be\nencountered.\n\n    if_safe_div_zero(X, Y, Fun) -\u003e\n        do([maybe_m ||\n            Result \u003c- case Y == 0 of\n                          true  -\u003e fail(\"Cannot divide by zero\");\n                          false -\u003e return(X / Y)\n                      end,\n            return(Fun(Result))]).\n\nIf `Y` is equal to 0, then `Fun` will not be invoked, and the result\nof the `if_safe_div_zero` function call will be `nothing`. If `Y` is\nnot equal to 0, then the result of the `if_safe_div_zero` function\ncall will be `{just, Fun(X / Y)}`.\n\nWe see here that within the *do*-block, there is no mention of `nothing`\nor `just`: they are abstracted away by the Maybe-monad. As a result,\nit is possible to change the monad in use, without having to rewrite\nany further code.\n\nOne common place to use a monad like the Maybe-monad is where you'd\notherwise have a lot of nested case statements in order to detect\nerrors. For example:\n\n    write_file(Path, Data, Modes) -\u003e\n        Modes1 = [binary, write | (Modes -- [binary, write])],\n        case make_binary(Data) of\n            Bin when is_binary(Bin) -\u003e\n                case file:open(Path, Modes1) of\n                    {ok, Hdl} -\u003e\n                        case file:write(Hdl, Bin) of\n                            ok -\u003e\n                                case file:sync(Hdl) of\n                                    ok -\u003e\n                                        file:close(Hdl);\n                                    {error, _} = E -\u003e\n                                        file:close(Hdl),\n                                        E\n                                end;\n                            {error, _} = E -\u003e\n                                file:close(Hdl),\n                                E\n                        end;\n                    {error, _} = E -\u003e E\n                end;\n            {error, _} = E -\u003e E\n        end.\n\n    make_binary(Bin) when is_binary(Bin) -\u003e\n        Bin;\n    make_binary(List) -\u003e\n        try\n            iolist_to_binary(List)\n        catch error:Reason -\u003e\n                {error, Reason}\n        end.\n\ncan be transformed into the much shorter\n\n    write_file(Path, Data, Modes) -\u003e\n        Modes1 = [binary, write | (Modes -- [binary, write])],\n        do([error_m ||\n            Bin \u003c- make_binary(Data),\n            Hdl \u003c- file:open(Path, Modes1),\n            Result \u003c- return(do([error_m ||\n                                 file:write(Hdl, Bin),\n                                 file:sync(Hdl)])),\n            file:close(Hdl),\n            Result]).\n    \n    make_binary(Bin) when is_binary(Bin) -\u003e\n        error_m:return(Bin);\n    make_binary(List) -\u003e\n        try\n            error_m:return(iolist_to_binary(List))\n        catch error:Reason -\u003e\n                error_m:fail(Reason)\n        end.\n\nNote that we have a nested *do*-block so, as with the non-monadic\ncode, we ensure that once the file is opened, we always call\n`file:close/1` even if an error occurs in a subsequent operation. This\nis achieved by wrapping the nested *do*-block with a `return/1` call:\neven if the inner *do*-block errors, the error is *lifted* to a\nnon-error value in the outer *do*-block, and thus execution continues to\nthe subsequent `file:close/1` call.\n\nHere we are using an Error-monad which is remarkably similar to the\nMaybe-monad, but matches the typical Erlang practice of indicating\nerrors by an `{error, Reason}` tuple:\n\n    -module(error_m).\n    -behaviour(monad).\n    -export(['\u003e\u003e='/2, return/1, fail/1]).\n    \n    '\u003e\u003e='({error, _Err} = Error, _Fun) -\u003e Error;\n    '\u003e\u003e='({ok, Result},           Fun) -\u003e Fun(Result);\n    '\u003e\u003e='(ok,                     Fun) -\u003e Fun(ok).\n    \n    return(X) -\u003e {ok,    X}.\n    fail(X)   -\u003e {error, X}.\n\n\n#### Monad Transformers\n\nMonads can be *nested* by having *do*-blocks inside *do*-blocks, and\n*parameterized* by defining a monad as a transformation of another, inner,\nmonad. The State Transform is a very commonly used monad transformer,\nand is especially relevant for Erlang. Because Erlang is a\nsingle-assignment language, it's very common to end up with a lot of\ncode that incrementally numbers variables:\n\n    State1 = init(Dimensions),\n    State2 = plant_seeds(SeedCount, State1),\n    {DidFlood, State3} = pour_on_water(WaterVolume, State2),\n    State4 = apply_sunlight(Time, State3),\n    {DidFlood2, State5} = pour_on_water(WaterVolume, State4),\n    {Crop, State6} = harvest(State5),\n    ...\n\nThis is doubly annoying, not only because it looks awful, but also\nbecause you have to re-number many variables and references whenever a\nline is added or removed. Wouldn't it be nice if we could abstract out the\n`State`? We could then have a monad encapsulate the state and provide\nit to (and collect it from) the functions we wish to run.\n\n\u003e Our implementation of monad-transformers (like State) uses a \"hidden feature\"\nof the Erlang distribution called *parameterized modules*. These are\ndescribed in [Parameterized Modules in Erlang](http://ftp.sunet.se/pub/lang/erlang/workshop/2003/paper/p29-carlsson.pdf).\n\nThe State-transform can be applied to any monad. If we apply it to the\nIdentity-monad then we get what we're looking for. The key extra\nfunctionality that the State transformer provides us with is the\nability to `get` and `set` (or just plain `modify`) state from within\nthe inner monad. If we use both the *do* and *cut* parse-transformers, we\ncan write:\n\n    StateT = state_t:new(identity_m),\n    SM = StateT:modify(_),\n    SMR = StateT:modify_and_return(_),\n    StateT:exec(\n      do([StateT ||\n\n          StateT:put(init(Dimensions)),\n          SM(plant_seeds(SeedCount, _)),\n          DidFlood \u003c- SMR(pour_on_water(WaterVolume, _)),\n          SM(apply_sunlight(Time, _)),\n          DidFlood2 \u003c- SMR(pour_on_water(WaterVolume, _)),\n          Crop \u003c- SMR(harvest(_)),\n          ...\n\n          ]), undefined).\n\nWe began by creating a State-transform over the Identity-monad:\n\n    StateT = state_t:new(identity_m),\n    ...\n\n\u003e This is the syntax for *instantiating* parameterized modules. `StateT` is a\nvariable referencing a module instance which, in this case, is a monad.\n\nand we define two shorthands for running functions that either just\nmodify the state, or modify the state *and* return a result:\n\n    SM = StateT:modify(_),\n    SMR = StateT:modify_and_return(_),\n    ...\n\nThere's a bit of bookkeeping required but we achieve our goal: there are no\nstate variables now to renumber whenever we make a change. We used *cut*s\nto leave holes in the functions where State should be fed in; and we\nobeyed the protocol that if a function returns both a result and a state, it\nis in the form of a `{Result, State}` tuple. The State-transform does the rest.\n\n\n### Beyond Monads\n\nThere are some standard monad functions such as `join/2` and\n`sequence/2` available in the `monad` module. We have also implemented\n`monad_plus` which works for monads where there's an obvious sense of\n*zero*  and *plus* (currently Maybe-monad, List-monad, and Omega-monad).\nThe associated functions `guard`, `msum` and `mfilter` are available\nin the `monad_plus` module.\n\nIn many cases, a fairly mechanical translation from Haskell to Erlang\nis possible, so converting other monads or combinators should mostly\nbe straightforward. However, the lack of type classes in Erlang is\nlimiting.\n\n\n\n## Import As\n\nFor cosmetic reasons, it is sometimes desirable to import a remote\nfunction into the current module's namespace. This eliminates the need\nto continuously prefix calls to that function with its module\nname. Erlang can already do this by using the\n[`-import` attribute](http://www.erlang.org/doc/reference_manual/modules.html).\nHowever, this always uses the same function name locally as remotely\nwhich can either lead to misleading function names or even\ncollisions. Consider, for example, wishing to import `length`\nfunctions from two remote modules. Aliasing of the functions is one\nsolution to this.\n\nFor example:\n\n    -import_as({lists, [{duplicate/2, dup}]}).\n    \n    test() -\u003e\n        [a, a, a, a] = dup(4, a).\n\nAs with `-import`, the left of the tuple is the module name, but the\nright of the tuple is a list of pairs, with the left being the\nfunction to import from the module (including arity) and the right\nbeing the local name by which the function is to be known--the\n*alias*. The implementation creates a local function, so the alias is\nsafe to use in, for example, `Var = fun dup/2` expressions.\n\n\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%2Ftravelping%2Ferlando","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftravelping%2Ferlando","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftravelping%2Ferlando/lists"}