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] 
> <javascript:>> 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] <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.

Reply via email to