Add deriving via Generically/Generically1#316
Add deriving via Generically/Generically1#316sjoerdvisscher wants to merge 21 commits intomasterfrom
Conversation
|
I'm not really very comfortable with this, until Also, one quick question: why does |
|
Using deriving via is actually a really good idea, I don't like the default signatures way. Control.Functor needs a separate class because I need to prove that the mapping function is used only once. This doesn't work f.e. for Control.Functor: instance (Functor f, Functor g) => Functor (f :*: g) where
fmap f (l :*: r) = fmap f l :*: fmap f rbecause
instance GFunctor One Zero Par1 where
gmap (One f) (Par1 a) = (Par1 (f a), Zero)And for the product instance you see the threading happening: instance (GFunctor i m l, GFunctor m o r) => GFunctor i o (l :*: r) where
gmap f (l :*: r) = gmap @i @m f l & \case
(l1, f') -> gmap @m @o f' r & \case
(r1, f'') -> (l1 :*: r1, f'') |
|
Makes sense. I was trying to remember why I've always needed a separate class for the generic thing. I haven't made a generic deriver for quite a while. And maybe it actually was for this very reason of needing extra parameters. |
|
You also need a separate class for classes of kind |
aspiwack
left a comment
There was a problem hiding this comment.
I approve of the new implementation strategy for deriving control functors. But it needs a little documentation for when we come back there later 🙂
Aside from that (and the comments below), may I ask of you to add an example file in the examples/ directory. Probably with a couple of tests (we have a dedicated test suite for the examples).
| import Prelude.Linear.Internal | ||
| import Data.V.Linear () | ||
|
|
||
| newtype Generically a = Generically a |
There was a problem hiding this comment.
Please add some Haddock to explain why one would use Generically, and why it is presently unsafe. And when it is safe to use.
| eitherNoPar1Map f (l :*: r) = unused l :*: fmap f r | ||
| instance (Unused r, Functor l, NoPar1 l ~ 'False) => EitherNoPar1 'False 'True l r where | ||
| eitherNoPar1Map f (l :*: r) = fmap f l :*: unused r | ||
| type MessageMany = 'Text "Can't derive an instance of Functor. One of the constructors of your datatype uses the type parameter more than once." |
There was a problem hiding this comment.
This is fine for the first brush. But I think that we can improve on this error message by carrying the name and type of the offending constructor and adding it to the error message.
There was a problem hiding this comment.
@aspiwack, yes, we'll want to add a GFunctor class that takes the name of the type as well as the constructor.
Co-authored-by: Arnaud Spiwack <arnaud.spiwack@tweag.io>
# Conflicts: # src/Data/Unrestricted/Internal/Instances.hs
|
Thanks @treeowl for raising this again I remember that there was an issue with this Pull Request which prevented us from moving forward. But it seems that we failed to document the problem here. @sjoerdvisscher do you remember what happened? |
|
The problem is that the generated code by deriving |
I wasn't even thinking about that aspect. Where does |
Yes that's what I meant, the generated instances. I still don't know what the best way is to do deriving of linear generics. I was hoping to do it with some type level hackery and maybe a minimal amount of TH based on the generated instances of regular generics, but I couldn't really figure that one out. Doing everything in TH seems a daunting task, maybe there's a library that we can hook into? Or else baking it in into GHC might be the easiest approach? |
|
|
True, but this is partly because there are stock derivers for |
|
Where are the linear |
|
Could you possibly rebase this? I can't figure out which pieces are supposed to go where. |
|
I was thinking to rework this whole thing to use |
|
@treeowl No please, go ahead, I'm done now. I was just trying to get this in the shape that's hopefully the most useful to you. |
|
Great, as long as we're on the same page. |
|
@treeowl note that now that you have write access you can work directly on this branch, if it's convenient to you. |
|
Sounds good. I'll let y'all know when I'm ready for review. I expect I'll need other people to tell me what to name everything and where to put it, since I don't know your conventions, but that can happen later. |
* Switch to `linear-generics`. * Make `Unused (f :.: g)` handle the case that `g` is `Unused` but `f` is not. * Avoid `error` when facing unsatisfiable constraints.
The contents of `Unsafe.Linear` were moved to `Unsafe.Linear.Internal`, presumably to avoid cyclical imports with `Generically` stuff. `Generically` is no longer unsafe, so we don't need that, at least for now. We can get it back if we want/need, but it doesn't belong in this PR and it makes the diff hard for me to use.
* Put the 'Generically' and 'Generically1' types in a module, `Prelude.Linear.Generically`, at the very base of the module hierarchy. This seems to help avoid the module cycle problems that have been worked around various ways. * Move the generic-deriving machinery for each class into the module that defines that class. * Start unwinding orphan instance spaghetti. I'm optimistic this will work, but I know it's not a sure thing.
* Many more orphans removed. * Many more instances added. * Added generic deriving for data `Applicative`s.
* Improve generic `Traversable`. It works well for product types and sufficiently simple sum types. For nontrivial sum types the GHC optimizer makes hash of it in 9.0; I don't have 9.2 yet, so I don't know if that's changed any. * Add `Yoneda` and `Coyoneda` for both data functors and control functors, and `Curried` for control functors. `Yoneda` and `Coyoneda` especially tend to come in handy.
03e1f0b to
4415181
Compare
|
I finally felt well enough today to start the rebasing process. No major roadblocks yet. What |
|
@aspiwack I'd really like to merge Another question: do we want |
|
Progress continues. I may not need to merge the One frustrating thing: generic deriving of |
I've never payed much attention I suggest that we don't care about it for the sake of this PR. And if you want to make linear-base safe-haskell compatible, we discuss how in a second PR.
I honestly don't care either way. If it's useful to your PR, feel free. If it's orthogonal, I prefer we do that in a separate step.
Is it worth splitting this to another PR? (I feel my remarks, so far, have been having a theme 🙂; at the end of the day, do whatever is more expedient).
I'd say yes. It's an oversight that we haven't (but, you guessed it: it should be a different PR)
I suggest that we keep the instance for |
| newtype Generically a = Generically a | ||
|
|
There was a problem hiding this comment.
Generically and Generically1 have been added to base-4.17. I don't know whether this is relevant for you, but I thought I'd mention it just in case.
There was a problem hiding this comment.
It's definitely relevant. I'm not sure how we will handle this, but when 9.4 is out, we will strive to make these newtypes re-exports from base.
I think the Traversable instances nicely show the difference between traversing with linear control Applicatives and linear data Applicatives. (I'm planning to write a blogpost about this part.)
The linear control Functor generics are a bit more complicated, since you need to prove that the mapping function is used exactly once, which means having
Par1exactly once. My approach has some runtime overhead, maybe there's a better way?