Re: [elm-discuss] Bubbling Http Errors from Nested Components

2016-07-07 Thread Alex Lew
Here's one way to achieve the all-http-errors-should-go-to-root 
functionality described by OP. I'm not sure I love it, but thought I'd 
throw it in.

A general idea of what's going on: every component, rather than just 
returning a `Cmd Msg`, produces a `Cmd (Wrapped Msg)`, where a `Wrapped 
Msg` can either be a message-for-the-root-component or a 
message-for-the-component-itself. When the parent uses `Cmd.map` on these 
wrapped messages, it lets root messages through unchanged, but tags other 
messages as being for-the-child.

Here's the code. First, create a separate module called, e.g., 
`RootMessage`, that looks like this:

module RootMessage exposing (..)

type RootMessage = 
   LoginFailed Http.Error

type Wrapped msg = Internal msg | ForRoot RootMessage

tag : (childMsg -> parentMsg) -> Wrapped childMsg -> Wrapped parentMsg
tag tagger msg =
  case msg of
 Internal m -> Internal (tagger m)
 ForRoot r -> ForRoot r

Then, in each *non-root* module, you do something like the following. Here, 
we have a Child module that includes a Grandchild component.

module Child exposing (..)

import RootMessage exposing (..)

type Msg = GrandchildMsg Grandchild.Msg 
 | APIResponse String 
 | ButtonPressed 

update : Msg -> Model -> (Model, Cmd (Wrapped Msg))
update msg model = 
  case msg of
GrandchildMsg subMsg -> 
  let 
(newModel, cmd) = Grandchild.update subMsg model.grandchild 
  in
{ model | grandchild = newModel } ! [ Cmd.map (tag GrandchildMsg) 
cmd ]

ButtonPressed -> 
  model ! [ Task.perform (ForRoot << LoginFailed) (Internal << 
APIResponse) Http.get... ]

The trick here is in `Cmd.map (tag GrandchildMsg) cmd`. The `tag` function, 
defined in the RootMessage module, takes in a normal tag, like 
GrandchildMsg, but returns a "tagger" that only applies the `GrandchildMsg` 
tag to non-root messages. It leaves the root messages unaltered.

Finally, in the root, you can handle these generated root messages:

module Root exposing (..)

import RootMessage exposing (..)
import Child exposing (..)

type Msg = Something | AnotherThing | ChildMsg Child.Msg

update : Wrapped Msg -> Model -> (Model, Cmd (Wrapped Msg))
update msg model =
   case msg of
  ForRoot LoginFailed err -> ...
  Internal Something -> ...
  Internal AnotherThing -> ...
  Internal ChildMsg subMsg -> 
let 
  (newModel, cmd) = Child.update subMsg model.child 
in 
  { model | child = newModel } ! [ Cmd.map (tag ChildMsg) cmd ]


Sorry for the terseness -- happy to elaborate if something is unclear!

-Alex


On Thursday, July 7, 2016 at 11:30:08 AM UTC-4, Erik Lott wrote:
>
> Mark:
>
> That's lead me to think about but not yet write a command alternative that 
>> could also handle tasks that didn't need to be routed back to the 
>> originator and that could be used to send messages to the top-level (or 
>> elsewhere).
>
>
> I think you've nailed it. The suggested ELM architecture doesn't easily 
> allow for application specific communication from the lower nested 
> components to the upper components. There are times when in the scope of an 
> overall architecture, you'd like to return some type of application wide 
> recognized event/message back up the update stack, and have any interested 
> ancestor component react to it... This is essentially how Commands 
> function. Commands are a globally recognized type, that are returned up the 
> stack, and although they may carry a local message type with them, the 
> command itself is never returned or reused. The Command reaches the 
> application root, and is processed by the core language.
>
> Like you said: I think what we need (or at least what I need) is our own 
> Application specific type to return up the stack instead of Cmd. An "Event" 
> union type possibly? And CmdEvt tag could wrap the native elm Cmd, and be 
> unwrapped at the root.
>
> type Event 
>   = CmdEvt Cmd
>   | MsgEvt
>   | Unauthorized
>   | LoggedIn User
>   | ConfirmPopup CancelMsg OkMsg
>
>
>
> On Tuesday, July 5, 2016 at 1:30:51 PM UTC-4, Mark Hamburg wrote:
>>
>> The first option feels repugnant from an encapsulation standpoint. I've 
>> built the second option and it works but it increases the amount of 
>> boilerplate in hierarchical systems because we now have three results to 
>> deal with in update functions. That's lead me to think about but not yet 
>> write a command alternative that could also handle tasks that didn't need 
>> to be routed back to the originator and that could be used to send messages 
>> to the top-level (or elsewhere). That said, once one gets into replacing 
>> Cmd, the API request model makes a lot of sense.
>>
>> Mark
>>
>> On Jul 5, 2016, at 5:46 AM, Erik Lott  wrote:
>>
>> My app has several layers of nested components. Various components 
>> throughout the tree will need to interact with our API via http requests. 
>> If any API request returns a 401 - Not 

