> > Having read the reddit thread about just starting big and breaking things > down, it reminds of arguments I've heard for years about how monolithic > programs are easier to work with.
This Reddit post <https://www.reddit.com/r/elm/comments/5jd2xn/how_to_structure_elm_with_multiple_models/dbuu0m4/> is about *how to break up a monolith*. It doesn't talk about anything else except how to break up a monolith. :) Abstraction barriers are what make big programs possible. I don't think > there is any magic in Elm that changes this fundamental lesson of 60 years > of software development. I couldn't agree more! Glad we're on the same page about that. > The rest of the world has looked at TEA and seen value in it and is > looking to extend it. If people look at it, see value in it, and then think "okay this works great, but how can we take it in a different direction?" - that sounds like putting the cart before the horse to me. I think a lot of people would be happier if they gave the "use the simplest API that works" approach a shot. :) > The remark that composing TEA-shaped units isn't what Elm is designed for > raises the question of whether we should then be expecting Cmd.map and > friends to be going away since that would seem to be exactly what they are > designed for. > That's a great question! They are designed to make it easier to work with Html and Cmd alongside several message constructors. Example Pretend there is no Html.map and I'm writing a reusable view whose entire API consists of one function: checkbox : (Bool -> msg) -> Bool -> Html msg The only state I care about is whether the checkbox is checked (the Bool argument), and the only user interaction is checking or unchecking the box (the (Bool -> msg) argument; the caller is free to use whatever Msg type they are already working with, no conversion necessary). Using a higher-order function to manage messages is a great lightweight default choice, so I don't miss Html.map at all here. I wouldn't use it regardless! Now let's say what I'm building is not a reusable checkbox, but something more complex: a reusable *signup form* that expands in-place when the user clicks it. This will involve a lot more Msg constructors - for recording text entry in the username/password fields, HTTP responses for username availability checks, expanding and collapsing it, submitting it...overall, a lot more going on. Let's say it needs 10 message constructors to work properly. Using the same API design as we did for checkbox above, we'd write something like this: signupForm : (SignupForm.State -> GiganticRecordOfTenMsgConstructors msg) -> SignupForm.State -> Html msg With this API, reusing this signup form entails spelling out all 10 Msg constructors as the first argument to this function. That's a lot more work than the single Bool argument needed by checkbox! This is where a more heavyweight approach can pay off: creating a new SignupForm.Msg and having signupForm return Html SignupForm.Msg instead of Html msg. Here's how that API would look: signupForm : SignupForm.State -> Html SignupForm.Msg type SignupForm.Msg We had to do more work to set this up, of course (the checkbox function didn't need the Msg implementation this signupForm function requires), but we successfully eliminated the need for the gigantic record argument. Great! In exchange, though, not only have we needed a new Msg type on the SignupForm side, but now we also need our caller to convert from SignupForm.Msg to whatever their local flavor of Msg is. (Hence why this is a more heavyweight approach.) Since our application uses FooApp.Msg, but signupForm returns Html parameterized on a SignupForm.Msg, we'll have a type mismatch if our application's view tries to call signupForm directly. In the absence of Html.map, we're stuck - this thing is no longer reusable, it's become a nonfunctional art piece that we can't actually plug in anywhere. If we have Html.map, problem solved! We can add this approach to our toolbox, right alongside the simpler "one higher-order function and you're done" API we used for checkbox. Overengineering Now granted, Html.map does introduce the potential for overengineering. We wrote this earlier: checkbox : (Bool -> msg) -> Bool -> Html msg This was an unremarkably simple API, but Html.map gives us the ability to replace this single function with a full-blown Checkbox module, complete with: - type alias Model = { checked : Bool } - type Msg = SetChecked Bool - update : Msg -> Model -> ( Model, Cmd Msg) *[gotta return Cmd Msg in addition to Model, because what if someday our checkbox wants to send HTTP requests?!]* - view : Model -> Html Msg - a chauffeured Rolls-Royce to deliver the single Boolean worth of state and interaction from one function to another. To implement this massively overengineered API (remember, we achieved the same functionality using *one small function* before) in a world without Html.map and Cmd.map, we'd have to pass those hulking 10-field records between even more functions. In such a world, this API would be so outrageously cumbersome to use that I doubt anyone would attempt it. The fact that Html.map makes this appear deceptively okay as an API choice is an unfortunate downside of Html.map. And sure, given that Html.map and Cmd.map exist, it's a reasonable question to ponder - "if I was correct to give my Reusable Signup Form this API, shouldn't One Size Fit All, meaning it would also be correct to choose that exact same API for every other use case I can think of?" - but there is an answer to this question, and it is "no; my code will be simpler if I fit the API to the use case." Despite the fact that Html.map introduces this potential overengineering pitfall, I think it's clearly useful enough for the Reusable Signup Forms of the world to be worth including. I wouldn't (and don't) worry about it going anywhere! :) -- You received this message because you are subscribed to the Google Groups "Elm Discuss" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
