Closed rfreytag closed 1 year ago
@LyndonGingerich @TysonMN do you think we should we should do something similar with the rest of the examples?
@marner2 Yes, I do think this edit for readability would help with the other examples. I am adding these parameter lists and return values to Capabilities.Core now. Could you merge this request so I can branch from 'master' again?
Ok sounds good. Please avoid any samples touched by #551 for now to avoid conflicts (until it gets merged in).
FileDialogsCmdMsg.Core changes were merged in with the commit; sorry. But I can stay away from:
I plan on next working on Capabilities.Core and perhaps merging in FileDialogsCmdMsg.Core.
@rfreytag, since this PR was already merged, can you create another PR that address my concerns?
I don't think this change was thought through with a lot of nuance. I see lines such as let init : string = ""
, which doesn't need a type annotation.
Unless I misunderstand, the samples attempt to communicate simply and clearly with those who are used to idiomatic F#. We do not wish to require readers to learn a new set of idioms specific to Elmish.WPF; many probably need only one or two samples to browse. Since the reader is already trying to learn a new framework, he should not have to learn a new way of writing F# at the same time. For this reason, I propose that we do not introduce new idioms that F# developers cannot immediately understand and effortlessly remember.
Changes this PR makes:
cmdMsg: CmdMsg
). This changed should have been considered individually. I don't know when type annotations help others, but I probably would not have added most of these myself. We have better ways of increasing clarity.function
to match
. I have no strong opinions on this change, but I will suggest that type unclarity on a function
might be better resolved by qualifying the discriminated union cases (and probably adding RequireQualifiedAccess
to the DU).On the other hand, we do use idioms in this sample that most F# developers probably wouldn't recognize:
init
for initial values. But this is an Elmish idiom, so I think we should keep it.Model
for the top-level record type. Developers will probably understand this.msg
to mean "message". This is a simple abbreviation and probably clear.m
to mean "model". I think this abbreviation is likely unclear. I move to expand it to model
. Where the type in question is not called model
, we should rename it to reflect the type in question.flip
. flip
decreases readability by increasing semantic code complexity. Let's replace it with lambdas.AutoOpen.map
. This function is difficult to read and comprehend, but replacing it might increase the code length significantly. Could we at least rename it to the more familiar (and less overloaded) lens
?InOut
. This DU is simple and I don't see how we could replace it with anything simpler anyway. We could use Choice
, but InOut
has more meaning. (If we decide that the effort of learning the in-message/out-message concept outweighs the organization, though, then we should replace InOut
with Choice
.)cata
. I know what cata
means, and Mark Seeman knows what cata
means, but I don't think most developers would recognize it. Dictionary.com defines it as "'down', 'against', 'back'", which would not make its meaning clear even to one familiar with the prefix. Let's inline it. match
is quickly and easily readable.get
/set
/map
. I see most people using lambdas. Let's replace get
and set
with lambdas and thus reduce mental overhead. We can keep map
where it's useful, but let's rename it to lens
.mapOutMsg
/mapInOutMsg
: These don't need to be so far away from their call sites. They are trivially short and used only once each. Let's inline them.LyndonGingerich TysonMN do you think we should we should do something similar with the rest of the examples?
@marner2 Above post intends to answer your question.
Yeah I think those suggestions in the last half of your comment make sense @LyndonGingerich, and we definitely should be more considered and deliberate with future sweeping updates of the samples.
I was ok with the update as-is because it's always valuable to get empirical feedback when someone unfamiliar with the project details their experience (and it was just a single file that was updated).
As far as explicitly specified types taking longer to read, while that may be true of production code, samples have a slightly different target. Which is again why I'm ok with liberal eta-expansion (especially of function
to match
) showing up in the samples. It just helps guide people who unfamiliar with the idioms. The other thing about these sample projects is that they are intentionally short, and generally in very small implementations you should err more on the side of verbosity. When you have a very large codebase where things get repeated a bunch, that's where the shortcuts start to kick in and pay off.
Adding more type annotations is typically fine. There were some in this PR that don't improve readability, and those can be removed.
Here's my first effort at a low-complexity version of the sample program. I'm not making a PR because I'm intending to diff on the original, not the current version with this PR merged.
@rfreytag , would these changes have made the code easier to read?
@TysonMN I will remove the changes to which you objected and reissue the PR. In my defense, I did offer my branch for review before issuing a pull request.
Glad to learn from this discussion.
Yes, no worries. You didn't do anything wrong. It is easy to change code (back).
As you have time would you review my fork, branch Adding_fully-specified_parameter_lists
latest commit (6de8aae7)?
The commit note reads... "
get
, set
, and map
per @TysonMN preference. Window1.init
, Window2.bindings
per @LyndonGingerich preference. init
, update
, and bindings
out of App
to allow App.Window1
and App.Window2
for readability in both bindings
and update
. Program.fs
now references AppModule.init
, AppModule.bindings
and AppModule.update
. unit
per @LyndonGingerich but fully-specified parameters seemed to require the remaining unit
references. NewWindow.Core
and FileDialogsCmdMsg.Core
).
"If this doesn't suit I accept it might be best if I stay in my fork.
I'll issue the PR if you folks are satisfied.
@LyndonGingerich I have been rushing to revise/undo my prior PR. Will read your thoughts with interest as time allows.
Please first create a PR that only addresses the concerns I raised on this PR. After that is merged, we can look at your other improvement suggestions.
I found Sample/NewWindow.Core/App.fs hard to read. I have a version in my fork, branch: Adding_fully-specified_parameter_lists that I find more readable with the following changes:
App
, moduleWindow1
and moduleWindow2
have been renamed moduleApp_Window1
and moduleApp_Window2
so their usage inApp.bindings
is not confused with the actual references toWindow2
inApp.bindings
.App
, moduleWindow1
, and moduleWindow2
:get
,set
, andmap
so they specify the type on which they operate for readability. They are now respectively:App
, moduleWindow1
:get_Window1State1
,set_Window1State1
, andmap_Window1State1
andApp
, moduleWindow2
:get_Window2
,set_Window2
,map_Window2
.I added fully-specified parameter lists to the remaining
Sample/NewWindow.Core
F# files while I was at it. And then for good measure I did the same toFileDialogsCmdMsg.Core
.Doing this helped me understand what was going on. I hope it will help others adopt Elmish.WPF. I will issue a pull request if you like where I'm going.