Skip to content
This repository has been archived by the owner on Aug 2, 2020. It is now read-only.

move a bunch of types into dedicated modules #502

Merged
merged 3 commits into from
Feb 19, 2018

Conversation

alpmestan
Copy link
Collaborator

@alpmestan alpmestan commented Feb 15, 2018

Alright, it's show time.

This is the first PR extracted out of #445, hopefully addressing the feedback you (@snowleopard) gave in comments around here. Let me know what you think.


import Hadrian.Package.Type
import Stage.Type
import Way
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably, this can be relaxed to import Way.Type?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in an upcoming commit.

@@ -0,0 +1,17 @@
module Expression.Type where

import Builder
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I think Builder.Type is sufficient.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, ignore the above comment, apparently there is no Builder.Type. Or does it make sense to add it as well, for consistency?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at Stage below, I think we should only add .Type modules if they help resolve a cyclic import. So, let's not add Builder.Type if it's not necessary.

Copy link
Collaborator Author

@alpmestan alpmestan Feb 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed I didn't add .Type modules that were not in @angerman's PR under src/Types/, and the ones that were there presumably were there to resolve cyclic imports, so I think the PR effectively implements what you converged on in your last comment, doesn't it?

EDIT: looking at the comments below, looks like it doesn't just yet =)

@@ -0,0 +1,26 @@
module Hadrian.Builder.Type where
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit unhappy about this module: do we really need to separate the definition of builder modes and builder arguments? This looks very awkward.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's the only way forward, perhaps we can at least give this module a bit clearer name: Hadrian.Builder.Mode.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in an upcoming commit.

@@ -0,0 +1,27 @@
module Stage.Type where
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module doesn't seem to buy us anything. Is it here only for consistency? I think I'd prefer to keep everything simply in Stage, as otherwise the latter looks really silly with a single stageString function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got rid of this module and put back everything into src/Stage.hs, in an upcoming commit.

@@ -0,0 +1,45 @@
module Hadrian.Package.Type where
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a quick look at Hadrian.Package, it doesn't seem that this split is necessary. Or is it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will be necessary shortly (as in, in a PR or two), and given that we're not really in a comparable situation as with the Stage/Stage.Type case (the .Package module has several functions and the package-related types are likely going to be needed by "base" modules), I would personally suggest that we leave that one as-is, but it's your call of course :)

import Data.List
import Data.Maybe
import Development.Shake.Classes
import Hadrian.Utilities
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, it seems that Hadrian.Utilities is the only potential source of cyclic imports. But does it really cause a cyclic import? If not, perhaps we don't need this split?

@snowleopard
Copy link
Owner

@alpmestan Many thanks! I've left a few comments above. Most of the comments arise because it's unclear which .Type submodules are necessary and why.

I would argue that we need to introduce a .Type submodule only if it helps to break a cyclic import. But I don't see how Stage.Type (for example) can possibly break a cycle. It only seems to complicate the code base and slows down Hadrian's build.

@snowleopard
Copy link
Owner

snowleopard commented Feb 15, 2018

Perhaps, some cyclic imports only become obvious in the reloc branch. I suggest to add a comment to every .Type module, clarifying which particular cyclic import it is meant to resolve, perhaps attaching it exactly to the offending imports. In this way, if these imports are removed later, we would realise this and merge the modules back, simplifying the code base.

@@ -12,25 +12,13 @@ module Context (
pkgConfFile, objectPath
) where

import GHC.Generics
import Context.Type
Copy link
Owner

@snowleopard snowleopard Feb 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an example of the kind of comment I'm looking for:

-- The module "Context.Type" is used to break import cycles caused by "Oracles.Setting".
import Context.Type

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt those comments will be of much use. They will document what the initial cycle that was broken was, but will ultimately bitrot pretty quickly. Any further refactoring or addition of code to hadrian that uses the .Type modules will not see that there could have been a cycle without the .Type module and subsequently not update the comment.

As such I believe the comment while it might be initially correct will be ultimately incorrect.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree, these comments will likely get out-of-date quickly, and we probably don't want to keep changing the structure very often.

