Okay, saw this only after sending my previous message. Still, the FilterOnly message to be handled in update functions now is like a return of the NothingHappened message that I was so happy to have eliminated. Plus, the filters function’s added complexity. I still stand with “I’d rather have the Keyboard API changed in the way I proposed, than to have to use this more complicated machinery.”
2016-08-10 19:38 GMT+02:00 OvermindDL1 <[email protected]>: > 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]>: >> >>> > 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/don >>>>>>>>>>> tfall. 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. >>>>> >>>> >>>> -- >>> 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.
