New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot make Address for dispatching events to multiple Addresses? #277

Closed
jinjor opened this Issue Jun 13, 2015 · 4 comments

Comments

Projects
None yet
4 participants
@jinjor
Contributor

jinjor commented Jun 13, 2015

Hi, I have a question.

I am making a new component that triggers multiple events.

type Event = EventA | EventB
component : Address Event -> Html

The parent of this component has two Addresses addressA and addressB.
When the parent receives EventA, then it should be sent to addressA.
In the same way, EventB should be sent to addressB.

But I cannot find a way to create that kind of Address.
Signal.forwardTo is useful but only for single address.

I also tried to define another version of forwardTo, but got a compile error.

type Either a b
    = Left a
    | Right b

forwardTo' : Address b -> Address c -> (a -> Either b c) -> Address a
forwardTo' (Address send) (Address send2) f =
  let g e = case f e of
    Left a -> send a
    Right b -> send2 b
  in Address g
Could not find pattern 'Address'.

I saw the Address as a data constructor cannot be used outside.
(Is this an expected beavior?)

So, is there any good way to do such a thing?

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Jun 13, 2015

Contributor

I wonder what is your use case that requires two addresses, but from using this API a bit, I've realized that the fewer address/mailboxes I have in a system, the better. The following does not answer your concern but perhaps it can help with solving the underlying problem behind your question.

When you have a component that emits multiple events (of potentially different types), you can always send all these events to a single address, provided the values you want to send are wrapped in some sort of union type like:

type ButtonAction
  = Click (Int, Int)
  | Hover Float
  | NoOp

And then, you can have a single address of type

Address ButtonAction

You can even take this logic one step further. Suppose you have a container that can have buttons, checkboxes, input boxes. You can then make a union type for each of these actions (themselves union types).

type ContainerAction 
  = Button ButtonAction 
  | Checkbox CheckboxAction
  | Inputbox InputboxAction
  | NoOp

You use forwardTo to go from the button to the container.

containerAddress : Address ContainerAction 

buttonAddress : Address ButtonAction
buttonAddress = 
  Signal.forwardTo Button containerAddress

checkboxAddress : Address CheckboxAction
checkboxAddress = 
  Signal.forwardTo Checkbox containerAddress

inputboxAddress : Address InputboxAction 
inputboxAddress = 
  Signal.forwardTo Inputbox containerAddress

And then do the same for further container types, etc... Basically, you end up with a big tree of Union Types and use pattern matching to navigate through these actions in your update logic.

In the end, you end up with one single giant mailbox for all of your actions (and perhaps another one for all of your tasks).

Contributor

TheSeamau5 commented Jun 13, 2015

I wonder what is your use case that requires two addresses, but from using this API a bit, I've realized that the fewer address/mailboxes I have in a system, the better. The following does not answer your concern but perhaps it can help with solving the underlying problem behind your question.

When you have a component that emits multiple events (of potentially different types), you can always send all these events to a single address, provided the values you want to send are wrapped in some sort of union type like:

type ButtonAction
  = Click (Int, Int)
  | Hover Float
  | NoOp

And then, you can have a single address of type

Address ButtonAction

You can even take this logic one step further. Suppose you have a container that can have buttons, checkboxes, input boxes. You can then make a union type for each of these actions (themselves union types).

type ContainerAction 
  = Button ButtonAction 
  | Checkbox CheckboxAction
  | Inputbox InputboxAction
  | NoOp

You use forwardTo to go from the button to the container.

containerAddress : Address ContainerAction 

buttonAddress : Address ButtonAction
buttonAddress = 
  Signal.forwardTo Button containerAddress

checkboxAddress : Address CheckboxAction
checkboxAddress = 
  Signal.forwardTo Checkbox containerAddress

inputboxAddress : Address InputboxAction 
inputboxAddress = 
  Signal.forwardTo Inputbox containerAddress

And then do the same for further container types, etc... Basically, you end up with a big tree of Union Types and use pattern matching to navigate through these actions in your update logic.

In the end, you end up with one single giant mailbox for all of your actions (and perhaps another one for all of your tasks).

@jinjor

This comment has been minimized.

Show comment
Hide comment
@jinjor

jinjor Jun 14, 2015

Contributor

Great answer! thanks.

I'm using WebSocket in Elm.
Until the useful modules are ready, I temporary use many ports.
I often write some boilerplate like below...

sendChatMB : Signal.Mailbox String
sendChatMB = Signal.mailbox ""
port sendChat : Signal String
port sendChat = sendChatMB.signal

chatInput = input [onEnter sendChatMB.address] []

That is the reason I need many addresses, but I never think this is a good practice.

Now I see your strategy is much clearer.
Maybe the signals for porting can be made by filtering the integrated Action stream.

Ok, I'll try refactoring it :)

Contributor

jinjor commented Jun 14, 2015

Great answer! thanks.

I'm using WebSocket in Elm.
Until the useful modules are ready, I temporary use many ports.
I often write some boilerplate like below...

sendChatMB : Signal.Mailbox String
sendChatMB = Signal.mailbox ""
port sendChat : Signal String
port sendChat = sendChatMB.signal

chatInput = input [onEnter sendChatMB.address] []

That is the reason I need many addresses, but I never think this is a good practice.

Now I see your strategy is much clearer.
Maybe the signals for porting can be made by filtering the integrated Action stream.

Ok, I'll try refactoring it :)

@jinjor jinjor closed this Jun 14, 2015

@evancz

This comment has been minimized.

Show comment
Hide comment
@evancz

evancz Jun 15, 2015

Member

Thanks for writing up a great answer @TheSeamau5! Much appreciated :)

Member

evancz commented Jun 15, 2015

Thanks for writing up a great answer @TheSeamau5! Much appreciated :)

@amitaibu

This comment has been minimized.

Show comment
Hide comment
@amitaibu

amitaibu Sep 1, 2015

@TheSeamau5 your answers are always the best, thanks!

amitaibu commented Sep 1, 2015

@TheSeamau5 your answers are always the best, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment