Skip to content
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

Defined instances #1

Closed
garyb opened this issue Sep 4, 2014 · 11 comments
Closed

Defined instances #1

garyb opened this issue Sep 4, 2014 · 11 comments

Comments

@garyb
Copy link
Member

garyb commented Sep 4, 2014

Do we think the instances for Monoid and Contravariant should be defined in here, or would it be better to require Const in those packages and provide instances there instead?

I'm asking as if we move Identity out into its own package we'll have the same quandary there too. It seems to me like we'll end up having less problems later if Const and Identity have no dependencies but rather are required in a lot of places. It might end up similar to the situation with purecript-control where almost everything will end up requiring them transitively, but they're fairly fundamental things anyway - in fact they should never need to change again unless we alter the classes in Prelude.

Thoughts? (@paf31, @joneshf)

@garyb garyb added the question label Sep 4, 2014
@paf31
Copy link
Contributor

paf31 commented Sep 4, 2014

I see Identity and Const as being roughly equivalent to things like Maybe. I would say we should do the same thing as for Maybe, but there is a problem there, since Maybe depends on control but monoids depends on Maybe, for example. My preference would probably be to have the new packages depend on things like control and monoids (and have data structures depend on control structures, generally).

@garyb
Copy link
Member Author

garyb commented Sep 4, 2014

Hm, I figured having control depend on data made more sense as data is "simpler" and also I would expect there to be fewer data structures than control structures.

I don't know, it just seems to me more like bifunctors, for example, should bring either, const and tuple with it, than either bringing bifunctors, and by extension foldable-traversable, control and monoid.

Either way you're going to end up depending on stuff you don't care about, but it seems that data dragging control with it will pull more stuff than the reverse, as dependencies for control modules tend to be more complex than dependencies for data.

Thinking about it, it's not really even control vs data, more a case of modules for data declarations vs modules defining typeclasses, as I'm thinking of Monoid as control-like in this, but it's not.

@garyb
Copy link
Member Author

garyb commented Sep 4, 2014

Another thing is, control stuff sometimes depends on data (like transformers needs Identity), whereas the reverse seems less likely.

@garyb
Copy link
Member Author

garyb commented Sep 4, 2014

Although it would be weird if control included pretty much all the data structures, which would happen if we went with my suggestion... augh, I don't know.

@paf31
Copy link
Contributor

paf31 commented Sep 4, 2014

:) Yeah it's a bit of a pain. I guess I was thinking that the number of data structures for a given control type is pretty much unbounded, whereas the number of new control structures tends to grow quite slowly. I was thinking it would be unfortunate to pull in control and have to compile 20 new data structures, most of which I wouldn't need, especially in the psc-make case where we don't have dead code elimination.

Identity is obviously the worst offender here since the transformers dependency forces our hand. I almost feel like Identity for transformers should be a newtype defined in transformers.

@joneshf
Copy link
Member

joneshf commented Sep 4, 2014

Hmm, where is the main pain here? Is it with bower? Is it with psc*? Is it possible to have a cycle in module dependencies without it being an issue? Is it that you'll end up importing the whole world if you use transformers or parsers?

If it's bower, we can use something else.
If it's psc*, can we fix that?
If it's just unsound to have a cyclical module graph, I guess we actually have to do something about it :).
If it's the last point, that seems like the least dangerous concern.

From the outside looking in, it seems a bit off that mutually recursive modules cause an issue in a language like haskell or this, though you have a hs-boot files for that (do they still exist/work?). I'm not suggesting something like hs-boot files though.

@garyb
Copy link
Member Author

garyb commented Sep 4, 2014

It's not a problem yet, was just thinking about how best to deal with the tangle of relationships we currently have and if there's a better way to deal with it in the future, and yes to minimise the effect of some modules importing the whole world.

Bower is actually fine with circular dependencies, but it does cause some problems for psc* as you can end up with a copy of the current package in bower_components - originally I "fixed" this by adding a step in the grunt build that would delete the duplicate version of the current package from bower_components, but it was pretty hacky so that's why we ended up with core-tests - quickcheck and arb-instances import almost everything, so having tests in the core projects causes all kinds of trouble. That's not really relevant to this, but just an illustration of the kind of thing that can happen.

@joneshf
Copy link
Member

joneshf commented Sep 6, 2014

I see. So, it has more to do with the fact we're all using file globs than anything else? I mean, if we go out and list all the dependencies to psc* explicitly, is it still a problem? Does the circular dependency cause an issue there, or can psc* handle that?

@michaelficarra
Copy link

It seems there's no compelling reason to choose either strategy, so I would prefer to define the instances alongside the datatypes themselves. That way, for example, everything related to Maybe is in one place.

@paf31
Copy link
Contributor

paf31 commented Oct 13, 2014

On the module level, there is no unsoundness, but I don't like having package-level cycles anyway. It just demonstrates that we don't understand the relationships between the various packages.

@joneshf
Copy link
Member

joneshf commented Oct 13, 2014

I think one thing that might be conflating the issue is this idea of Control vs Data. At least, I don't understand the difference. Nor do I understand what is "control" and what is "data". It feels like a more useful separation would be between type classes and data types.

I think I'm in agreement with @michaelficarra. but it still seems like it's possible to get in a cycle even with this convention (unless we break it). Take purescript-generics for example, the typeclass Generic defines unTerm :: Tm -> Maybe a. This obviously has to depend on purescript-maybe for the data type, and Maybe can be made an instance of this type class, yet we can't have purescript-maybe depend on purescript-generics as well. And since it doesn't seem like we can implement this in a function outside of the typeclass, it doesn't seem like it can be refactored somewhere else. Unless I totally don't understand it.

A simple example, sure, but generics should be a core module, along with typeable. We don't have the compiler support to make it really shine, but they're fairly fundamental to using some of the greater features that should be possible in this language. Also, what about other type classes that might give back an [], or a Tuple, or an Either. It seems that certain data types are fundamental to our ecosystem: optional values, sum, product, non-determinism, etc.

Should there be some Prim types defined in the compiler that then have isomorphisms to the actual types used in user facing libraries? Oris this too complex? Maybe some other formulation?

@garyb garyb closed this as completed Nov 1, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants