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/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. > -- 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.
