It would need the HandleKeyboardDown/Up messages, however I've recently
made a change to my Msg structure so I have something like this:
```elm
type alias MsgFiltered =
{ HandleKeyboardUp Char
, HandleKeyboardDown Char
}
type alias Msg
= FilterOnly MsgFiltered
| Other
| Interesting
| To
| Update
| Messages
| Like
| JumpDown
| JumpUp
```
And it seems to be working well. I just ignore the `FilterOnly` message
entirely in my Update and can handle `FilterOnly` messages in the filter
with ease (while still filtering others if I want). It makes it easy to
add messages that are only converted to others in the filter, then just
subscribe to `(\c -> FilterOnly (TestFiltered c))` as normal. That way you
do not need to even have a case for them in your main update, just for the
FilterOnly one, which you can just `( model, Cmd.none )` once.
On Wednesday, August 10, 2016 at 11:22:47 AM UTC-6, Janis Voigtländer wrote:
>
> What will the GameMsg type look like?
>
>
> 2016-08-10 18:59 GMT+02:00 OvermindDL1 <[email protected] <javascript:>>:
>
>> > If there are alternatives *with currently available functions* to my
>> proposal, very interested to hear it in this thread.
>>
>> That is why I looked at more options and started thinking about how it
>> could also solve other filtering issues that I have, so a singular thing
>> that could solve it all would be nice. :-)
>>
>>
>> Given code from the dontfall game, the current top-post suggestion was to
>> change subscriptions to be:
>> ```elm
>>
>> subscriptions : GameData -> Sub GameMsgsubscriptions d =
>> Sub.batch
>> ([ Keyboard.downsSelectively (\c -> if Char.fromCode c == 'P' then
>> Just PauseToogle else Nothing) ] ++
>> if d.state == Running then
>> [ AnimationFrame.diffs Tick
>> , Keyboard.downsSelectively (\c -> if Char.fromCode c == ' '
>> then Just JumpDown else Nothing)
>> , Keyboard.upsSelectively (\c -> if Char.fromCode c == ' '
>> then Just JumpUp else Nothing)
>> ]
>> else
>> [])
>>
>> ```
>>
>> In the thing I proposed that can handle generic message filtering (not
>> just subscription) would turn the above back into a fairly normal and
>> readable style of:
>> ```elm
>>
>> subscriptions : GameData -> Sub GameMsgsubscriptions d =
>> Sub.batch
>> [ Keyboard.downs HandleKeyboardDown
>> , Keyboard.ups HandleKeyboardUps
>> , if d.state == Running then AnimationFrame.diffs Tick else Sub.none
>> ]
>>
>> ```
>> And a new callback (added in the main where init/update/etc are, in my
>> package is called 'filters') would be something like this, this is a very
>> simple example and `filters` can do a lot more, but for simple keyboard
>> filtering:
>> ```elm
>>
>> filters : Msg -> Model -> ( Msg, States Model Msg )filters msg model =
>> if model.state == Running then
>> case msg of
>> HandleKeyboardDown c ->
>> case Char.fromCode c ->
>> 'P' -> ( PauseToggle, States.enableAll )
>> ' ' -> ( JumpDown, States.enableAll )
>> _ -> ( msg, States.disableAll )
>> HandleKeyboardUp c ->
>> case Char.fromCode c ->
>> ' ' -> ( JumpUp, States.enableAll )
>> _ -> ( msg, States.disableAll )
>> _ -> ( msg, States.enableAll )
>> else
>> ( msg, States.enableAll )
>>
>> ```
>> You could also delegate to other TEA-style modules. You could filter out
>> the Tick here instead of subscribing and unsubscribing (I like a simple
>> 'subscriptions' callback). Anything with `States.disableAll` will disable
>> all callbacks for that msg, so update will not be called, subscriptions
>> will not be called, and view will not be called (it returns the cached
>> values from last time so an exact match test of === in javascript shows
>> they are identical and thus Elm will do nothing).
>>
>> With the above `filters` you will not ever get
>> HandleKeyboardDown/HandleKeyboardUp in your `update` at all, ever, it is
>> either translated to another message or entirely canceled. Adding more
>> message conversions is as simple as adding another case, so you could add,
>> say a shooting state with like:
>> ```elm
>>
>> filters : Msg -> Model -> ( Msg, States Model Msg )filters msg model =
>> if model.state == Running then
>> case msg of
>> HandleKeyboardDown c ->
>> case Char.fromCode c ->
>> 'P' -> ( PauseToggle, States.enableAll )
>> ' ' -> ( JumpDown, States.enableAll )
>> 'z' -> ( StartShooting, States.enableAll )
>> _ -> ( msg, States.disableAll )
>> HandleKeyboardUp c ->
>> case Char.fromCode c ->
>> ' ' -> ( JumpUp, States.enableAll )
>> 'z' -> ( StopShooting, States.enableAll )
>> _ -> ( msg, States.disableAll )
>> _ -> ( msg, States.enableAll )
>> else
>> ( msg, States.enableAll )
>>
>> ```
>>
>> Just as simple as adding a readable case to each the down and up to send
>> the two StartShooting/StopShooting messages. You could easily pull values
>> from the model if the user can remap their keys and just do if's instead
>> too.
>>
>> But yes, the filtering of messages is done in one and only area, and if
>> you filter everything well then your update does not even need to become a
>> mess of nested case's for the various states and such but you know that you
>> will only get a message when it is actually valid for your state, so you
>> know that you will not get a, say, `JumpUp` message unless you are in the
>> `Running` state, so you do not even need to check that. Your operations
>> become simple and direct.
>>
>>
>> On Wednesday, August 10, 2016 at 10:27:42 AM UTC-6, Janis Voigtländer
>> wrote:
>>>
>>> Generally, Nick is right, I primarily want to hear feedback about the
>>> specific API proposal I made.
>>>
>>> However, I wrote the other day that "If there are alternatives *with
>>> currently available functions* to my proposal, very interested to hear
>>> it in this thread."
>>>
>>> So since Overmind seems to since have released some package/functions
>>> that are now available, it could be on-topic to see how/whether these do
>>> address points I try to address with the API proposal. Hence my questions
>>> (or "challenge" if you wish) in the previous message.
>>>
>>>
>>> 2016-08-10 18:13 GMT+02:00 Nick H <[email protected]>:
>>>
>>>> That is not what this discussion is about. Janis proposed a change to
>>>> the Keyboard API. The reason he started this thread was to get our
>>>> thoughts
>>>> on his proposal.
>>>>
>>>> On Wed, Aug 10, 2016 at 8:37 AM, OvermindDL1 <[email protected]>
>>>> wrote:
>>>>
>>>>> How is it off-topic considering its whole purpose is to handle this
>>>>> exact kind of issue? The discussion is about filtering messages,
>>>>> potentially translating them to a a better message that is for a specific
>>>>> purpose without needing to worry about checking for valid input in update
>>>>> area. It is handled generically.
>>>>>
>>>>>
>>>>> On Wednesday, August 10, 2016 at 8:44:12 AM UTC-6, Nick H wrote:
>>>>>>
>>>>>> Overmind, we can all see the thread that you started. It is still
>>>>>> off-topic for you to bring it up here.
>>>>>>
>>>>>> On Wed, Aug 10, 2016 at 7:40 AM, OvermindDL1 <[email protected]>
>>>>>> wrote:
>>>>>>
>>>>>>> Well for what it is worth I made a library that can filter messages
>>>>>>> before they are handled, so you could, for example, turn a keypress of
>>>>>>> 'w'
>>>>>>> into a message of 'GoForward' and 's' into 'GoBackward' or whatever.
>>>>>>> And
>>>>>>> if you cancel a message then it becomes entirely unhandled and you will
>>>>>>> never even see it appear in the event loop (update/subscription/view).
>>>>>>> If
>>>>>>> your convert a message then you never see the original, only your
>>>>>>> converted
>>>>>>> one. I am curious if anyone wants to test it with these issues as I
>>>>>>> think
>>>>>>> it would work very well as a, for example, key remapper. It is a
>>>>>>> nice-central place to put key mapping (since it has a model you could
>>>>>>> even
>>>>>>> setup a dynamic key mapper based on user settings with ease). Cleans
>>>>>>> up
>>>>>>> code in update as since you can expect to only receive valid messages
>>>>>>> if
>>>>>>> you filter them here first, vastly cleans up code there to only the
>>>>>>> actual
>>>>>>> work. The package is up at the elm package website (ProgramEx I think
>>>>>>> I
>>>>>>> named it). Still very in testing but it seems to be working utterly
>>>>>>> perfect in one of my big apps and has already cleaned up a lot of code.
>>>>>>>
>>>>>>> Here is how the filter code looks (this one 'delegates' to two
>>>>>>> different
>>>>>>> TEA-form libraries their messages and alters a Scroll event just like
>>>>>>> you'd
>>>>>>> expect to do with key remapping):
>>>>>>>
>>>>>>> ```elm
>>>>>>>
>>>>>>> filters : Msg -> Model -> ( Msg, States Model Msg )filters msg model =
>>>>>>> let
>>>>>>> log =
>>>>>>> debug_log (model.programFlags.debug_log |>
>>>>>>> Maybe.withDefault False) "filters" ( msg, model )
>>>>>>> in
>>>>>>> case msg of
>>>>>>> Helpers helpersMsg ->
>>>>>>> ( msg
>>>>>>> , States.enableAll |> States.delegate (helpers_delegate
>>>>>>> Helpers helpersMsg)
>>>>>>> )
>>>>>>>
>>>>>>> Mdl mdlMsg ->
>>>>>>> ( msg
>>>>>>> , States.enableAll
>>>>>>> |> States.delegate
>>>>>>> { key = "Mdl"
>>>>>>> , update = Just (\_ o -> Material.update mdlMsg
>>>>>>> o)
>>>>>>> , subscriptions = Just (\o ->
>>>>>>> Material.subscriptions Mdl o)
>>>>>>> }
>>>>>>> )
>>>>>>>
>>>>>>>
>>>>>>> MesgList_Scroll scrolled ->
>>>>>>> case model.loc of
>>>>>>> RoomLocation rid _ ->
>>>>>>> let
>>>>>>> doLoad : Bool
>>>>>>> doLoad =
>>>>>>> (model.firstMessageReached == False)
>>>>>>> && (scrolled.pos < 16)
>>>>>>> && (model.isLoadingOlder == False)
>>>>>>>
>>>>>>> && ((Dict.size
>>>>>>> model.active_room_msgs) >= 10)
>>>>>>>
>>>>>>> in
>>>>>>> if doLoad then
>>>>>>> ( MesgList_LoadOlder rid,
>>>>>>> States.enableAll )
>>>>>>> else
>>>>>>> ( msg, States.disableAll )
>>>>>>>
>>>>>>> _ ->
>>>>>>> ( msg, States.disableAll )
>>>>>>>
>>>>>>>
>>>>>>> _ ->
>>>>>>> ( msg, States.enableAll )
>>>>>>>
>>>>>>> ```
>>>>>>>
>>>>>>> On Tuesday, August 9, 2016 at 10:55:43 PM UTC-6, Nick H wrote:
>>>>>>>>
>>>>>>>> This would be a really nice improvement. I doubt there is anybody
>>>>>>>> using the keyboard API who wants to capture every key press. (But
>>>>>>>> would
>>>>>>>> love to see a counter-example!) Typing is handled by the HTML library
>>>>>>>>
>>>>>>>> On Mon, Aug 8, 2016 at 11:01 PM, Janis Voigtländer <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> To up the proposal, here a revision.
>>>>>>>>>
>>>>>>>>> Instead of proposing to *add* new functions, I now propose to
>>>>>>>>> *replace*
>>>>>>>>>
>>>>>>>>> Keyboard.presses : (KeyCode -> msg) -> Sub msgKeyboard.downs :
>>>>>>>>> (KeyCode -> msg) -> Sub msgKeyboard.ups : (KeyCode -> msg) -> Sub msg
>>>>>>>>>
>>>>>>>>> by
>>>>>>>>>
>>>>>>>>> Keyboard.presses : (KeyCode -> Maybe msg) -> Sub msgKeyboard.downs :
>>>>>>>>> (KeyCode -> Maybe msg) -> Sub msgKeyboard.ups : (KeyCode -> Maybe
>>>>>>>>> msg) -> Sub msg
>>>>>>>>>
>>>>>>>>> It would still be easy to recover the old behavior if wanted in a
>>>>>>>>> specific situation, by throwing in Just. But actually I posit
>>>>>>>>> that one almost never really wants to capture all keys. Usually, one
>>>>>>>>> wants
>>>>>>>>> to be selective. That selectiveness has to be expressed somewhere,
>>>>>>>>> and
>>>>>>>>> doing it with the Maybe type that is designed for such purpose is
>>>>>>>>> generally better than going for something like extra NoOp or
>>>>>>>>> NothingHappened or NotBound constructors that then need to be
>>>>>>>>> handled in a special way in the app’s update logic, without help from
>>>>>>>>> the
>>>>>>>>> type system. Making consideration of the selectiveness part of the
>>>>>>>>> modelling up front would lead to better design. That’s at least the
>>>>>>>>> case in
>>>>>>>>> the projects/repositories I have pointed to.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> 2016-08-08 14:48 GMT+02:00 Janis Voigtländer <
>>>>>>>>> [email protected]>:
>>>>>>>>>
>>>>>>>>>> A while back there was a thread
>>>>>>>>>> <https://groups.google.com/d/msg/elm-discuss/u-6aCwaJezo/fu-HMPy6CQAJ>
>>>>>>>>>>
>>>>>>>>>> about filtering subscriptions. The following is related, but can
>>>>>>>>>> also (and
>>>>>>>>>> probably better) be consumed and discussed independently. For those
>>>>>>>>>> that do
>>>>>>>>>> have that older thread as context in mind, the following differs in
>>>>>>>>>> two
>>>>>>>>>> essential ways:
>>>>>>>>>>
>>>>>>>>>> - Earlier, the discussion was about generic filtering of
>>>>>>>>>> arbitrary subscriptions. The following involves no genericity
>>>>>>>>>> whatsoever.
>>>>>>>>>> It is only a proposal about the Keyboard API specifically.
>>>>>>>>>> - The earlier thread was not rooted in practice, since very
>>>>>>>>>> little stuff had been built yet with subscriptions. In the
>>>>>>>>>> following, I
>>>>>>>>>> point to how things have played out in practice, based on uses
>>>>>>>>>> students
>>>>>>>>>> have made of the current API in projects.
>>>>>>>>>>
>>>>>>>>>> ------------------------------
>>>>>>>>>>
>>>>>>>>>> So, on to the subject matter:
>>>>>>>>>>
>>>>>>>>>> The keyboard package
>>>>>>>>>> <http://package.elm-lang.org/packages/elm-lang/keyboard>
>>>>>>>>>> currently contains functions such as:
>>>>>>>>>>
>>>>>>>>>> Keyboard.downs : (KeyCode -> msg) -> Sub msg
>>>>>>>>>>
>>>>>>>>>> Common uses (I’ll point to several repositories below) are such
>>>>>>>>>> that only some keys are relevant for an application. My proposal is
>>>>>>>>>> to have
>>>>>>>>>> functions such as:
>>>>>>>>>>
>>>>>>>>>> Keyboard.downsSelectively : (KeyCode -> Maybe msg) -> Sub msg
>>>>>>>>>>
>>>>>>>>>> where the semantics is that if a given KeyCode is mapped to
>>>>>>>>>> Nothing by the tagger, then no message gets sent along the
>>>>>>>>>> subscription; otherwise the Just is peeled off and the message
>>>>>>>>>> gets sent.
>>>>>>>>>> ------------------------------
>>>>>>>>>>
>>>>>>>>>> Let’s look at a practical case,
>>>>>>>>>> https://github.com/arpad-m/dontfall. It’s a game, where the
>>>>>>>>>> player uses the keyboard for part of the control. Important excerpts
>>>>>>>>>> from
>>>>>>>>>> the code are:
>>>>>>>>>>
>>>>>>>>>> The message type (in
>>>>>>>>>> https://github.com/arpad-m/dontfall/blob/master/src/BaseStuff.elm
>>>>>>>>>> ):
>>>>>>>>>>
>>>>>>>>>> type GameMsg = NothingHappened | ... several other messages ...
>>>>>>>>>>
>>>>>>>>>> The subscriptions definition (in
>>>>>>>>>> https://github.com/arpad-m/dontfall/blob/master/src/main.elm):
>>>>>>>>>>
>>>>>>>>>> subscriptions : GameData -> Sub GameMsgsubscriptions d =
>>>>>>>>>> Sub.batch
>>>>>>>>>> ([ Keyboard.downs (\c -> if Char.fromCode c == 'P' then
>>>>>>>>>> PauseToogle else NothingHappened) ] ++
>>>>>>>>>> if d.state == Running then
>>>>>>>>>> [ AnimationFrame.diffs Tick
>>>>>>>>>> , Keyboard.downs (\c -> if Char.fromCode c == ' '
>>>>>>>>>> then JumpDown else NothingHappened)
>>>>>>>>>> , Keyboard.ups (\c -> if Char.fromCode c == ' ' then
>>>>>>>>>> JumpUp else NothingHappened)
>>>>>>>>>> ]
>>>>>>>>>> else
>>>>>>>>>> [])
>>>>>>>>>>
>>>>>>>>>> The main case distinction in the main update function (in
>>>>>>>>>> https://github.com/arpad-m/dontfall/blob/master/src/main.elm):
>>>>>>>>>>
>>>>>>>>>> updateScene : GameMsg -> GameData -> (GameData, Cmd
>>>>>>>>>> GameMsg)updateScene msg d =
>>>>>>>>>> (case d.state of
>>>>>>>>>> ...
>>>>>>>>>> Running -> case msg of
>>>>>>>>>> MouseMove (x,_) -> { d | characterPosX = min x d.flWidth}
>>>>>>>>>> Tick t -> stepTime d t
>>>>>>>>>> PauseToogle -> { d | state = Paused }
>>>>>>>>>> JumpDown -> { d | jumpPressed = True }
>>>>>>>>>> JumpUp -> { d | jumpPressed = False }
>>>>>>>>>> _ -> d
>>>>>>>>>> , Cmd.none
>>>>>>>>>> )
>>>>>>>>>>
>>>>>>>>>> Given availability of the functions I propose above, the code
>>>>>>>>>> could instead look as follows:
>>>>>>>>>>
>>>>>>>>>> type GameMsg = ... only the other messages, no NothingHappened ...
>>>>>>>>>> subscriptions : GameData -> Sub GameMsgsubscriptions d =
>>>>>>>>>> Sub.batch
>>>>>>>>>> ([ Keyboard.downsSelectively (\c -> if Char.fromCode c ==
>>>>>>>>>> 'P' then Just PauseToogle else Nothing) ] ++
>>>>>>>>>> if d.state == Running then
>>>>>>>>>> [ AnimationFrame.diffs Tick
>>>>>>>>>> , Keyboard.downsSelectively (\c -> if Char.fromCode
>>>>>>>>>> c == ' ' then Just JumpDown else Nothing)
>>>>>>>>>> , Keyboard.upsSelectively (\c -> if Char.fromCode c
>>>>>>>>>> == ' ' then Just JumpUp else Nothing)
>>>>>>>>>> ]
>>>>>>>>>> else
>>>>>>>>>> [])
>>>>>>>>>> updateScene : GameMsg -> GameData -> (GameData, Cmd
>>>>>>>>>> GameMsg)updateScene msg d =
>>>>>>>>>> (case d.state of
>>>>>>>>>> ...
>>>>>>>>>> Running -> case msg of
>>>>>>>>>> MouseMove (x,_) -> { d | characterPosX = min x d.flWidth}
>>>>>>>>>> Tick t -> stepTime d t
>>>>>>>>>> PauseToogle -> { d | state = Paused }
>>>>>>>>>> JumpDown -> { d | jumpPressed = True }
>>>>>>>>>> JumpUp -> { d | jumpPressed = False }
>>>>>>>>>> , Cmd.none
>>>>>>>>>> )
>>>>>>>>>>
>>>>>>>>>> Advantages:
>>>>>>>>>>
>>>>>>>>>> 1.
>>>>>>>>>>
>>>>>>>>>> simpler message type, no special role no-op constructor needed
>>>>>>>>>> 2.
>>>>>>>>>>
>>>>>>>>>> no spurious update and render cycles while the game is running
>>>>>>>>>> 3.
>>>>>>>>>>
>>>>>>>>>> less room for bugs in the update logic
>>>>>>>>>>
>>>>>>>>>> Some additional comments on the latter two of these points:
>>>>>>>>>>
>>>>>>>>>> Re 2., given the current implementation, whenever a key is hit
>>>>>>>>>> that is not relevant, the update function is still called and
>>>>>>>>>> produces an
>>>>>>>>>> unchanged model, which is then rendered, which is extra/useless
>>>>>>>>>> work. Since
>>>>>>>>>> the game uses Graphics.*, no use can be made of Html.Lazy.* to
>>>>>>>>>> avoid the re-rendering. Even if something like Graphics.Lazy.*
>>>>>>>>>> were available, having to use it would not be as nice/pure as not
>>>>>>>>>> causing
>>>>>>>>>> those spurious updates in the first place.
>>>>>>>>>>
>>>>>>>>>> Re 3., given the current implementation, there is both more room
>>>>>>>>>> for bugs in the now and in a potential later, when extending the
>>>>>>>>>> game. In
>>>>>>>>>> the now, the programmer has to make sure that NothingHappened
>>>>>>>>>> does indeed not change the model. Concerning later, imagine that the
>>>>>>>>>> programmer extends the message type for some reason. With the
>>>>>>>>>> current
>>>>>>>>>> version of updateScene, the programmer might forget to actually
>>>>>>>>>> add a branch for handling the new message, and the compiler would
>>>>>>>>>> not catch
>>>>>>>>>> that, because of the _ -> d branch that will silently catch not
>>>>>>>>>> only NothingHappened but also the new message which was actually
>>>>>>>>>> supposed to make something happen. With the version of
>>>>>>>>>> updateScene after the proposed change, the situation would be
>>>>>>>>>> different. Since there is no _ -> d branch in that Running ->
>>>>>>>>>> case msg of ... part anymore (thanks to NothingHappened not
>>>>>>>>>> being a thing), the compiler will immediately complain if the
>>>>>>>>>> message type
>>>>>>>>>> is extended but the new message is not handled there. Bug prevented.
>>>>>>>>>> ------------------------------
>>>>>>>>>>
>>>>>>>>>> It’s not only this single project. I have observed students
>>>>>>>>>> applying different strategies to deal with “Not all keys are
>>>>>>>>>> relevant to my
>>>>>>>>>> program”. In each case, using an API with functions of type (KeyCode
>>>>>>>>>> -> Maybe msg) -> Sub msg instead of (KeyCode -> msg) -> Sub msg
>>>>>>>>>> would have been conceptually nicer and would have simplified things.
>>>>>>>>>>
>>>>>>>>>> Some more example repos:
>>>>>>>>>>
>>>>>>>>>> - https://github.com/chemmi/elm-rocket, uses type Key = Left
>>>>>>>>>> | Right | ... | NotBound and keyBinding : KeyCode -> Key and
>>>>>>>>>> then needs to make sure to correctly (non)-deal with NotBound
>>>>>>>>>> in functions like updateKeyDown; whereas just not having
>>>>>>>>>> NotBound, but having keyBinding : KeyCode -> Maybe Key and
>>>>>>>>>> using that in a call to a (KeyCode -> Maybe msg) -> Sub msg
>>>>>>>>>> function would simplify things with the same benefits as in the
>>>>>>>>>> above more
>>>>>>>>>> fully elaborated example case.
>>>>>>>>>> - https://github.com/Dinendal92/Abschlussprojekt-DP2016, less
>>>>>>>>>> complete project, but with same approach and issues as in the
>>>>>>>>>> preceding
>>>>>>>>>> example, using type Key = Space | Unknown and fromCode : Int
>>>>>>>>>> -> Key. Here, since eliminating Unknown would turn Key into a
>>>>>>>>>> type with only one constructor, even more conceptual
>>>>>>>>>> simplifications would
>>>>>>>>>> be enabled after a switch to the (KeyCode -> Maybe msg) ->
>>>>>>>>>> Sub msg approach.
>>>>>>>>>> - https://github.com/Shaomada/Elm-Project, quite elaborate
>>>>>>>>>> project, structured according to TEA, uses no special Key
>>>>>>>>>> type, instead maps with Char.fromCode in the calls to the
>>>>>>>>>> keyboard subscriptions, then has to case dispatch on actual
>>>>>>>>>> Chars at several places distributed over the update functions
>>>>>>>>>> of the TEA subcomponents. Subscribing with (KeyCode -> Maybe
>>>>>>>>>> msg) -> Sub msg functions should allow to eliminate branches
>>>>>>>>>> at some of those places, removing redundancies and room for bugs.
>>>>>>>>>> - https://github.com/Sulring/elmaction, similar story
>>>>>>>>>> (without TEA)
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> 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.
>>>>>>>
>>>>>>
>>>>>> --
>>>>> 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.
>>>>
>>>
>>> --
>> 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] <javascript:>.
>> 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.