Closed Divesh-Otwani closed 3 years ago
I don't agree with all of this.
Internal
one way or another (it doesn't need to be the final item in the module path, but it needs to be there) this does signal: this is not public interface, it exists purely for internal, technical reason, use at your own risk: it won't be considered in backwards compatibility. It's a convention that we can't do without in current Haskell.We remove the internal
Data.Functor
andControl.Monad
modules, establishData.Functorial.Linear
andControl.Functorial.Linear
in which we re-export everything and make internal modules according to the system I've described above.
Data.Functor
is nice: everything in there are functors. That's what I want this module to be named. But Control.Monad
should become Control.Functor
. If you need an internal name for “just the Functor
type class” , you can use Data.Functor.Internal.Functor
. A bit redundant, but very regular.
We make
Data.SymmetricMonoidal.Linear
Likewise, it's Symmetric Monoidal products are in the right place in Data.Bifunctor.Linear
, I don't know why we would want to change it. (again, making a dedicated internal module for it is ok)
We do as we did with functors to
Data.Profunctor.*
I don't know what this means.
We make similar changes wherever else there are internal modules or ought to be because there are too many ideas in a file that aren't unified under a clear purpose or concept to the module.
I don't know what this means either.
I don't agree with all of this.
Correct me if I'm wrong, but I think this means what you don't explicitly disagree with you are neutral or in support of.
Internal module need to be named
Internal
one way or another (it doesn't need to be the final item in the module path, but it needs to be there) this does signal: this is not public interface, it exists purely for internal, technical reason, use at your own risk: it won't be considered in backwards compatibility. It's a convention that we can't do without in current Haskell.
I wasn't advocating for removing the Internal
from the path of the name; just that naming the file Internal.hs
communicates nothing unless we have a convention of what these Internal.hs
files mean and what they stand for. And my main point is "internal modules should have clear boundaries" usually because they are centered around a concept.
It may be worth it making smaller internal modules, though, since it would increase build parallelism, and conceivably makes the imports more predictable.
I would emphasize that it's more than predictable: it makes the use of these readable, sustainable as the project grows, easier to read, etc.
I think that you are advocating for moving all the code into such internal modules and only use public module to re-export code from such internal modules: why not. I rather like the idea even.
That might be a consequence of what I"m saying but what I want is the opposite of my three complaints: non-arbitrariness, cohesion and consistency.
Data.Functor
is nice: everything in there are functors. That's what I want this module to be named. ButControl.Monad
should becomeControl.Functor
. If you need an internal name for “just theFunctor
type class” , you can useData.Functor.Internal.Functor
. A bit redundant, but very regular.
I don't think a new user will find it intuitive to see Applicative Functors in Data.Functor
. If we are going to depart from the way base
makes things in a lot of self-contained modules then we can't use the same names. We should call it Data.Functorial.Linear
or something. Otherwise this will just be another strange thing a new user has to learn to use linear-base
and I'd like as little of that as possible. More fundamentally, linear-base
is not about making corrections to things we don't like in base
-- we should do as little as possible of that and focus as much as possible on the linear
part.
Likewise, it's Symmetric Monoidal products are in the right place in
Data.Bifunctor.Linear
, I don't know why we would want to change it. (again, making a dedicated internal module for it is ok)
Exactly the point I'm making about Data.Functor
applies here. It just makes it harder for a new user who can't read the minds of the writers of linear-base
.
We do as we did with functors to `Data.Profunctor.*`
I don't know what this means.
This means we put Data.Profunctor.{Profunctor, Wandering, Exchange}
each in it's own internal file and have a Data.Kleisli.Linear
. However, as I'm writing this I think I'll defer this to when I take a look at optics.
We make similar changes wherever else there are internal modules or ought to be because there are too many ideas in a file that aren't unified under a clear purpose or concept to the module.
I don't know what this means either.
If there isn't a clear boundary or concept that defines an internal module then it should be broken up into more internal modules each of which have clear boundaries and are ideally centered around a concept.
I don't think a new user will find it intuitive to see Applicative Functors in
Data.Functor
. If we are going to depart from the waybase
makes things in a lot of self-contained modules then we can't use the same names. We should call itData.Functorial.Linear
or something. Otherwise this will just be another strange thing a new user has to learn to uselinear-base
and I'd like as little of that as possible.
This discussion is not going to be resolved by arguing. So let me just make this a decision: Functor
stays.
More fundamentally,
linear-base
is not about making corrections to things we don't like inbase
-- we should do as little as possible of that and focus as much as possible on thelinear
part.
This is not true. There are plenty of corrections, in linear-base, to things in base that I believe haven't stood the test of time. Not everything, one has to choose their combat. But it's not the goal to make a strict base look-alike with different arrows.
Okay, so the things that remain are minimal enough that I think this issue can be closed. The module organization that I think could be made just a bit cleaner is:
id
"Profunctor
s could be made cleaner, less entangled with lenses and subdivided (but this makes more sense to tackle when we flush out lenses more)Polarized
arrays should perhaps have just 4 modules: Polarized
: Internal.Polarized
, Internal.Pull
, and Internal.Push
I'll make this another issue later.Suffice it to say, all the big things are taken care of.
Also, as an "Author's note": some of the wording above is a bit too emotional and not as grounded it could have been. Let's just say 2020 was a rough year.
The internal module structure has some major (not completely disjoint) problems:
flip
defined inPrelude.Linear
but not inPrelude.Linear.Internal
? What is the rule by which we determine what goes in an internal module and what goes together?(<$)
inData.Functor.Linear
?ReaderT
inControl.Monad.Linear
but not in the internal modules?Consumable
andDupable
andMovable
inData.Unrestricted
? With the exception of the last, these seem like separate concepts.Internal
doesn't communicate what is internal. I should have some idea of what ought to be in this module and what ought not to be -- this way, if I see it imported somewhere I know basically what is imported. This ideally comes from each module having a clearly defined and self contained purpose, usually about a singular concept.Data.Unrestricted.Linear
and see the importData.Functor.Linear.Internal
I have no idea what this is importing. Is it importing all ofData.Functor.Linear
essentially? It seems the purpose is to avoid a cycle of module dependencies but this still tells me very little. It seems like the original programmer has a clear idea of what that module is and what it does but as a new reader, I do not. This is a problem: there shouldn't be this silent knowledge or "internal wisdom" that makes it hard to do things unless you know all this background that isn't explicitly written down. [Cf., normalization of deviance.] If stuff like this grows, this becomes unsustainable.Control.Monad.Linear.Internal
I have no idea what they are really importing. I don't know the boundaries of what is and is not in this module, nor do I understand it's purpose.Internal
which just holds anything and everything core that requires no internal dependencies and other parts likeStreaming
have internal modules likeStreaming.Type
this is inconsistent.I propose the following system and subsequent changes:
Design.md
document.Data.Unrestricted.Consumable
because it isolates a single self-contained concept, and if the consumable utilities grow, we don't want them to get messy. We also avoid a few module dependency issues this way.Data.Functor
andControl.Monad
modules, establishData.Functorial.Linear
andControl.Functorial.Linear
in which we re-export everything and make internal modules according to the system I've described above.Data.Unrestricted.Linear
Data.SymmetricMonoidal.Linear
Data.Profunctor.*
I recognize this system is abstract but hopefully there are enough examples to give a clear enough idea of my proposed solution.