A while back there was a thread 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:
So, on to the subject matter:
The keyboard package 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 GameMsg
subscriptions 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 GameMsg
subscriptions 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:
simpler message type, no special role no-op constructor needed
no spurious update and render cycles while the game is running
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:
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.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.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.<div title="MDH:PGRpdj48ZGl2PjxkaXY+PGRpdj48ZGl2PkEgd2hpbGUgYmFjayB0aGVyZSB3YXMgYSBbdGhyZWFk XShodHRwczovL2dyb3Vwcy5nb29nbGUuY29tL2QvbXNnL2VsbS1kaXNjdXNzL3UtNmFDd2FKZXpv L2Z1LUhNUHk2Q1FBSikgYWJvdXQgZmlsdGVyaW5nIHN1YnNjcmlwdGlvbnMuIFRoZSBmb2xsb3dp bmcgaXMgcmVsYXRlZCwgYnV0IGNhbiBhbHNvIChhbmQgcHJvYmFibHkgYmV0dGVyKSBiZSBjb25z dW1lZCBhbmQgZGlzY3Vzc2VkIGluZGVwZW5kZW50bHkuIEZvciB0aG9zZSB0aGF0IGRvIGhhdmUg dGhhdCBvbGRlciB0aHJlYWQgYXMgY29udGV4dCBpbiBtaW5kLCB0aGUgZm9sbG93aW5nIGRpZmZl cnMgaW4gdHdvIGVzc2VudGlhbCB3YXlzOjxicj48YnI+PC9kaXY+KiBFYXJsaWVyLCB0aGUgZGlz Y3Vzc2lvbiB3YXMgYWJvdXQgZ2VuZXJpYyBmaWx0ZXJpbmcgb2YgYXJiaXRyYXJ5IHN1YnNjcmlw dGlvbnMuIFRoZSBmb2xsb3dpbmcgaW52b2x2ZXMgbm8gZ2VuZXJpY2l0eSB3aGF0c29ldmVyLiBJ dCBpcyBvbmx5IGEgcHJvcG9zYWwgYWJvdXQgdGhlIEtleWJvYXJkIEFQSSBzcGVjaWZpY2FsbHku PGJyPjwvZGl2PiogVGhlIGVhcmxpZXIgdGhyZWFkIHdhcyBub3Qgcm9vdGVkIGluIHByYWN0aWNl LCBzaW5jZSB2ZXJ5IGxpdHRsZSBzdHVmZiBoYWQgYmVlbiBidWlsdCB5ZXQgd2l0aCBzdWJzY3Jp cHRpb25zLiBJbiB0aGUgZm9sbG93aW5nLCBJIHBvaW50IHRvIGhvdyB0aGluZ3MgaGF2ZSBwbGF5 ZWQgb3V0IGluIHByYWN0aWNlLCBiYXNlZCBvbiB1c2VzIHN0dWRlbnRzIGhhdmUgbWFkZSBvZiB0 aGUgY3VycmVudCBBUEkgaW4gcHJvamVjdHMuPGJyPjxicj4tLS0tLS0tLS0tLTxicj48YnI+PC9k aXY+U28sIG9uIHRvIHRoZSBzdWJqZWN0IG1hdHRlcjo8YnI+PGJyPjwvZGl2PlRoZSBba2V5Ym9h cmQgcGFja2FnZV0oaHR0cDovL3BhY2thZ2UuZWxtLWxhbmcub3JnL3BhY2thZ2VzL2VsbS1sYW5n L2tleWJvYXJkKSBjdXJyZW50bHkgY29udGFpbnMgZnVuY3Rpb25zIHN1Y2ggYXM6PGJyPjwvZGl2 PmBgYGhhc2tlbGw8YnI+S2V5Ym9hcmQuZG93bnMgOiAoS2V5Q29kZSAtJmd0OyBtc2cpIC0mZ3Q7 IFN1YiBtc2c8YnI+PGRpdj5gYGA8YnI+PC9kaXY+PGRpdj5Db21tb24gdXNlcyAoSSdsbCBwb2lu dCB0byBzZXZlcmFsIHJlcG9zaXRvcmllcyBiZWxvdykgYXJlIHN1Y2ggdGhhdCBvbmx5IHNvbWUg a2V5cyBhcmUgcmVsZXZhbnQgZm9yIGFuIGFwcGxpY2F0aW9uLiBNeSBwcm9wb3NhbCBpcyB0byBo YXZlIGZ1bmN0aW9ucyBzdWNoIGFzOjxicj48L2Rpdj48ZGl2PmBgYGhhc2tlbGw8YnI+S2V5Ym9h cmQuZG93bnNTZWxlY3RpdmVseSA6IChLZXlDb2RlIC0mZ3Q7IE1heWJlIG1zZykgLSZndDsgU3Vi IG1zZzxicj5gYGA8YnI+PC9kaXY+PGRpdj53aGVyZSB0aGUgc2VtYW50aWNzIGlzIHRoYXQgaWYg YSBnaXZlbiBgS2V5Q29kZWAgaXMgbWFwcGVkIHRvIGBOb3RoaW5nYCBieSB0aGUgdGFnZ2VyLCB0 aGVuIG5vIG1lc3NhZ2UgZ2V0cyBzZW50IGFsb25nIHRoZSBzdWJzY3JpcHRpb247IG90aGVyd2lz ZSB0aGUgYEp1c3RgIGlzIHBlZWxlZCBvZmYgYW5kIHRoZSBtZXNzYWdlIGdldHMgc2VudC48YnI+ PGJyPi0tLS0tLS0tLS0tPGJyPjxicj48L2Rpdj48ZGl2PkxldCdzIGxvb2sgYXQgYSBwcmFjdGlj YWwgY2FzZSwgaHR0cHM6Ly9naXRodWIuY29tL2FycGFkLW0vZG9udGZhbGwuIEl0J3MgYSBnYW1l LCB3aGVyZSB0aGUgcGxheWVyIHVzZXMgdGhlIGtleWJvYXJkIGZvciBwYXJ0IG9mIHRoZSBjb250 cm9sLiBJbXBvcnRhbnQgZXhjZXJwdHMgZnJvbSB0aGUgY29kZSBhcmU6PGJyPjxicj48L2Rpdj48 ZGl2PlRoZSBtZXNzYWdlIHR5cGUgKGluIGh0dHBzOi8vZ2l0aHViLmNvbS9hcnBhZC1tL2RvbnRm YWxsL2Jsb2IvbWFzdGVyL3NyYy9CYXNlU3R1ZmYuZWxtKTo8YnI+PC9kaXY+PGRpdj5gYGBoYXNr ZWxsPGJyPjwvZGl2PjxkaXY+dHlwZSA8c3BhbiBjbGFzcz0iIj5HYW1lTXNnPC9zcGFuPiA8c3Bh biBjbGFzcz0iIj49PC9zcGFuPiA8c3BhbiBjbGFzcz0iIj5Ob3RoaW5nSGFwcGVuZWQgfCAuLi4g c2V2ZXJhbCBvdGhlciBtZXNzYWdlcyAuLi48YnI+PC9zcGFuPjwvZGl2PjxkaXY+YGBgPGJyPjwv ZGl2PjxkaXY+VGhlIHN1YnNjcmlwdGlvbnMgZGVmaW5pdGlvbiAoaW4gaHR0cHM6Ly9naXRodWIu Y29tL2FycGFkLW0vZG9udGZhbGwvYmxvYi9tYXN0ZXIvc3JjL21haW4uZWxtKTo8YnI+PC9kaXY+ PGRpdj5gYGBoYXNrZWxsPGJyPnN1YnNjcmlwdGlvbnMgOiBHYW1lRGF0YSAtJmd0OyBTdWIgR2Ft ZU1zZzxicj5zdWJzY3JpcHRpb25zIGQgPTxicj4mbmJzcDsmbmJzcDsmbmJzcDsgU3ViLmJhdGNo PGJyPiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyAoWyBLZXlib2Fy ZC5kb3ducyAoXGMgLSZndDsgaWYgQ2hhci5mcm9tQ29kZSBjID09ICdQJyB0aGVuIFBhdXNlVG9v Z2xlIGVsc2UgTm90aGluZ0hhcHBlbmVkKSBdICsrPGJyPiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNw OyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBpZiBkLnN0YXRlID09 IFJ1bm5pbmcgdGhlbjxicj4mbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJz cDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgWyBBbmlt YXRpb25GcmFtZS5kaWZmcyBUaWNrPGJyPiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZu YnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNw OyAsIEtleWJvYXJkLmRvd25zIChcYyAtJmd0OyBpZiBDaGFyLmZyb21Db2RlIGMgPT0gJyAnIHRo ZW4gSnVtcERvd24gZWxzZSBOb3RoaW5nSGFwcGVuZWQpPGJyPiZuYnNwOyZuYnNwOyZuYnNwOyZu YnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNw OyZuYnNwOyZuYnNwOyAsIEtleWJvYXJkLnVwcyAoXGMgLSZndDsgaWYgQ2hhci5mcm9tQ29kZSBj ID09ICcgJyB0aGVuIEp1bXBVcCBlbHNlIE5vdGhpbmdIYXBwZW5lZCk8YnI+Jm5ic3A7Jm5ic3A7 Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5i c3A7Jm5ic3A7Jm5ic3A7Jm5
Please don’t derail this thread. As its title says, and as my preface to the opening post says, it is specifically about the Keyboard API.
If you want to pursue a completely different direction with a more general API for generic “skipping updates” stuff, please continue the other thread I pointed to, or start a new thread.
If there are alternatives with currently available functions to my proposal, very interested to hear it in this thread. Also interested in hearing suggestions for other changes to the Keyboard API that would address my points in an alternative way. But suggestions that are dependent on stuff that does not exist and that is not keyboard specific are derailing.
--
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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
Am I correct in thinking that this is something that would benefit everyone who wants to handle keyboard shortcuts in Elm?
If the scenario is:
then yes, that would be nicer to do with the proposed new functions rather than the existing ones, since you would not need to suddenly add a NoOp message constructor and provide (trivial) update logic for it, and possibly fix a number of other places in the program where case distinction on message constructors happens. Instead, you simply map the wanted keys to Just the relevant message constructors you already have, and all others to Nothing, and don’t need to touch any other part of your program.
--
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+unsubscribe@googlegroups.com.
To up the proposal, here a revision.
Instead of proposing to add new functions, I now propose to replace
Keyboard.presses : (KeyCode -> msg) -> Sub msg
Keyboard.downs : (KeyCode -> msg) -> Sub msg
Keyboard.ups : (KeyCode -> msg) -> Sub msg
by
Keyboard.presses : (KeyCode -> Maybe msg) -> Sub msg
Keyboard.downs : (KeyCode -> Maybe msg) -> Sub msg
Keyboard.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.
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 )
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.
My opening post discussed the API proposal here in the context of several pieces of very concrete code using Keyboard (written as student projects not for the purpose of showing a particular point about Elm, but to simply get stuff done).
Please take one of them, preferably the one I elaborated on at some length in that message, https://github.com/arpad-m/dontfall. In the message I showed which code in that project would be affected by the proposed API change, and how the new code would look. Can you show how your proposal would affect that project? Which functions would be changed in what way? Then we could see whether your proposal addresses the points that I try to address (and mentioned as such) with my proposal.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.
subscriptions : GameData -> Sub GameMsg subscriptions 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 [])
subscriptions : GameData -> Sub GameMsg subscriptions d = Sub.
batch
[ Keyboard.downs HandleKeyboardDown
, Keyboard.ups HandleKeyboardUps
, if d.state == Running then AnimationFrame.diffs Tick else Sub.none
]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 )
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 )
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
case msg of
HandleKeyboardUp _ -> ( msg, States.disableAll )
HandleKeyboardDown _ -> ( msg, States.disableAll )
_ -> ( msg, States.enableAll )case Char<span class="pl-k" style="box-sizing: border-box; text-shadow: none !important; box-shadow: none !important; color: rgb(133,
What will the GameMsg type look like?
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.
type alias MsgFiltered = { HandleKeyboardUp Char , HandleKeyboardDown Char } type alias Msg = FilterOnly MsgFiltered | Other | Interesting | To | Update | Messages | Like | JumpDown | JumpUp
Let me answer that myself, after looking some more at your proposal.
The GameMsg type would become more complicated than it was before, rather than becoming less complicated as was the case with my proposal/design.
Moreover, the update function would also be more complicated. You say that certain messages will never get there. But still the compiler will force you to do something about them, due to exhaustiveness checking. In my original post I argued that using the new API the update function would be simpler and more refactoring safe in case of future extensions. That advantage is lost with your approach (if you don’t agree with that assessment, please show how the update function would look, concretely).
Plus, the filters function adds conceptual complexity, whereas in my original post the goal was reduced conceptual complexity, and the changes I showed there on the concrete project demonstrate that such a reduction is indeed achieved.
So, your approach may have other uses, but as far as I can see it does not have the use I was after with the proposed API change, namely to simplify typical keyboard handling code as exemplified by those five different projects of different people.
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.”
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.
Okay, saw this only after sending my previous message. Still, the
FilterOnlymessage to be handled inupdatefunctions now is like a return of theNothingHappenedmessage that I was so happy to have eliminated. Plus, thefiltersfunction’s added complexity. I still stand with “I’d rather have theKeyboardAPI changed in the way I proposed, than to have to use this more complicated machinery.”
--
You received this message because you are subscribed to a topic in the Google Groups "Elm Discuss" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elm-discuss/Ud7WzAhRsqE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elm-discuss+unsubscribe@googlegroups.com.
I don’t understand your concern. The Keyboard API would not get larger, because I have proposed to replace three old functions by three new functions.
The rest of your concerns build on that concern (ballooning), so seem immaterial to me at the moment.
Let’s judge the proposed change to the Keyboard API on its own merits, not on some feared implicit outreach to other APIs.
Other issues like onScroll etc. may have their own API problems. They are not directly affected by the change to the Keyboard API, though. Maybe you want to discuss those other API problems in separate threads, with concrete examples as well?
Generally, if you haven’t yet, you might want to go back and read the original “generic subscription filtering” thread I pointed to in the first post, in full. And I may add that since then I have been on the lookout for specific cases where subscription filtering is desired. I haven’t found enough cases to convince myself that a generic mechanism is called for. In fact, I found exactly the one, keyboard handling, that I am addressing here. I naturally have looked in only specific places, but let me say that I have had more than those five students coding Elm projects.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.
I like your general solution, OvermindDL1, but I don't think it is mutually exclusive with improving the keyboard api as Janis suggested. For general cases, your solution is great. But as several people already said, with Keyboard it is practically always the case that you want to filter, ergo it makes sense to "bake it into the api" of keyboard.
So from me +1 for Janis revised proposal for this specific case, but also a thumbs up to the general approach to filtering. OvermindDL1, do you want to write a post about this showing it to newcomers in more detail?
I don’t understand your concern. The
KeyboardAPI would not get larger, because I have proposed to replace three old functions by three new functions.
The rest of your concerns build on that concern (ballooning), so seem immaterial to me at the moment.
Generally, if you haven’t yet, you might want to go back and read the original “generic subscription filtering” thread I pointed to in the first post, in full. And I may add that since then I have been on the lookout for specific cases where subscription filtering is desired. I haven’t found enough cases to convince myself that a generic mechanism is called for. In fact, I found exactly the one, keyboard handling, that I am addressing here. I naturally have looked in only specific places, but let me say that I have had more than those five students coding Elm projects.
Ah entirely replace them
Yes, that’s what I proposed in an earlier message in this thread.
An action manager like mentioned above could make it very easy then, especially if it was a state-based action mapper as is common in many windowing systems.
An action manager would be something else, and not needed for the cases I showed. Concerning extending the Keyboard API in a different direction (stateful), see the “conditions” set out by Evan in the README: https://github.com/elm-lang/keyboard/blob/master/README.md.
Just many experiences over many decades of seeing once good API’s explode and become a mess,…
You may find that what you experienced in other languages and frameworks about how APIs develop over time does not translate to how things happen in the Elm sphere, due to the way Evan manages things. In particular for something like the Keyboard API, which depends on native code and thus will continue to stay under his control.
Only one?
Yes, indeed. Note what I wrote there. It talks about filtering subscriptions. All the examples you mention do not seem to be about subscriptions, but instead about updates that come from other sources, probably from HTML events. Where (except keyboard handling) have you wanted to apply a filter function to a Sub-typed thing? (Leading us away from keyboard, so maybe this is more suitable to continue in the other thread about generic subscription filtering.)
Only one?
Yes, indeed. Note what I wrote there. It talks about filtering subscriptions. All the examples you mention do not seem to be about subscriptions, but instead about updates that come from other sources, probably from HTML events. Where (except keyboard handling) have you wanted to apply a filter function to a
Sub-typed thing? (Leading us away from keyboard, so maybe this is more suitable to continue in the other thread about generic subscription filtering.)
subscriptions : Model -> Sub Msg subscriptions model = Sub.batch [ port_MessageOptionCB (\{ text } -> MessageOptionCB text) , onInfoConnect (\{ msg } -> InfoConnect msg.uid) , onRoomListInit (\{ msg, cb_data } -> RoomList_Init cb_data msg.roomlist) , onRoomListJoined (\{ msg, cb_data } -> RoomList_Joined cb_data msg.roomlist) , onRoomListParted (\{ msg, cb_data } -> RoomList_Parted cb_data msg.roomlist) , onRoomListNotif (\{ msg, cb_data } -> RoomList_Notif cb_data msg.msgs) , onRoomClosed (\{ msg, cb_data } -> Room_Part cb_data msg) , onRoomInfo (\{ msg, cb_data } -> Room_Info cb_data msg) , onRoomMsgsInit (\{ msg, cb_data } -> RoomMsg_Init cb_data msg) , onRoomMsgsNew (\{ msg, cb_data } -> RoomMsg_New cb_data msg) , onRoomUserInit (\{ msg, cb_data } -> RoomUser_Init cb_data msg) , onRoomUserJoined (\{ msg, cb_data } -> RoomUser_Joined cb_data msg) , onRoomUserParted (\{ msg, cb_data } -> RoomUser_Parted cb_data msg) , onRoomNotif (\{ msg, cb_data } -> RoomMsg_Notif cb_data msg.msgs) , onRoomSyncState (\{ msg, cb_data } -> RoomMsg_SyncState cb_data msg) , onRoomSyncJoin (\{ msg, cb_data } -> RoomMsg_SyncJoin cb_data msg) , onRoomSyncLeave (\{ msg, cb_data } -> RoomMsg_SyncLeave cb_data msg) , {- snip a *lot* more -}
For actual subscription filters that I am using I have a lot of ports
Yes, I can see that that would be an additional case. Ports have not been on my radar, since they were not relevant to the projects considered.
So Keyboard and ports.
For ports, that could motivate a similar change, where
port name : (Type -> msg) -> Sub msg
becomes
port name : (Type -> Maybe msg) -> Sub msg
But let’s not make this part of the current proposal.
In any case, that’s still only two cases (since all ports would be handled equally), nothing spreading to all APIs.
Also, since my last email, more than 100 more lines total are gone.
That’s nice. But let’s not confuse it with anything meaningful about the proposal at hand. You are obviously removing a lot of stuff. There’s no way really for us to know why that stuff was there and what other strategies might be applicable to remove it or to have prevented it from being there in the first place. If that’s to be discussed, it belongs in the other thread, about your “generic filtering of all kinds of events” proposal.
For the five concrete projects I pointed to, I say with confidence that the Keyboard API proposal I brought up here achieves something which that other proposal does not, in terms of simplifying the code.
Also, that is the level where I want to do filtering in this case: as close to the event source as possible. Since the idea is that I am not interested in a bunch of events: I do not want them to be sent to my application at the first place.
--
You throw in a lot of different ideas. I’m getting confused.
Concerning the approach with having both subscriptions and your new filters hook, I still think what I wrote in my last message (about the case if you were to make the same decisions as the original code about which subscriptions are running when, particularly concerning the animation frame ticking):
However, the result will be again more complex and less readable, I think. You will have the dealing with subscriptions distributed over different places in the code, while the original version and also the version improved by selective Keyboard functions has only one.
Your new message does not seem to try to rebut this. Instead, it proposes a completely different route? The Keyboard.keyMapChar and Keyboard.keyMapFn etc. things. This would be intended as completely replacing the current Keyboard API? As a more radical proposal than just changing the types of the current Keyboard.downs etc. functions by bringing Maybe into play?
--
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
Okay, this keyMap* suggestion would need more scrutiny. Being a more radical departure from the current API, it is not obvious that all current Keyboard uses would still be well supported.
I will open an issue in the Keyboard repository with my more incremental API proposal.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.
Okay, this
keyMap*suggestion would need more scrutiny. Being a more radical departure from the current API, it is not obvious that all currentKeyboarduses would still be well supported.