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/don
>>>> tfall/blob/master/src/BaseStuff.elm):
>>>>
>>>> type GameMsg = NothingHappened | ... several other messages ...
>>>>
>>>> The subscriptions definition (in https://github.com/arpad-m/don
>>>> tfall/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.

Reply via email to