Re: [elm-discuss] Bubbling Http Errors from Nested Components

2016-07-07 Thread Erik Lott
Mark:

That's lead me to think about but not yet write a command alternative that 
> could also handle tasks that didn't need to be routed back to the 
> originator and that could be used to send messages to the top-level (or 
> elsewhere).


I think you've nailed it. The suggested ELM architecture doesn't easily 
allow for application specific communication from the lower nested 
components to the upper components. There are times when in the scope of an 
overall architecture, you'd like to return some type of application wide 
recognized event/message back up the update stack, and have any interested 
ancestor component react to it... This is essentially how Commands 
function. Commands are a globally recognized type, that are returned up the 
stack, and although they may carry a local message type with them, the 
command itself is never returned or reused. The Command reaches the 
application root, and is processed by the core language.

Like you said: I think what we need (or at least what I need) is our own 
Application specific type to return up the stack instead of Cmd. An "Event" 
union type possibly? And CmdEvt tag could wrap the native elm Cmd, and be 
unwrapped at the root.

type Event 
  = CmdEvt Cmd
  | MsgEvt
  | Unauthorized
  | LoggedIn User
  | ConfirmPopup CancelMsg OkMsg



On Tuesday, July 5, 2016 at 1:30:51 PM UTC-4, Mark Hamburg wrote:
>
> The first option feels repugnant from an encapsulation standpoint. I've 
> built the second option and it works but it increases the amount of 
> boilerplate in hierarchical systems because we now have three results to 
> deal with in update functions. That's lead me to think about but not yet 
> write a command alternative that could also handle tasks that didn't need 
> to be routed back to the originator and that could be used to send messages 
> to the top-level (or elsewhere). That said, once one gets into replacing 
> Cmd, the API request model makes a lot of sense.
>
> Mark
>
> On Jul 5, 2016, at 5:46 AM, Erik Lott  
> wrote:
>
> My app has several layers of nested components. Various components 
> throughout the tree will need to interact with our API via http requests. 
> If any API request returns a 401 - Not Authorized error, or a Timeout 
> Error, the error needs to bubble up to the root component where is can be 
> handled appropriately.
>
> What is the most idiomatic way of dealing with this? 
>
> *1. Parent should pattern match against important child messages*: 
> Reference 
> 
> This could work, but would be unreasonable in this case. The root 
> component would need to match against every failing api http request made 
> by every child, grandchild, great-grandchild, etc, component in the tree. 
> If a single pattern is missed, the app would be in an error state, so this 
> is prone to mistakes.
>
> *2. Nested Components return additional info from the "update" function*: 
> Reference 
> 
> Each component returns an additional value from its update function like 
> this:
> update : Msg -> Model -> (Model, Cmd Msg, SomeInfo)
>
> The parent component could inspect the returned "SomeInfo" value from its 
> direct children, and act on that information if necessary.  In my case, any 
> nested component that makes http requests to our API would be responsible 
> for returning a APINotAuthorized and APITimeout value to its parent, and 
> its parent would do the same, until the error has bubbled up to the root 
> component.
>
>
> Option 2 is simple and robust, and can be used to pass messages of any 
> type, for any situation... but I'm wondering if I'm missing an obvious 3rd 
> solution?
>
> -- 
> 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 elm-discuss...@googlegroups.com .
> 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 elm-discuss+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [elm-discuss] Bubbling Http Errors from Nested Components

2016-07-05 Thread Erik Lott
Mark, that reminds me of a similar solution I saw here:
https://gist.github.com/pdamoc/a47090e69b75433efa60fe4f70e6a06a

Rather than returning a Cmd from the update function, a custom Req type is 
being returned instead. The Req naturally returns to the root component, 
gets mapped to the local Msg type, and then transformed into a Cmd.

I'm not exactly sure what that buys us, but it is... interesting.




On Tuesday, July 5, 2016 at 1:30:51 PM UTC-4, Mark Hamburg wrote:
>
> The first option feels repugnant from an encapsulation standpoint. I've 
> built the second option and it works but it increases the amount of 
> boilerplate in hierarchical systems because we now have three results to 
> deal with in update functions. That's lead me to think about but not yet 
> write a command alternative that could also handle tasks that didn't need 
> to be routed back to the originator and that could be used to send messages 
> to the top-level (or elsewhere). That said, once one gets into replacing 
> Cmd, the API request model makes a lot of sense.
>
> Mark
>
> On Jul 5, 2016, at 5:46 AM, Erik Lott  
> wrote:
>
> My app has several layers of nested components. Various components 
> throughout the tree will need to interact with our API via http requests. 
> If any API request returns a 401 - Not Authorized error, or a Timeout 
> Error, the error needs to bubble up to the root component where is can be 
> handled appropriately.
>
> What is the most idiomatic way of dealing with this? 
>
> *1. Parent should pattern match against important child messages*: 
> Reference 
> 
> This could work, but would be unreasonable in this case. The root 
> component would need to match against every failing api http request made 
> by every child, grandchild, great-grandchild, etc, component in the tree. 
> If a single pattern is missed, the app would be in an error state, so this 
> is prone to mistakes.
>
> *2. Nested Components return additional info from the "update" function*: 
> Reference 
> 
> Each component returns an additional value from its update function like 
> this:
> update : Msg -> Model -> (Model, Cmd Msg, SomeInfo)
>
> The parent component could inspect the returned "SomeInfo" value from its 
> direct children, and act on that information if necessary.  In my case, any 
> nested component that makes http requests to our API would be responsible 
> for returning a APINotAuthorized and APITimeout value to its parent, and 
> its parent would do the same, until the error has bubbled up to the root 
> component.
>
>
> Option 2 is simple and robust, and can be used to pass messages of any 
> type, for any situation... but I'm wondering if I'm missing an obvious 3rd 
> solution?
>
> -- 
> 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 elm-discuss...@googlegroups.com .
> 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 elm-discuss+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [elm-discuss] Bubbling Http Errors from Nested Components

