-
Notifications
You must be signed in to change notification settings - Fork 15
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
Structuring legacy code so that era-based code does not depend on it #158
Conversation
7866eee
to
3c5d1d0
Compare
bec404f
to
9de3e58
Compare
, GovernanceCmds (..) | ||
, GenesisCmds (..) | ||
, TextViewCmds (..) | ||
, LegacyAddressCmds (..) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I dont't get this part:
An additional change was made which is that all legacy command type names are prefix with Legacy. The intention of this is to drop the EraBased prefix from era-based command types. A prefix is necessary to avoid confusion between the two types (which is possible). It is better that it is the legacy code is prefixed rather than the era-based code because we will increasingly work with era-based code so it is better for the naming overhead to live with the legacy code.
EraBased commands aren't prefixed from what I could find.
I'm not a big fan of prefixing types with Legacy
- module name should be enough imo. Unless we want to turn types into a constant reminder that they should be removed eventually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the reasons I opted for Legacy
vs just L
or omitting the prefix is greppability.
For example I know the type hasn't been imported indirectly into an EraBased
module via some other module without Legacy
in its qualified name:
find cardano-cli/src/Cardano/CLI/EraBased -name '*.hs' | grep -v dist-newstyle | xargs grep Legacy
This is also an additional reason why it's better to prefix the legacy code than the era-based code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module name is Legacy
so without prefixing types you should be able to do the same, because the import would be there, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is true, but imports can still pass transitively through modules that lack Legacy
in the qualified module name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module name is the big one though, so greppability of the prefix is a secondary concern in comparision, yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The surest way is to put it in a different library component altogether, but not sure if we are keen on going that far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, you can use HLS call-hierarchy to inspect usage sites. I think I also saw one team somewhere which had CI checks for modules' imports limiting what could be included from where - I think this should be doable in haskell as well.
In the end I'm not pushing for changing it to L, I'm just listing possible alternatives. If you feel like leaving this change as is, I'm ok with it. 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be routine to import EraBased
modules from Legacy
modules so we could drop the Legacy
prefix altogether at the cost of using qualified imports or EraBased
code when in a Legacy
module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's leave it in there and have a separate PR for deciding what to do with the prefix. That way we can have a good look at the impact of different decisions with actual code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the Legacy prefix, it immediately tells me I'm dealing with a legacy command (i.e I don't have to think about it).
| GovernanceCmds GovernanceCmds | ||
| GenesisCmds GenesisCmds | ||
| TextViewCmds TextViewCmds | ||
= LegacyAddressCmds LegacyAddressCmds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: prefix constructors with L
instead of Legacy
for better readability. The meaning still would be obvious enough I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good effort!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work doing this now and saving us more headache down the road 👍
Cardano.Cli.*
has some top level modules that we should also group together/move in a more appropriate place
Cardano.CLI.Run.Legacy.TextView | ||
Cardano.CLI.Run.Legacy.Transaction | ||
Cardano.CLI.Run.Legacy.Validate | ||
Cardano.CLI.Legacy.Run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok cool. I'll do a follow up.
…no.CLI.EraBased.Options.Common
….CLI.Types.Governance
…ce Cardano.CLI.EraBased.Options.Common
…tificateTransfer from Cardano.CLI.Legacy.Run.Governance to Cardano.CLI.EraBased.Run.Governance
… Cardano.CLI.EraBased.Errors.StakeAddress
…o.CLI.EraBased.Run.Certificate
9de3e58
to
2e2dda4
Compare
Please read the "Context" and "Reading this PR" sections before reviewing the code.
Changelog
Context
Currently, we have some modules that are "legacy" and serve to implement the legacy command structure and other modules that are "era-based" modules that serve to implement the era-based command structure.
Occasionally these modules were located in the wrong place (for example some legacy modules were found to be under
EraBased
. Furthermore there is no "home" for the legacy module.This PR aims to do four main things:
Cardano.CLI.EraBased
and all the legacy code is underCardano.CLI.Legacy
.The first two goals are important for the eventual day we remove legacy modules from the project.
If we proceed along these goals, it should be possible to delete just the legacy modules when the time comes and break (at compile time) very little code.
If era-based modules depend on legacy modules, the process will be a lot more involved.
Furthermore, if these boundary is not established early then more code will be written that causes the bad dependency to become more and more entrenched. Haskell does not allow cyclic dependencies after all, so there is a risk more and more code gets put into legacy modules as has already happened and used from era-based modules and existing bad dependencies will prevent us from adding newer dependencies that point in the right direction.
The third goal means we duplicate code less and if we fix bugs, we will not need to fix them in two places.
The fourth goal is important because there are existing commands in the legacy command structure we ought to port over to the era-based command structure (
transaction
for instance) and in the longer term we want to deprecate and delete the legacy command structure so porting is necessary.The module structure that results from this PR is as follows:
The module structure for
EraBased
commands:Cardano.CLI.EraBased.Commands.**
Command types and render functions
Cardano.CLI.EraBased.Errors.**
Errors
Cardano.CLI.EraBased.Options.Common
CLI parsers for non-command types
Cardano.CLI.EraBased.Options.**
CLI parsers for commands
Cardano.CLI.EraBased.Run.**
Command run functions
The module structure for
Legacy
commands follows the same pattern except underCardano.CLI.Legacy
instead.It is not anticipated that this PR will affect ongoing work (much) because it deals with the relationship between legacy code and era-based code and moving legacy code around.
Feature work is currently concentrated around era-based modules that are already largely where they are supposed to be and this PR does not change that.
Reading this PR
This PR is admittedly very large. However, in order to achieve the stated aims, a lot of code needs to move around.
The risk of this PR is minimised by the fact that no code is new (this is just a matter of moving code around after all) and the fact we have CLI golden tests which serve as a reliable regression test for the CLI interface.
There are no changes to
*.cli
files which means the CLI interface has not changed at all.Every commit in this PR compiles.
This means if necessary, it is possible to review the commits of the PR individually in chronological order. If there are issues with merging this PR in its entirety it is possible to select a sequence of commits that can be merged instead and the rest pushed to a new PR.
If this is the case, when reviewing this PR, indicate which commit you are willing to merge up too as part of this PR and the PR can be reset to that commit and a new PR raised for the rest.
All of the commits are designed to service the four goals listed.
If a module was renamed, it was because it did not conformed to the desired separation.
If a module was split, it was because of one of the following:
If modules are merged, it was because the code they contained serve a similar purpose.
An additional change was made which is that all legacy command type names are prefix with
Legacy
. The intention of this is to drop theEraBased
prefix from era-based command types. A prefix is necessary to avoid confusion between the two types (which is possible). It is better that it is the legacy code is prefixed rather than the era-based code because we will increasingly work with era-based code so it is better for the naming overhead to live with the legacy code.Related PRs
A lot of the changes were inspired by #158 which involves importing
transaction build
into the era-based command structure. The move of that command was not straight forward due to modules not being in the right place and import direction being wrong and inducing cycling dependencies among other things.The idea with this PR is to collect all those changes that are "dumb refactoring" into one PR and apply it across the board (rip the bandaid so-to-speak).
Int is anticipated that after merging this PR, that other PR will be a lot smaller.
Verification
It was verified no
EraBased
modules depend onLegacy
modules like this:If the command returns no results, it means no bad dependencies are present.
Checklist
See Running tests for more details
.cabal
files are updatedhlint
. See.github/workflows/check-hlint.yml
to get thehlint
versionstylish-haskell
. See.github/workflows/stylish-haskell.yml
to get thestylish-haskell
versionghc-8.10.7
andghc-9.2.7