If you buy my premise, then one could say that the old examples using
counters were useful because they allowed one to focus on the plumbing
without distraction of use case specific details but problematic because
they also suggested that something the size of the counter model was a good
target to break things down to.

Mark

On Tue, Apr 18, 2017 at 4:44 PM Mark Hamburg <[email protected]> wrote:

> It's certainly reasonable to say that there is a point where pursuing
> fractal TEA is overkill and not buying one much but extra plumbing work.
> But it is also exactly the case of things like the sign up form where being
> able to say "Here is a sign up form. It has a model, messages, update, and
> view. You embed it in the rest of your app like this." The goal in a
> composable architecture is to have "like this" fit enough of a pattern that
> a programmer coming into an embedding situation knows what to expect. TEA
> works pretty well for this though it can feel a bit boilerplate heavy. I
> would put it as:
>
> It's often easiest to work in a monolithic program and there are things
> you can do to mitigate the effects of doing so and thereby build a bigger
> monolith. But when a project gets truly large, the ease of building a
> monolithic ball of mud gives way to the problems of figuring out what
> interacts with what and while the type system will have your back at some
> level, there are limits to what it can achieve. But TEA is fractal. You can
> build more isolated components using the same architecture and embed them
> within your larger program. Do you have multiple pages? It may be natural
> to build each of them using the TEA pattern. You can then fit them together
> in the parent app as follows... The caveat here is that while this is both
> straightforward and robust, it is a noticeable amount of code and
> translation at the glue layer, so taking the TEA hammer and smashing pieces
> down until you have tiny nuggets is going to leave you with a lot of glue
> holding those nuggets together.
>
> Mark
>
> P.S. The checkbox could also have the signature: `checkbox : Bool -> Html
> Bool` and one could use `Html.map` to apply the translation into some other
> message space. I don't know that that's better.
>
>
> On Tue, Apr 18, 2017 at 3:36 PM Richard Feldman <
> [email protected]> wrote:
>
>> 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.
>>
>>
>>

-- 
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.

Reply via email to