-
-
Notifications
You must be signed in to change notification settings - Fork 307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce lenses for ReportOpts, ReportSpec, CliOpts, etc. #1545
Conversation
65d32b1
to
f6200f2
Compare
963c280
to
a8f9244
Compare
f665d40
to
9693f6b
Compare
@simonmichael Do you have any opinions on the renaming of these option records? Do you think it's likely to cause much breakage? |
Xitan9, sorry. This is one I have been putting off because I have mixed feelings and it will take some work to have a useful opinion. This has been something to figure out for years and it's great that you have made a start. Why mixed feelings ? I think some use of lenses will be great for experienced developers, I worry that they will obfuscate the code further for new contributors and for future porters/maintainers; it seems like a hard balance, such that we'll have to choose one or the other. Then simply the adoption process seems a big task - lots of churn and potential for confusion with some of the code lensified and some not, unless we figure out a clear policy and plan. Also which lens package - lens, microlens, optics.. |
Not a problem. I've used microlens here since that was already in use in the code base – somebody had put it in hledger-ui at some point. I've done minimal refactoring to use the new lenses here, but I'm happy to remove those places I have put it in if you think they add more in indirection than they save in clarity. I think the most obvious place where they clear things up is in Hledger.UI.UIState, where the messes of nested pattern matching and record updating can be significantly simplified. |
Great. Yes indeed, hledger-ui is probably the most urgent need. I guess we already use microlens somewhere. I expect all will be well. |
I did a first pass on this. Likely to cause much breakage - do you mean, in other users of hledger-lib/hledger libs, like hledger-iadd, and custom scripts ? Not sure but I'm guessing they will all break. I see you add Data.Default in a few places, making the code more concise. I used to use this more but it seems to be out of favour in the community, and I think I agree. It breaks code navigation in IDEs, and makes the code a little harder to understand for the reader. I think perhaps we should avoid it. Lenses. It feels like they both giveth and taketh away, especially for non-experts. Code can be much more concise, and (with luck) also easier to understand superficially, but at the same time it becomes much harder to understand completely. Generating them with TH also breaks IDE navigation, and reputedly makes compilation slower (I don't know if that's still true or true in all cases). I guess we could avoid both of those issues by defining them explicitly, at the cost of huge boilerplate. Perhaps worth a try to see how it looks though. The typeclasses I guess are an optional extra, making it possible to use the lenses even more concisely. They too seem to make coding easier and full understanding harder, probably worth it though. Perhaps we can discuss this more and run through the changes in chat next ? I'll look out for you, or feel free to suggest a time or two and we could schedule it. |
Re our discussions, I have made the following changes.
Writing the lenses out manually is not so bad, and is very doable for all classes except possibly |
Some more comments:
|
Alternately, instead of eliminating UIOpts, we could make it bigger, and pull in all the ui options pulled from rawopts in an ad-hoc way. These include |
Regarding my second point above, it looks like this can be done with the following definition:
The price we pay is that we can no longer have a useful typeclass |
Actually, we could have our cake and eat it too through a more careful choice of the lens definitions. We could have normal ReportOpts lenses for anything which cannot possibly result in an error, and these fancier lenses only for |
How about:
|
That sounds like a reasonable proposal. This means we will currently leave field names for |
We are already using tiFieldName there, and the |
We will still need to rename some of the In that case, I will propose the following renaming for
As a first step, I will implement the above renamings, and hand-write classy lenses for Does this sound good? |
And I think I'll implement this in a new branch/PR for now, due to almost all the code being changed. |
22071c7 are the kind of renames I don't love, creating a new set of names for things. I'm thinking about alternatives. The ReportSpec renames are fine. |
They are the kind of clashy names where the policy says we resort to tiFieldName. We could avoid renaming everything, instead just doing minimal renames on the problem fields - rocolor, rodrop, roempty... |
Okay, so we would have a mix of naming styles for Would we use camel case for prefixed fields? My understanding of your proposal is that those would be called |
Okay, so we would have a mix of naming styles for ReportOpts fields. That is slightly aesthetically displeasing, but reasonable.
Yes it's ugly but it's just two extra characters on a couple of fields - I'd rather try it as a possibly temporary next step, than adding a new set of names.
Would we use camel case for prefixed fields? My understanding of your proposal is that those would be called roColor, roDrop, roEmpty, etc..
That would be the proper `tiFieldName` spelling, but I'm tentatively proposing something in between: `tifieldname`. It'll be less jarring than mixing `fieldname` and `tiFieldName` in the type don't you think.
|
Or we go all the way and switch all ReportOpts fields to tiFieldName but it seems a pity ? Your call. |
Hmm… I don't think it's significantly less jarring than
This would be my inclination, but I think experience has shown that I'm more willing to make breaking changes. I think these are pretty safe. If it's my call I'd go with this, but I'm happy for you to rein me in. If we do go this route it will cause a lot of conflicts with other PRs. I would recommend merging any other PRs which are ready or close to it, then creating a PR implementing just the renamings and merging it quickly. |
ReportOpts is a special case because:
- It's a large type, with many fields
- it's used everywhere, so clarity, brevity, and avoiding unnecessary churn are more important than usual
- many of the fields correspond to UI flags, and we'd like to preserve that correspondence, so renaming is less desirable than usual
Importing qualified is an alternative to renaming, but probably too cumbersome.
If we must rename I prefer it to be a consistent predictable change rather than different names. Like prepending type initials, or appending two underscores, or appending a not-too-distracting suffix like flag (emptyflag, dropflag, colorflag..)
Let's sleep on it.
|
There are several good options there. If we're not renaming everything, appending |
As general question, is it good or bad for (a) fields and (b) lenses to be visually distinct from other kinds of bindings (functions, "variables") ? I would guess it's good, because fields and lenses are used in specific ways, and it's helpful for comprehension and to avoid collisions if they follow some consistent, low-noise naming convention. I don't have enough lens experience to really know, though. If it's good, here are some possibilities (
PS isn't |
The only widely-used convention that I'm aware of is
|
Great link! Ok then let's follow that advice. So how about:
Right you are about --drop. I was using "flag" loosely. |
That sounds good. Just to make sure I understand, for the clashy
Agreed, I'm happy to play fast and loose with the term flag. |
I think Later we'll most likely rename to |
Great. I think we've decided on the scope for the next PR. I'll provide a link to that when I've completed it. Since the scope still includes renaming the fields of |
Great! Thanks Stephen.
|
Just recording this for my own information. The only PR which seem to have significant conflicts with a renaming of the fields of #1554 also has significant conflicts, but it is exploration and unlikely to be merged soon; I think that one is not a blocker. #1593 has a very minor conflict, but as that PR may not be in final form it may get worse. |
This is done to be more consistent with future field naming conventions, and to make automatic generation of lenses simpler. See discussion in \simonmichael#1545. rsOpts -> _rsReportOpts rsToday -> _rsDay rsQuery -> _rsQuery rsQueryOpts -> _rsQueryOpts
I have submitted #1624 to rename the fields of I just realised, based on |
Generally let's do the minimum renaming at this step. We can keep the legacy spelling for these types as well for now.
|
This is done to be more consistent with future field naming conventions, and to make automatic generation of lenses simpler. See discussion in \#1545. rsOpts -> _rsReportOpts rsToday -> _rsDay rsQuery -> _rsQuery rsQueryOpts -> _rsQueryOpts
With so many nested options, it would greatly improve developer ergonomics to have some lenses in there.
I've put together a few commits to this end, but I don't think the API is ideal yet. Since the options all have names ending in an underscore, I've generated the lenses by dropping the underscore from the end. The main problem with this is that ReportOpts has
color_
,drop_
,empty_
, andtranspose_
, for which dropping the underscore would result in name clashes. My proposal would be to give these slightly longer names to avoid the clash, for examplecolor_ -> showcolor_
,drop_ -> dropaccount_
,empty_ -> showempty_
,transpose_ -> transposetable_
, or something like that. We might also want to renamewatch_
andchange_
in UIOpts.I would be interested in hearing opinions on this.