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

Reply via email to