AMP (Applicative/Monad proposal)
Straight adaption of the original proposal.
Rendered version of this proposal: https://github.com/quchen/rfcs/blob/amp/texts/0000-applicative-monad-superclass.rst
The redundant functions (like return) are all well and good in an implementation such as GHC, but I think they should be struck from the Report. Let's design it like we should have from the beginning, cleanly. GHC could then include its non-conformance with this new standard as an infelicity. Perhaps GHC can even deprecate return, but I don't have a problem with just living with the infelicity.
I agree, @goldfirere.
The Monad of no Return proposal was met with too much resistance (thanks to @hvr anyway!), so I think it’s off the table for now. For the next language standard however, I completely agree with @goldfirere: we should remove return from the standard, and GHC is free to keep it as a GHC-ism.
One of the main points why I think we should even have a new standard, despite GHC being the only widely used compiler, is that we want to encourage new best practices for all users. Having redundant functions certainly does not help that cause. If implementations decide that it is useful to deviate from the standard for technical reasons (compatibility with old libraries) then that’s fine with me, but we would give a strong signal of the future direction of that almost-keyword. I suspect GHC will follow after a few releases, and even before that people on the IRC and on Reddit will tell others to avoid return.
@strake MonadPlus is unfortunately not redundant: it adds laws that Alternative does not have.
Speaking of laws, maybe we should remove the right zero law of MonadPlus, but that’s something for a different discussion.
On 19/08/2016, David Luposchainsky [email protected] wrote:
@strake MonadPlus is unfortunately not redundant: it adds laws that Alternative does not have.
Ah, i see. But the methods are obsolete, yes? We could have class
MonadPlus where empty >>= f = empty.
Yes, that looks correct, @strake.
This is probably a minority view, but I'm not in favour of requiring users to implement Applicative in order to have a valid Monad instance. I understand the maths, but it makes teaching monads more convoluted than it needs to be.
Forgive me for riffing off in a strange direction, but I think we can have our cake and eat it too. Imagine we had this:
instance Functor m where
fmap :: (a -> b) -> m a -> m b
default fmap :: Monad m => (a -> b) -> m a -> m b
fmap = liftM
instance Functor m => Applicative m where
pure :: a -> m a
default pure :: Monad m => a -> m a
pure = return
(<*>) :: m (a -> b) -> m a -> m b
default (<*>) :: Monad m => m (a -> b) -> m a -> m b
(<*>) = ap
Now suppose we have a fully-defined instance Monad M. Users need only write
instance Functor M
instance Applicative M
to get what they want. If only we had a -XAutoDeriveSuperclasses, then these lines would come for free with instance Monad M and we'd all go home early.
I know that this isn't the place to suggest such drastic measures, but we're so frustratingly close to being able to make the problem go away, I couldn't resist.
@zenzike I sympathise with your concern, but I believe that not requiring the Applicative instance does not solve the problem satisfactorily for everyone. In most (though admittedly not all) scenarios in which monads are introduced, one would also want to talk about applicative functors, either before or not long after discussing monads. In such cases, temporarily pretending Applicative didn't exist for the purpose of introducing monads would only delay the problem. (From your perspective, @goldfirere 's idea above might be an improvement, though it still would be necessary to make an aside on why it is necessary to add two instance declarations with no implementations, or to turn on a compiler extension.)
@goldfirere I had already considered the idea of default instances for Functor and Applicative, but I hadn't thought about having an -XAutoDeriveSuperclasses as you suggest. I think this would alleviate my concerns if it were on by default (though the exact semantics of that extension don't seem trivial at first glance, and it's not clear that this is desirable). I don't think that this is a strange direction at all: we need to nail this if we'd like to keep the language accessible.
I don't think Haskell2020 necessarily has to include -XAutoDeriveSuperclasses together with AMP. For teaching purposes, would it not be enough for GHC to support the former feature?
Good point @blamario . I wouldn't feel at all comfortable recommending discussion on -XAutoDeriveSuperclasses here (although I do think it could be a well-specified feature -- but it would need time to mature).
On the other hand, my comment above also requires -XDefaultSignatures, which I do think could be ready for standardization.
@goldfirere having default signatures for fmap/return/<*> would be in conflict with all the
-Wnoncanonical-monad-instances
-Wnoncanonical-monadfail-instances
-Wnoncanonical-monoid-instances
compat warnings I put in place which aim at helping to detect and fix/change code to a situation where the implementations flow in a natural direction from superclasses to their subclasses via ordinary H2010 default implementations, and this is deliberately ruling out -XDefaultSignatures based migration paths.
As having e.g. both return = pure (in place since GHC 7.10) and pure = return (which would not be possible with plain H2010) can result in annoyingly to debug cyclic definitions (or similarly for <>/mappend or *>/>> etc), and one of the reasons I state for moving return (or >> or mappend or ...) out of the Monad (or Monoid) class to make such an undesired state unrepresentable in the first place.
That being said, we should have something like -XAutoDeriveSuperclasses but I consider it a bad design to base its support for cases like AM(F)P or Monoid/MonadFail/Semigroup/... via -XDefaultSignatures which reach for subclasses' implementations.
Refactoring type-classes is something that happens when libraries evolve and unfortunately something that Haskell doesn't yet provide a good built-in compatibility/migration story for, but I think there are other better designs possible for implicitly defining superclass instances when specifying e.g. Monad/Monoid/MonadFail/... which would not require us to keep Monad.return/Monad.(>>)/Monoid.mappend/Monad.fail/... as a class methods around as well as rule out cyclic definitions between two redundant methods.
Specifically, since I consider it error-prone to have -XDefaultSignatures refer to subclasses which is most often just a way of trading safety for convenience (and I'd go as far as making this a compile warning as I have been burned by this already a few times), I'd rather prefer a design where the subclass controls how to implement its superclasses, which would IMO allow to avoid the concerns I point out.
I can even think of schemes tailored to these simpler use-cases which would allow you to write H2010 Monad definitions (for simple cases at least), and conveniently end up with a return/>>-free AMP-compliant instance hierarchy (assuming you don't have orphan instances lurking somewhere for Functor/Applicative/(MonadFail) which would then clash of course -- however, use-sites would still need to be upgraded to use e.g. MonadFail and there's problematic cases for when a data-type has nested inner generic Monad -- think transformers, but the -XDefaultSignatures-based auto-instancing suffers from the same limitation anyway).
The canary RFC https://github.com/haskell/rfcs/pull/17 is on its way to become the first accepted and merged proposal. Once it flies through the complete process, I think this one could be next. Would anybody like to volunteer to add the actual Report modifications to this PR?
My opinion is that MonadFail and MonadOfNoReturn should be left as a separate proposal, if only for the sake of easier task breakdown.
To be honest, I'm having a hard time discussing AMP isolated from MRP as for me they're inseparable.
I know, but we shouldn't let the perfect be the enemy of the good. I think we can agree that bringing the report up to sync with GHC 8.0 would be a good thing (i.e., an improvement upon its current state), even if it leaves the pure vs. return issue in an ugly state.
Another argument in favour of baby steps is that MonadOfNoReturn is likely to present way more work overall. Not only is it more controversial, but the report text contains many occurrences of return that must be sifted through. If the proposals are combined, they are less likely to be done.
What’s the challenge to having return = pure and leaving that report language as is? Just the proof reading ?
On Mon, Nov 19, 2018 at 9:11 AM Mario [email protected] wrote:
I know, but we shouldn't let the perfect be the enemy of the good. I think we can agree that bringing the report up to sync with GHC 8.0 would be a good thing (i.e., an improvement upon its current state), even if it leaves the pure vs. return issue in an ugly state.
Another argument in favour of baby steps is that MonadOfNoReturn is likely to present way more work overall. Not only is it more controversial, but the report text contains many occurrences of return that must be sifted through. If the proposals are combined, they are less likely to be done.
— You are receiving this because you commented.
Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/1#issuecomment-439905165, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAQwrWMs31zH-7MfvKb3EWMF-JuOmd2ks5uwrv3gaJpZM4JS1Lq .
I think not doing MRP as part of AMP genuinely risks making an H2020 update out of date by construction, and that would be a bummer ..
as is, we should invest some effort into getting JOIN back into the monad type class in ghc to reconcile that bit too
On Mon, Nov 19, 2018 at 9:14 AM Carter Schonwald [email protected] wrote:
What’s the challenge to having return = pure and leaving that report language as is? Just the proof reading ?
On Mon, Nov 19, 2018 at 9:11 AM Mario [email protected] wrote:
I know, but we shouldn't let the perfect be the enemy of the good. I think we can agree that bringing the report up to sync with GHC 8.0 would be a good thing (i.e., an improvement upon its current state), even if it leaves the pure vs. return issue in an ugly state.
Another argument in favour of baby steps is that MonadOfNoReturn is likely to present way more work overall. Not only is it more controversial, but the report text contains many occurrences of return that must be sifted through. If the proposals are combined, they are less likely to be done.
— You are receiving this because you commented.
Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/1#issuecomment-439905165, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAQwrWMs31zH-7MfvKb3EWMF-JuOmd2ks5uwrv3gaJpZM4JS1Lq .
Hi,
On 11/19/2018 08:24 PM, Carter Tazio Schonwald wrote:
I think not doing MRP as part of AMP genuinely risks making an H2020 update out of date by construction, and that would be a bummer ..
Many would disagree with this. MRP remains very controversial.
And a design with AMP but without MRP is not at all unreasonable. Not just for backwards compatibility (in a broad sense, not just code), but also looking forward as it (in principle) would allow for providing default instances of Functor and Applicative in terms of a monad instance. Which would be a jolly good thing in many ways, and is an application of extensions proposed for other reasons. After all, that would just amount to a generalisation of the existing possibility of providing default methods and giving programmers choice of which ones to provide, for example ...
as is, we should invest some effort into getting JOIN back into the monad type class in ghc to reconcile that bit too
... whether to define a monad through bind or join (which I definitely think should be possible).
In any case, without getting into a deep discussion about MRP at this point, there are definitely good reasons to discuss AMP and MRP separately, and there is no need suggest that the entire H2020 would be doomed unless MRP is adopted as part of AMP: regardless of what one think about AMP and MRP, there is a lot more to H2020.
Best,
/Henrik
This message and any attachment are intended solely for the addressee and may contain confidential information. If you have received this message in error, please contact the sender and delete the email and attachment.
Any views or opinions expressed by the author of this email do not necessarily reflect the views of the University of Nottingham. Email communications with the University of Nottingham may be monitored where permitted by law.
What’s the challenge to having return = pure and leaving that report language as is? Just the proof reading ?
That would certainly be the easiest way to do it, but there should at least be some justification offered for having two names for the same thing. The report also contains sentences like
The type of
returnisMonad m => a -> m a
If we're not actually restricting the type of return, this piece of text has to change. AMP alone doesn't need to answer these questions. At most, it needs to state that return a == pure a is a law.
theres a simple reason: lots of code uses that name, and no code will fail to compile if return suddenly has Applicative f => a -> f a
as far as I'm concerned, theres zero controversy on this topic, all those second editions of haskell teaching material worked out well for their authors.
plus applicative do is a pretty sweet extension and we shouldnt choose a semantics for return that precludes a haskell 2020 implementation that isn't ghc from having that fancy feature :)
On Mon, Nov 19, 2018 at 8:39 PM Mario [email protected] wrote:
What’s the challenge to having return = pure and leaving that report language as is? Just the proof reading ?
That would certainly be the easiest way to do it, but there should at least be some justification offered for having two names for the same thing. The report also contains sentences like
The type of return is Monad m => a -> m a
If we're not actually restricting the type of return, this piece of text has to change. AMP alone doesn't need to answer these questions. At most, it needs to state that return a == pure a is a law.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/1#issuecomment-440105257, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAQwu94wFYERz9kbdroNFqUI8_PEW_3ks5uw11AgaJpZM4JS1Lq .
Hi,
On 11/20/2018 05:23 AM, Carter Tazio Schonwald wrote:
as far as I'm concerned, theres zero controversy on this topic, all those second editions of haskell teaching material worked out well for their authors.
Well, there was a very extensive discussion about this a year or two ago, that made it very clear that many people found it very controversial. I have not seen any more extensive discussion since, so I so no reason to assume that the controversy is gone.
As to teaching, that is a very strong statement. There is a lot more to teaching than text books. Personally, I have, on numerous occasions, given introductions to monads without discussing applicatives, as monads is what was needed and there was no time at all for applicatives. (Try to cover functors, applicatives, and monads in a single lecture to an audience new to the topic.)
And at least one colleague of mine is the author of a best-seling Haskell textbook with decades of experience of teaching Haskell. In his (strong) opinion, teaching monads has become much more challenging with AMP, and further changes like MRP would not help at all.
plus applicative do is a pretty sweet extension and we shouldnt choose a semantics for return that precludes a haskell 2020 implementation that isn't ghc from having that fancy feature :)
Not having MRP does not mean not having applicatives. At all.
Best,
/Henrik
This message and any attachment are intended solely for the addressee and may contain confidential information. If you have received this message in error, please contact the sender and delete the email and attachment.
Any views or opinions expressed by the author of this email do not necessarily reflect the views of the University of Nottingham. Email communications with the University of Nottingham may be monitored where permitted by law.
My experience of teaching cohorts of 200 students is that AMP has made it harder to teach monads and more frustrating to grasp. It is still a controversial topic and I have spoken to several colleagues at other universities who universally agree that it has made teaching harder. (I am also somewhat frustrated and apprehensive about having to teach Semiring before Monoid). The point is not “because maths”: we academics well understand the theory involved. The point is that we are deeply concerned for the masses of undergraduates who will struggle with yet one more hurdle.
With all due respect, this is a reason to use base / prelude as h2010, or write your own fork of ghc/prelude, not for deliberately deviating in a way that no compiler will sensibly choose to follow. Complain at the compiler, not at the standard.
This is a terrible reason to not fold in how we now know how to build better systems.
Applicative-do notation lets users write applicative computations in monadic style and then the compiler transforms the program into an applicative only computation, which (ignoring some subtleties around evaluation strictness) allows some very powerful applicative only patterns to be made usable in the more comprehensible do-notation style. I'd rather we ACTUALLY make effort to ensure a compliant compiler could choose to support applicative do as a strictly mechanical desugaring computation of programs that preserves well typed-ness, which NECESSITATES return :: Applicative f => a -> f a, OR a more needlessly complicated rewriting implementation
I'm hearing griping about choices that GHC will be doing irrespective of your personal feelings on the matter, and a failure to reflect these changes will just cement h2020 as something that ghc will not even implement/adhere to.
I support keeping Join in Monad because its the right thing, and means we can poke Richard and Stephanie to circle back on augmenting GHCs Role system so that GHC can reflect that design. Theres no planet where MRP will be inverted in any distribution of ghc that can be reasonably anticipated to exist.
On Tue, Nov 20, 2018 at 5:28 AM Nicolas Wu [email protected] wrote:
My experience of teaching cohorts of 200 students is that AMP has made it harder to teach monads and more frustrating to grasp. It is still a controversial topic and I have spoken to several colleagues at other universities who universally agree that it has made teaching harder. (I am also somewhat frustrated and apprehensive about having to teach Semiring before Monoid). The point is not “because maths”: we academics well understand the theory involved. The point is that we are deeply concerned for the masses of undergraduates who will struggle with yet one more hurdle.
— You are receiving this because you commented.
Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/1#issuecomment-440223208, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAQwpc6dXdz9AylbFmWG_PQ9SSXvrrJks5uw9lngaJpZM4JS1Lq .
If I might make a modest proposal: what about removing the specifics of Monad from the standard?
I see two reasons to include Monad in the standard:
-
In order to describe the libraries. But, as is, the description of the libraries is a separable part of the report, and one might imagine making that section suggestive and/or an example instead of normative. Perhaps best in this regard is to make a set of minimal requirements of the libraries (i.e., that there exist a
Numclass with afromInteger :: Integer -> amethod, in order to define overloaded numbers), but not tightly specify the lot. -
To define
do. But we can definedowithout describing the details ofMonad. All we require is the desugaring into>>=and>>. (I don't mean to do this via rebindable syntax. The>>=and>>could be specified as regards to originating module.) I don't think there's even a language feature that directly invokesreturn. (Monad comprehensions would, but I don't think they're standardized.)
If we remove the details of Monad from the standard, then GHC can remain compliant, and other Haskell compilers may choose not to have Applicative as a superclass of Monad, or may decide that some other design (e.g. defaults) is better.
Hi,
With all due respect, this is a reason to use base / prelude as h2010, or write your own fork of ghc/prelude, not for deliberately deviating in a way that no compiler will sensibly choose to follow. Complain at the compiler, not at the standard.
This is a terrible reason to not fold in how we now know how to build better systems.
Note quite sure exactly what the "terrible reason" here is.
But no one has suggested a standard that would be DOA. The point is that there is plenty of scope for different designs, with various pros and cons, technical and not, and these pros and cons needs to be articulated clearly and concisely so that an informed choice can be made.
As to suggesting that teaching is done using a different version of the language, that is possible, of course (and attempts have been made), but carries with it its own problems. A very practical one is that it seems highly unlikely that a well-maintained teaching alternative would emerge. As to maintaining a fork of GHC in a teaching context, that does extremely infeasible once we consider it needs to keep working as the platforms and libraries keep evolving, and not only on some isolated lab computers, but also on students' computers.
Another point is that Haskell's success ultimately relies on a reasonably low barrier to entry. And in that context it is interesting to note many of my industrial contacts lament how difficult it is to hire Haskell programmers, and how even more difficult it is to hire Haskell programmers who do not overcomplicate things. To the point that in at least one recent case a company with a long-standing investment in Haskell felt compelled to switch to C++-.
But I digress.
So let's consider some solid technical arguments. Precluding support for some form of applicative do would certainly be a big cons. But:
we ACTUALLY make effort to ensure a compliant compiler could choose to support applicative do as a strictly mechanical desugaring computation of programs that preserves well typed-ness, which NECESSITATES return :: Applicative f => a -> f a, OR a more needlessly complicated rewriting implementation
It would be good to understand exactly why this is so.
Surely one could use "pure" in applicative do code? (Kind of similarly to how "returnA" is used in the Arrow do notation.) And using "pure" would arguably be a good thing as it clearly signals that a piece of code is applicative rather than monadic, which of course has implications as to where bound variables may occur and would likely also help with pinpointing errors due to misusing of variables?
Best,
/Henrik
This message and any attachment are intended solely for the addressee and may contain confidential information. If you have received this message in error, please contact the sender and delete the email and attachment.
Any views or opinions expressed by the author of this email do not necessarily reflect the views of the University of Nottingham. Email communications with the University of Nottingham may be monitored where permitted by law.
Hi,
On 11/20/2018 03:48 PM, Richard Eisenberg wrote:
If I might make a modest proposal: what about removing the specifics of |Monad| from the standard?
Interesting thought, but as Monads have become a very fundamental aspect of Haskell (witness the logo! :-), I would worry about the the language fragmenting beyond repair if this was not part of the standard.
At least such a move would have to be very carefully considered, and not just from the point of view of the short-term impact on the report.
Best,
/Henrik
This message and any attachment are intended solely for the addressee and may contain confidential information. If you have received this message in error, please contact the sender and delete the email and attachment.
Any views or opinions expressed by the author of this email do not necessarily reflect the views of the University of Nottingham. Email communications with the University of Nottingham may be monitored where permitted by law.
other Haskell compilers may choose not to have Applicative as a superclass of Monad
Yikes! That would mean that
foo :: Monad m => m Int
foo = pure 3
Would not be valid Haskell, according to the report. GHC already made this move and I am strongly aligned with what the proposed: "Whenever there's Monad code, you can use Functor/Applicative functions, without introducing an additional constraint."
The redundant functions (like return) are all well and good in an implementation such as GHC, but I think they should be struck from the Report. Let's design it like we should have from the beginning, cleanly.
I am in favor of this instead. Strike return from the report, consider a deprecation cycle for it in ghc. Or keep return in the report for language backwards-compatibility with the previous report, but indicate in the report that it is a legacy artifact.
For this, the proposal says 'With the new hierarchy, the answer would always be “use the least restrictive one”.' This is ok, but what would be better is to have an actual deprecation cycle, so that we eventually arrive at always using fmap, and having liftA and liftM not exist anymore, etc.
As for the inconvenience of having to define instances, I think requiring users to use explicit definitions comparable to fmapDefault is not so bad.
instance Functor F where
fmap = ...
instance Applicative F where
pure = ...
(<*>) = apMonadDefault
instance Monad F where
join = ...
instance Functor G where
fmap = fmapMonadDefault
instance Applicative G where
pure = ...
(<*>) = apMonadDefault
instance Monad F where
(>>=) = ...
A code linter could check to make sure you're not using fmapMonadDefault to define fmap cyclically (since the default impl of >>= is in terms of fmap and join, and fmapMonadDefault f m = m >>= pure . f ).
I would prefer (<*>) = apMonadDefault to be implicit, but I understand if there are reasons not to do so.
Good points dan.
I guess we’re going to have to layout the landscape of possible edits and bring the decision before the full language committee.
I would like to remind folks that in some directions we have no authority over the decision making over CLC and GHC. It’s a partnership.
On Tue, Nov 20, 2018 at 11:45 AM Dan Burton [email protected] wrote:
other Haskell compilers may choose not to have Applicative as a superclass of Monad
Yikes! That would mean that
foo :: Monad m => m Int foo = pure 3
Would not be valid Haskell, according to the report. GHC already made this move and I am strongly aligned with what the proposed: "Whenever there's Monad code, you can use Functor/Applicative functions, without introducing an additional constraint."
The redundant functions (like return) are all well and good in an implementation such as GHC, but I think they should be struck from the Report. Let's design it like we should have from the beginning, cleanly.
I am in favor of this instead. Strike return from the report, consider a deprecation cycle for it in ghc. Or keep return in the report for language backwards-compatibility with the previous report, but indicate in the report that it is a legacy artifact.
For this, the proposal says 'With the new hierarchy, the answer would always be “use the least restrictive one”.' This is ok, but what would be better is to have an actual deprecation cycle, so that we eventually arrive at always using fmap, and having liftA and liftM not exist anymore, etc.
As for the inconvenience of having to define instances, I think requiring users to use explicit definitions comparable to fmapDefault is not so bad.
instance Functor F where fmap = ... instance Applicative F where pure = ... (<*>) = apMonadDefault instance Monad F where join = ...
instance Functor G where fmap = fmapMonadDefault instance Applicative G where pure = ... (<*>) = apMonadDefault instance Monad F where (>>=) = ...
A code linter could check to make sure you're not using fmapMonadDefault to define fmap cyclically (since the default impl of >>= is in terms of fmap and join, and fmapMonadDefault f m = m >>= pure . f ).
I would prefer (<*>) = apMonadDefault to be implicit, but I understand if there are reasons not to do so.
— You are receiving this because you commented.
Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/1#issuecomment-440343126, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAQwoTa2QxiDBVwpY1bP8tNFkuqyiVLks5uxDHFgaJpZM4JS1Lq .
I'm glad that the passions have proven still alive, but can we please narrow the scope of this discussion a bit? This particular proposal is merely to add the Applicative class to the report as a subclass of Functor and superclass of Monad. This does open further questions, particularly what to do with return, but they can be resolved by follow-up proposals. If anybody feels strongly about this, here is a perfect chance to volunteer to write up both AMP and MRP or whatever and settle the issue the way you prefer.
So I'll repeat the question that started this round: will anybody volunteer to finalize this (AMP) proposal? The only reason not to do this is the one offered by goldfirere:
If I might make a modest proposal: what about removing the specifics of
Monadfrom the standard?
I had considered this myself, but I feel that constructor type classes are such a core type of Haskell in practice that glossing over them would be highly misleading. I know that the language report is not the language reference, but this to me feels like cheating.
Still, I could be convinced. Can you clarify what you mean by originating module in
- To define
do. But we can definedowithout describing the details ofMonad. All we require is the desugaring into>>=and>>. (I don't mean to do this via rebindable syntax. The>>=and>>could be specified as regards to originating module.)
Do you mean that the do notation would still desugar to Prelude.>>= and Prelude.>>?