2016-07-05 Thread Simon
I suggested option 1 and use it all the time. In practise I enable commands 
in the root and first couple of children (basically components that equate 
to a table in my database), and then turn to pure update functions 
thereafter, and catch "Submit" events at the lowest parent that does 
command handling. It works for me, and then I use the update function to 
pass back returned data into my children's models. I've wondered about the 
triple return `update` function, but I think it would make refactoring 
quite a bit more painful.

Simon

On Tuesday, 5 July 2016 19:30:51 UTC+2, Mark Hamburg wrote:
>
> The first option feels repugnant from an encapsulation standpoint. I've 
> built the second option and it works but it increases the amount of 
> boilerplate in hierarchical systems because we now have three results to 
> deal with in update functions. That's lead me to think about but not yet 
> write a command alternative that could also handle tasks that didn't need 
> to be routed back to the originator and that could be used to send messages 
> to the top-level (or elsewhere). That said, once one gets into replacing 
> Cmd, the API request model makes a lot of sense.
>
> Mark
>
> On Jul 5, 2016, at 5:46 AM, Erik Lott  
> wrote:
>
> My app has several layers of nested components. Various components 
> throughout the tree will need to interact with our API via http requests. 
> If any API request returns a 401 - Not Authorized error, or a Timeout 
> Error, the error needs to bubble up to the root component where is can be 
> handled appropriately.
>
> What is the most idiomatic way of dealing with this? 
>
> *1. Parent should pattern match against important child messages*: 
> Reference 
> 
> This could work, but would be unreasonable in this case. The root 
> component would need to match against every failing api http request made 
> by every child, grandchild, great-grandchild, etc, component in the tree. 
> If a single pattern is missed, the app would be in an error state, so this 
> is prone to mistakes.
>
> *2. Nested Components return additional info from the "update" function*: 
> Reference 
> 
> Each component returns an additional value from its update function like 
> this:
> update : Msg -> Model -> (Model, Cmd Msg, SomeInfo)
>
> The parent component could inspect the returned "SomeInfo" value from its 
> direct children, and act on that information if necessary.  In my case, any 
> nested component that makes http requests to our API would be responsible 
> for returning a APINotAuthorized and APITimeout value to its parent, and 
> its parent would do the same, until the error has bubbled up to the root 
> component.
>
>
> Option 2 is simple and robust, and can be used to pass messages of any 
> type, for any situation... but I'm wondering if I'm missing an obvious 3rd 
> solution?
>
> -- 
> 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 elm-discuss...@googlegroups.com .
> 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 elm-discuss+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[elm-discuss] Bubbling Http Errors from Nested Components

2016-07-05 Thread Erik Lott
My app has several layers of nested components. Various components 
throughout the tree will need to interact with our API via http requests. 
If any API request returns a 401 - Not Authorized error, or a Timeout 
Error, the error needs to bubble up to the root component where is can be 
handled appropriately.

What is the most idiomatic way of dealing with this? 

*1. Parent should pattern match against important child messages*: Reference 

This could work, but would be unreasonable in this case. The root component 
would need to match against every failing api http request made by every 
child, grandchild, great-grandchild, etc, component in the tree. If a 
single pattern is missed, the app would be in an error state, so this is 
prone to mistakes.

*2. Nested Components return additional info from the "update" function*: 
Reference 

Each component returns an additional value from its update function like 
this:
update : Msg -> Model -> (Model, Cmd Msg, SomeInfo)

The parent component could inspect the returned "SomeInfo" value from its 
direct children, and act on that information if necessary.  In my case, any 
nested component that makes http requests to our API would be responsible 
for returning a APINotAuthorized and APITimeout value to its parent, and 
its parent would do the same, until the error has bubbled up to the root 
component.


Option 2 is simple and robust, and can be used to pass messages of any 
type, for any situation... but I'm wondering if I'm missing an obvious 3rd 
solution?

-- 
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 elm-discuss+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.