-
Notifications
You must be signed in to change notification settings - Fork 40
Add deriving via Generically/Generically1 #316
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
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 r because
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 |
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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 <[email protected]>
# 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
Par1
exactly once. My approach has some runtime overhead, maybe there's a better way?