This is mostly a RnD idea that came up when looking into mattstermiller/koffee#20
What I try here is to extract away the different prompts into submodules to be able to assign to the MainModel what dispatcher to use.
-
Creating WPF
UserControl
's. Sadly I couldn't find an easy way as FsXaml doesn't support proper code-behind for controls. Also Acadian-Ambulance/VinylUI doesn't seem to have the greatest support for handling that kind of stuff. Would be nice to have though, like to let VinylUI to handle the dispatcher and focus stacks. -
Create
IPrompt
interface withdispatcher
method that different prompts (ex:DeleteBookmarkPrompt
vsGoToBookmarkPrompt
) implements. -
Change to
module
based with staticdispatcher
functions. That's the code snippet I provided here withPrompts.fs
.
-
Edit the model binding in MainView.fs and try in an abstract way display the correct data depending on selected prompt. Would be nice to allow specifying if we want to show a list and/or the input field, and what text should be written in those field.
-
Make composing of prompts better (in the code, that is). The current
module
based approach makes it tedious to handle that stuff and the file could get really big. It isn't that easy to read actually, in comparison to the previous solution. -
Make some unified way of opening prompts. Get's annoying quite quick though as you can't really have circle dependencies in F# in a smooth way. For example: I can't access
Koffee.MainLogic.Nav
from the prompts without doing some major refactoring. I can't create aIPrompt
interface that theMainModel
has as a property/member for routing dispatcher events to the hypothetical "selected prompt instance", as some of the functions has dependency on theMainModel
orMainEvents
itself (ex: the dispatcher). -
Change the flow of the
fallback
dispatcher calls. Currently they go in kind of reverse order. Optimally we want theClosablePrompt
dispatcher to fire first, then if it didn't handle the event it should continue to the next one.
I think this is me bringing too much C#/OO into F#. I want to solve this problem with polymorphism, but I can feel the framework and language is somewhat holding me back. The fact that everything has to be declared above/before does force me to not cheat and create a spaghetti of dependencies for the prompts, and that's probably for the better as it's probably considered bad-practice in F# code. But what do I know.
It's of course possible to create FSM's for this case. I just haven't found the way to do so yet in a clean way, while still staying true with the conventions of VinylUI and F#.
Bottom line: I think this is trying to bend the language and framework against their wills. I am consceding this fight and going with basic implementation instead.
The problem I was trying to solve is close to the one you guessed at. I was trying to abstract away the prompt to have for example a prompt class that handles how the input is handled and data is diplayed, and change the binding to just bind to the given implementation's values. It was also an effort as I tried to use the same for the history panel "prompt". Using modules instead of interfaces was an experiment to see if that was nicer, but it turned a bit into unclear code. Classes are obviously the way to go when it comes to inheritence, but I tried with modules anyway. I blame my tired mind at the time for that approach.
That's how I'm used to do it in C#, but to reinforce my statement: I didn't know if those patterns can be taken into F#. I haven't studied any functional patterns yet.
The multibinding reminds me of old AngularJs code :) It's a little bit nostalgic (if you're unfamiliar with that, here's the docs on that. The dependency injection gets nasty quick in the same way, where you have to specify everything twice). That is, if you're referring to the Bind.modelMulti for the InputMode. That section I think could be shortened quite a bit if all those parameters were moved to model properties, and then have those properties be updated by some
IPrompt
implementation. For reference, I'm trying to follow Uncle Bob's G23: Prefer Polymorphism to If/Else or Switch/Case heuristic here.Your comments there on F# are so on point. I can really see how those limitations result in better code, but yes this does feel veeery jarring at the moment. So many habits I have to throw out the window that I don't know which ones to keep. I don't know if it's F# or you, but Koffee is actually very easy and fun to write tests for (except the model binding). The "pure"-ness and lack of objects and stateful methods is actually nice. Either way, big kudos to you for that!