Opened 3 years ago

Closed 3 years ago

#11835 closed bug (fixed)

ApplicativeDo failed to desugar last line with pure $ <expr>

Reported by: Cosmia Owned by: simonmar
Priority: normal Milestone: 8.0.2
Component: Compiler Version: 8.0.1-rc2
Keywords: ApplicativeDo Cc:
Operating System: MacOS X Architecture: Unknown/Multiple
Type of failure: GHC rejects valid program Test Case:
Blocked By: Blocking:
Related Tickets: #11607 Differential Rev(s): Phab:D2345
Wiki Page:


{-# LANGUAGE ApplicativeDo #-}
f m = do
  x <- m 1
  y <- m 2
  return $ x + y

f should have type (Applicative f, Num a, Num b) => (a -> f b) -> f b but ghc considers f a monad

maybe similar with #11607

Change History (14)

comment:1 Changed 3 years ago by simonpj

Owner: set to simonmar

Yes, it's another example of #11607.

The user manual should state the importance of using pure or return as the last statement. And point out that you can't use a $.

Currently it is silent on these points.

comment:2 Changed 3 years ago by simonmar

@simonpj, the documentation does say that you need to use pure or return.

I'll add a note about $.

Last edited 3 years ago by simonmar (previous) (diff)

comment:3 Changed 3 years ago by simonmar

Milestone: 8.0.2
Status: newmerge

comment:4 Changed 3 years ago by chreekat

Would it possible for the docs to give a brief description of *why* one must follow these syntactic rules?

comment:5 Changed 3 years ago by bernalex

And, by extension, why we can't do better in GHC. Because I'm curious. Why can't we do better?

comment:6 Changed 3 years ago by simonmar

GHC translates

do x <- e
   return (f x)


(\x -> f x) <$> e

So we have to spot return, because the transformation removes it. You could invent more rules, say spot return $ e for example, but there would always be more examples that you couldn't handle, it's not possible to be completely general here.

Consider return (or pure) as part of the syntax of the do expression, like a keyword. Maybe if we were starting from scratch the syntax would be different. Indeed, in Monad Comprehensions, the return is always there implicitly.

comment:7 Changed 3 years ago by ezyang

Although it is terrible, I there is precedent for having a special case for $ (letting it be impredicatively instantiated to handle things like runST $ ...). So I don't think it would be unreasonable for there to be a special case here.

comment:8 Changed 3 years ago by simonmar

Differential Rev(s): Phab:D2345

comment:9 Changed 3 years ago by Simon Marlow <marlowsd@…>

In 0ba34b6/ghc:

ApplicativeDo: allow "return $ e"

There's a precedent for special-casing $, as we already have special
typing rules for it.

Test Plan: validate; new test cases

Reviewers: ezyang, austin, niteria, bgamari, simonpj, erikd

Subscribers: thomie

Differential Revision:

GHC Trac Issues: #11835

comment:10 in reply to:  6 Changed 3 years ago by chreekat

Comment 6 provides the rationale for this situation, where the expected behavior isn't really possible because of lower-level concerns.

Could that rationale be mentioned in the docs? Something like:

--- a/docs/users_guide/glasgow_exts.rst
+++ b/docs/users_guide/glasgow_exts.rst
@@ -860,7 +860,9 @@ upon the results ```` with either ``return`` or ``pure``.
 Note: the final statement really must be of the form ``return E`` or
 ``pure E``, otherwise you get a ``Monad`` constraint.  Using ``$`` as
-in ``return $ E`` or ``pure $ E`` is also acceptable.
+in ``return $ E`` or ``pure $ E`` is also acceptable. This is because we
+must spot the ``return`` before `GHC transforms do-syntax into fmap

I've not had much experience contributing to GHC, so I apologize for the unwieldy format of this suggestion.

Last edited 3 years ago by chreekat (previous) (diff)

comment:11 Changed 3 years ago by Simon Marlow <marlowsd@…>

In ee3bde7/ghc:

Expand and clarify the docs for ApplicativeDo (#11835)

comment:12 Changed 3 years ago by EyalLotem

Could be interesting to detect pure/return via their type though, rather than name.

If you have a function of type: (a -> m a) with up to Monad constraints on the m, then you can consider it a "return".

For example, here: ($) return :: Monad m => a -> m a

No special casing needed, it's the type of return so it must *be* return.

This would currently break down due to fail:

const (fail "boo!") :: Monad m => a -> m a

But that will be fixed once fail is taken the hell out of Monad.

Last edited 3 years ago by EyalLotem (previous) (diff)

comment:13 Changed 3 years ago by simonmar

Interesting idea, but at the point we're doing this, we're in the renamer and we don't have type info.

comment:14 Changed 3 years ago by bgamari

Resolution: fixed
Status: mergeclosed

Merged comment:11 and comment:9 to ghc-8.0.

Note: See TracTickets for help on using tickets.