Instead it may be more useful to add a general comment somewhere in the overview document that explains the module structure. (We don't have such a document at the moment, so we don't need to do it now.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we write such a description once we have integrated all of #445 as things will be in flux until we're done with that.

@angerman
Copy link
Collaborator

While I would have preferred to completely separate Data Types and logic in the reloc branch, I opted to only separate the ultimately necessary data types out. I don't think it would be a bad idea to follow this practice through though, as that will ultimately prevent some cycles from occuring in the first place. The occurance of cycles and GHCs inability to deal with them in the first place is rather sad though; however, as that is the current state of affairs, I believe that it's a good practice to separate data types from functions.

As this PR tries to provide a rather self contained refactoring, without pulling in the changes that forced the cycle breaking, I wonder if we can try to review this from the point of view that this PR provides a better extension point to hadrian, that reduces the risk of cycles?

@snowleopard
Copy link
Owner

I wonder if we can try to review this from the point of view that this PR provides a better extension point to hadrian, that reduces the risk of cycles?

@angerman Right, at the moment I'm bothered by two specific decisions:

  • On the one hand, the current solution seems to be going too far with respect to the Stage module. It is highly unlikely that Stage will ever cause import cycles. I would consider it risk-free and leave it as is.

  • On the other hand, the current solution doesn't go far enough with respect to the Builder module, which is potentially much more likely to cause import cycles due to importing Oracles.*. Why was it left out? It actually has a lot more functions compared to Stage.

@snowleopard
Copy link
Owner

snowleopard commented Feb 15, 2018

In general I am fine with splitting all modules that we identify as High Risk for Import Cycles. These seem to be: Context, Builder, Expression, Way (I think this will allow us to safely move Oracles.Settings.libsuf to Way), because they are interlinked with various oracles.

These modules on the other hand are Low Risk: Stage, Target, Flavour.

Does this sound reasonable?

@alpmestan
Copy link
Collaborator Author

OK, I'll tweak the PR accordingly next week :)

@snowleopard
Copy link
Owner

@alpmestan Thanks! I started to worry I scared you away with my avalanche of comments :-)

@alpmestan
Copy link
Collaborator Author

No no, absolutely not. It's just that I have to interleave the hadrian work with some other tasks :) My work on this will resume on monday.

@alpmestan
Copy link
Collaborator Author

On the other hand, the current solution doesn't go far enough with respect to the Builder module, which is potentially much more likely to cause import cycles due to importing Oracles.*. Why was it left out? It actually has a lot more functions compared to Stage.

It looks to me like one of the instances for Builder (the H.Builder one) is dependent on a lot of things, so if we were to split Builder into Builder/Builder.Type, we might end up having to make the H.Builder one orphan, otherwise we'd basically be pulling everything into the .Type module which would defeat the point. Unless I'm overlooking something?

@snowleopard I just pushed a commit addressing most of your feedback, let me know if that works for you or if you see other things that should be changed. :)

@snowleopard
Copy link
Owner

snowleopard commented Feb 19, 2018

@alpmestan Thanks! One last question: You renamed Hadrian.Builder.Type to Hadrian.Builder.Mode, which is better, but I'm still not sure if this module is really necessary. I don't see how moving simple mode definitions like data ArMode = Pack | Unpack deriving (Eq, Generic, Show) to a separate module allows to break import cycles. Can't we define builder modes together with builder arguments?

If Hadrian.Builder.Mode is necessary for subsequent PRs I'm happy to merge this as is.

@alpmestan
Copy link
Collaborator Author

alpmestan commented Feb 19, 2018

@snowleopard Got rid of Hadrian.Builder.Mode for now. I'll reintroduce it when it becomes strictly necessary. I'd rather get this PR merged and move on to the more juicy (but difficult) bits of #445 than spend more time on those few data types =)

@snowleopard snowleopard merged commit cc8f62c into snowleopard:master Feb 19, 2018
@snowleopard
Copy link
Owner

@alpmestan Thanks =) Merged now! Looking forward to more juicy PRs :-)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants