{"id":18423550,"url":"https://github.com/chadtech/return","last_synced_at":"2025-04-13T14:45:46.186Z","repository":{"id":57674558,"uuid":"127686194","full_name":"Chadtech/return","owner":"Chadtech","description":"(DEPRECIATED) Helpers for update function return values","archived":false,"fork":false,"pushed_at":"2019-03-02T13:27:53.000Z","size":18,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-16T07:42:06.581Z","etag":null,"topics":["elm","tuples"],"latest_commit_sha":null,"homepage":"http://package.elm-lang.org/packages/Chadtech/return/latest/","language":"Elm","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/Chadtech.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}},"created_at":"2018-04-02T01:21:38.000Z","updated_at":"2019-08-15T15:54:10.000Z","dependencies_parsed_at":"2022-08-29T17:40:28.729Z","dependency_job_id":null,"html_url":"https://github.com/Chadtech/return","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chadtech%2Freturn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chadtech%2Freturn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chadtech%2Freturn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chadtech%2Freturn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Chadtech","download_url":"https://codeload.github.com/Chadtech/return/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248732334,"owners_count":21152842,"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":["elm","tuples"],"created_at":"2024-11-06T04:37:45.309Z","updated_at":"2025-04-13T14:45:46.147Z","avatar_url":"https://github.com/Chadtech.png","language":"Elm","readme":"## Depreciated, sort of\n\nThis code still works, but it represents a technique of scaling Elm applications that I think is dated at this point (March 2nd, 2019). Nested state used to be more common in Elm, but now Elm people seem to have learned that nesting state is really undesirable. This package represents how I would handle nested state, but the purpose of this package is strongly undermined by the fact that now I just wouldnt nest state to begin with. So, if you wish, I hope this package can serve you well! Maybe [a similar package from a true believer in this approach would be better](https://package.elm-lang.org/packages/z5h/component-result/latest/). But, ultimately, I think you probably shouldnt need to do any of this stuff.\n\nHeres some code that shows what I mean..\n```elm\n-- The Before Times\n-- parent model has gloally things, and passes them down into child-y things\n\n--      Parent  \u003c---------- handles updates ----------\n--        |                                          |\n--  passes global stuff down                  passes it back up\n--        |                                          |\n--        v                                          |\n--      Child  ---------- updates globally stuff ----- \n\ntype alias Model =\n    { page : Page \n    , user : User\n    }\n\ntype Page \n    = Home Home.Model\n    | Login Login.Model\n\n\ntype ExternalMsg\n    = SetUser User\n\n-- Login.elm\nupdate : User -\u003e Login.Msg -\u003e Login.Model -\u003e (Login.Model, Cmd Login.Msg, ExternalMsg)\n\n-- Today\n-- Totally flat model, model states pass off globaly stuff to each other\n-- only rule is that all states possess same globaly stuff, and have\n-- methods for accepting and giving global state\ntype Model\n    = Home Home.Model\n    | Login Login.Model\n\ngetUser : Model -\u003e User\ngetUser model =\n    case model of\n        Login loginModel -\u003e\n            loginModel.user\n\n        Home homeModel -\u003e\n            homeModel.user\n\ngoToLoginPage : Model -\u003e Model\ngoToLoginPage model =\n    Login.init \u003c| getUser model\n\n-- Login.elm\ninit : User -\u003e Login.Model\nupdate : Login.Msg -\u003e Login.Model -\u003e (Login.Model, Cmd Login.Msg)\n```\n\nEverything below is the original readme preserved for posterity.\n\n\n# Return\n\nThis package is for handling the subtle architectural problems associated with nested update functions in big Elm applications. There are no simple principles at work here; this package is just a product of my cumulated experience from writing a lot Elm and paying attention to what other people do when they write Elm. Immediately below is a code example, and further below is an explanation of how I got here.\n\n## Example\n\n```elm\n-- Main.elm --\nimport Login\nimport Return2 as R2\nimport Return3 as R3\n\n\ntype alias Model =\n    { page : Page \n    , user : Maybe User\n    }\n\n\ntype Page\n    = Login Login.Model\n    | Home\n\n\ntype Msg\n    = LoginMsg Login.Msg\n\n\nupdate : Msg -\u003e Model -\u003e ( Model, Cmd Msg )\nupdate msg model =\n    case msg of\n        LoginMsg subMsg -\u003e\n            case model.page of\n                Login subModel -\u003e\n                    subModel\n                        |\u003e Login.update subMsg \n                        |\u003e R3.mapCmd LoginMsg\n                        |\u003e R3.incorp handleLoginReply model\n\n                _ -\u003e\n                    model\n                        |\u003e R2.withNoCmd\n\n\nhandleLoginReply : Login.Model -\u003e Maybe Login.Reply -\u003e Model -\u003e (Model, Cmd Msg)\nhandleLoginReply subModel maybeReply model =\n    case maybeReply of\n        Nothing -\u003e\n            model\n                |\u003e setPage Login subModel\n                |\u003e R2.withNoCmd\n\n        Just (Login.UserLoggedIn user) -\u003e\n            { model\n                | page = Home\n                , user = Just user\n            }\n                |\u003e R2.withNoCmd\n\n\nsetPage : (subModel -\u003e Page) -\u003e subModel -\u003e Model -\u003e Model\nsetPage pageCtor subModel model =\n    { model | page = pageCtor subModel }\n\n\n-- Login.Elm --\n\nimport Return3 as R3 exposing (Return)\nimport Return2 as R2\n\n\ntype alias Model =\n    { field : String }\n\n\ntype Msg\n    = FieldUpdated String\n    | SubmitClicked\n    | LoginSucceeded User\n\n\ntype Reply\n    = UserLoggedIn User\n\n\nupdate : Msg -\u003e Model -\u003e Return Model Msg Reply\nupdate msg model =\n    case msg of\n        FieldUpdated str -\u003e\n            { model | field = str }\n                |\u003e R3.withNothing\n\n        SubmitClicked -\u003e\n            model\n                |\u003e R2.withCmd (submitRequest model)\n                |\u003e R3.withNoReply\n\n        LoginSucceeded user -\u003e\n            model\n                |\u003e R2.withNoCmd\n                |\u003e R3.withReply (UserLoggedIn user)\n```\n\n## Explanation\n\nIn Elm, our update functions return a tuple containing our `Model` and whatever side effects we want to occur outside the Elm run-time.\n```elm\n( Model, Cmd Msg )\n```\nIn practice, that means we have to type stuff like this.\n```elm\n    ( editThing model, thingCmd (getData model))\n```\nUnfortunately this turns out to be not so nice. We have to type this tuple a lot, and doing so is a hassle because there are characters on the left, right, and center of the tuple you have to type. It would be much easier if you only had to put your cursor in one spot rather than jump around. As an alternative some of us began using our own infix operators; most notably the folks at NoRedInk use the [\"rocket\" operator](http://package.elm-lang.org/packages/NoRedInk/rocket-update/latest).\n```elm\n    model =\u003e thingCmd\n\n(=\u003e) : a -\u003e b -\u003e (a, b)\n(=\u003e) model cmd =\n    (model, cmd)\n```\nThats pretty good. But as I write this, the expectation is that the upcoming Elm 0.19 wont allow custom infix operators (which is probably for the best). We will need a new solution. I really like [Janiczek/cmd-extra](http://package.elm-lang.org/packages/Janiczek/cmd-extra/latest). His package exposes some extremely readable functions, like `withCmd`.\n```elm\n    model \n        |\u003e withCmd cmd\n\nwithCmd : Cmd msg -\u003e model -\u003e (model, Cmd msg)\n```\nIts a little bit more verbose, but it reads really nice and it plays well with whatever else is going on in your update function. If this were the end of the story, I would just settle on Janiczek's package, but its not. Big Elm applications usually have sub-update functions, sub-models, and sub-msgs. The communication between parent and child update functions can be kind of complicated, and often we need to transform values after they have been bundled into tuples. Our update function technique has to work well in the broader context of what update functions are doing in our application. \n\nRichard Feldman uses the `ExternalMsg` type for communication from child update functions to parent update functions. His update functions result in something that looks like this.\n```elm\n    ((Model, Cmd Msg), ExternalMsg)\n```\nThe `ExternalMsg` carries information from the sub module to its parent module. The best example of this is having a login page with its own update function, that needs to tell the main update function who the user is once they successfully log in. The sub update function in your login page doesnt just return a sub-model and sub-cmds, it also returns information that the application as a whole needs to know, like that the user session has changed.\n\nThat works very well. One criticism I have however, is nested tuples are kind of hard to work with. The model is in a tuple in a tuple. So if you want to access it- say to transform it- you need to do `Tuple.first \u003e\u003e Tuple.first`. Flat data structures are nicer, so I have learned to do triples instead.\n```elm\n    (Model, Cmd Msg, ExternalMsg)\n```\nAlso, I found that the name `ExternalMsg` didnt make much sense. Regular `Msg`s reflect external events that actually happened in your application that have indeterminate consequence. Stuff like mouse clicks, or http responses; your application just knows what happened and thereafter needs to figure out what to do. `ExternalMsg`s arent the same kind of thing despite what the name implies. They represent interal results from within your application which usually have explicit consequence. I instead started calling them `Reply` since they are like replies to the news sub-modulereceive from the parent-modules. Also I made it into a `Maybe Reply`, so you can consider the possibility of no reply abstractly, without assuming any particular `Reply` type.\n```elm\n    (Model, Cmd Msg, Maybe Reply)\n```\nImprovements from this point are harder and more tenuous, but I have also learned a bit from [Fresheyeball/elm-return](http://package.elm-lang.org/packages/Fresheyeball/elm-return/6.0.3/) as well. Sub-models need to be incorporated back into their parent-models, and usually in very regular and predictable ways, such as just being a field inside a record. Fresheyeball's package exposes functions that simplify that incorporation process. Unfortunately, I think Fresheyeball's package indulges a lot of functional programming stuff beyond its usefulness (and it uses infix operators, so its usefulness wont last into 0.19). But regardless, his approach to formalizing and mutating return results is a good one that I have tried to reproduce in this package.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchadtech%2Freturn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchadtech%2Freturn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchadtech%2Freturn/lists"}