[GHC] #10457: Revise/remove custom mapM implementation for lists

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: | Version: 7.10.1 libraries/base | Operating System: Unknown/Multiple Keywords: | Type of failure: None/Unknown Architecture: | Blocked By: Unknown/Multiple | Related Tickets: Test Case: | Blocking: | Differential Revisions: | -------------------------------------+------------------------------------- Recently, Simon Marlow asked why the list instance for Traversable had a custom mapM implementation that used the Monad operations. Having looked a bit, I don't think there's any good reason. The only fusion that the custom mapM can participate in is due to it being written as foldr, but traverse is, as well. So as long as 'mapM = traverse' is able to inline appropriately, there should be no difference. Further, this can be changed, in principle, for 7.10.2. It doesn't change any types, only the implementation. mapM = traverse is the class default definition, so this could possibly be completed by just removing the custom definition. Link to the libraries thread: https://mail.haskell.org/pipermail/libraries/2015-May/025708.html -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by dolio): `mapM_` is in a similar situation, despite not being in the `Foldable` class. It is currently implemented with `(>>)`, but it could be implemented with `(<*)` or just as `mapM_ = traverse_`. This is desired as well (see the most recent message in the libraries thread at the time of this comment). -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: -------------------------------------+------------------------------------- Changes (by simonmar): * cc: simonmar (added) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Changes (by hvr): * differential: => Phab:D924 Comment: here's a first attempt at a patch; please review if that's the change you suggest -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Comment (by ekmett): Looks right to me. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Comment (by simonpj): Well someone should preferably just check that whatever fusion used to happen still happens, and ideally add a performance test to make sure it stays that way. That done, go ahead if the Core Libraries Committee is happy. (And Edward is presumably speaking as its chair in comment:4.) I'm unconvinced it's worth merging to 7.10. After all, it changes nothing except omitting one line of code from a library, right? Simon -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Comment (by simonmar): Hmmm, so this is only a transparent change for Monads that have `<*> = ap`. Arguably since it is possible to write legal (though unconventional) Haskell that behaves differently, and it's not a bug fix, it should not go into 7.10.2. (arguing against my own best interests, because it being in 7.10.2 would be helpful for me, but I've been working around it for a while so I can continue to do so) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Comment (by dolio): I'm not really attached to this, so if simonmar is okay with postponing to 7.12, it's fine with me. Anyone who uses `Monad`s with `(<*>) /= ap` (at least, in ways giving distinct answers) is going to have a bad time, though. The specification of `Monad` now says they need to be equivalent, and I think that gives library writers license to switch between them without making a major version change. And it's only going to get worse. When ApplicativeDo gets implemented, using that extension will silently switch between these things. For instance, if ApplicativeDo were on in the file with the custom `mapM`, it would already be the same as `traverse`, I think. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:7 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Comment (by simonpj): I'm a bit lost. The only change seems to be this: {{{ instance Traversable [] where instance Traversable [] where traverse f = List.foldr cons_f (pure []) where cons_f x ys = (:) <$> f x <*> ys mapM = Monad.mapM -- <========= DELETE THIS LINE }}} That means using the default method, `traverse`, rather than `mapM`. Why is that a big deal? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Comment (by dolio): If I understand correctly, simonmar has a `Monad` whose `Applicative` instance does batching of remote calls, because you can do that when you use `(<*>)` but not `(>>=)`. So: {{{ f <*> x }}} does one round trip to the server, while: {{{ f >>= \g -> x >>= \y -> return (g y) }}} does two. The custom `mapM` does n trips, where n is the length of the list, while `traverse` does one, because the latter is implemented using: {{{ cons_f x ys = (:) <$> f x <*> ys }}} whereas the custom one is: {{{ cons_f x ys = do { z <- f x ; zs <- ys ; return (z:zs) } }}} -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Comment (by simonmar): @simonpj You could in principle write an `Applicative` or `Monad` instance that would distinguish between `mapM` and `traverse`, although it wouldn't be a good idea. But strictly speaking this is therefore a semantic change. In my case it makes a (huge) difference to performance, because `traverse` is parallel for the Haxl monad, where as `mapM` is serial. There's no difference in semantics, provided you don't dig into the monad implementation and only use the external interface, and provided some other conditions are met. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:10 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: patch Priority: normal | Milestone: 7.10.2 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Changes (by thoughtpolice): * status: new => patch -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:11 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: patch Priority: normal | Milestone: 7.12.1 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: | Test Case: Related Tickets: | Blocking: | Differential Revisions: Phab:D924 -------------------------------------+------------------------------------- Changes (by thoughtpolice): * milestone: 7.10.2 => 7.12.1 Comment: From my notes from last week, we're moving this out of 7.10.2, which I forgot about. Punting. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:12 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: patch Priority: normal | Milestone: 7.12.1 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: Phab:D1124 -------------------------------------+------------------------------------- Changes (by bgamari): * differential: Phab:D924 => Phab:D1124 Old description:
Recently, Simon Marlow asked why the list instance for Traversable had a custom mapM implementation that used the Monad operations. Having looked a bit, I don't think there's any good reason. The only fusion that the custom mapM can participate in is due to it being written as foldr, but traverse is, as well. So as long as 'mapM = traverse' is able to inline appropriately, there should be no difference.
Further, this can be changed, in principle, for 7.10.2. It doesn't change any types, only the implementation.
mapM = traverse is the class default definition, so this could possibly be completed by just removing the custom definition.
Link to the libraries thread: https://mail.haskell.org/pipermail/libraries/2015-May/025708.html
New description: Recently, Simon Marlow asked why the list instance for `Traversable` had a custom `mapM` implementation that used the `Monad` operations. Having looked a bit, I don't think there's any good reason. The only fusion that the custom `mapM` can participate in is due to it being written as `foldr`, but `traverse` is, as well. So as long as `mapM = traverse` is able to inline appropriately, there should be no difference. Further, this can be changed, in principle, for 7.10.2. It doesn't change any types, only the implementation. `mapM = traverse` is the class default definition, so this could possibly be completed by just removing the custom definition. Link to the libraries thread: https://mail.haskell.org/pipermail/libraries/2015-May/025708.html -- Comment: Phab:D924 proposed a redefinition of `mapM_` in addition to the removal of list's override of `mapM`. This changed the performance characteristics of user code, as seen in #10711. I've extracted the removal of the `mapM` override into Phab:D1124 and forwarded the `mapM_` issue to the Core Libraries Committee. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:13 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists
-------------------------------------+-------------------------------------
Reporter: dolio | Owner:
Type: task | Status: patch
Priority: normal | Milestone: 7.12.1
Component: libraries/base | Version: 7.10.1
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Revisions: Phab:D1124
-------------------------------------+-------------------------------------
Comment (by Ben Gamari

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: closed Priority: normal | Milestone: 7.12.1 Component: libraries/base | Version: 7.10.1 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: Phab:D1124 -------------------------------------+------------------------------------- Changes (by bgamari): * status: patch => closed * resolution: => fixed -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:15 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 7.12.1 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: Phab:D1124 -------------------------------------+------------------------------------- Changes (by simonpj): * status: closed => new * resolution: fixed => Comment: The [https://perf.haskell.org/ghc/#revision/60297486fddd5c9695e2263c2ae46fa90f0fe... perf tester says], about nofib allocations: {{{ nofib/allocs/cryptarithm2 + 64.55% nofib/allocs/k-nucleotide - 5.03% nofib/time/fannkuch-redux - 3.62% }}} This needs looking into! Simon -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:16 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: new Priority: normal | Milestone: 8.0.1 Component: libraries/base | Version: 7.10.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D1124 -------------------------------------+------------------------------------- Changes (by thomie): * failure: None/Unknown => Runtime performance bug Comment: nofib/allocs/cryptarithm2 13094088 + 64.55% 21545696 bytes -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:18 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: closed Priority: normal | Milestone: 8.0.1 Component: libraries/base | Version: 7.10.1 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D1124 Wiki Page: | -------------------------------------+------------------------------------- Changes (by thomie): * status: new => closed * resolution: => fixed Comment: The performance regression is fixed: https://perf.haskell.org/ghc/#revision/fff02548d237655dea39f108364d7ebe6d0e1.... nofib/allocs/cryptarithm2 21545696 - 41.58% 12588032 bytes -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:19 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10457: Revise/remove custom mapM implementation for lists -------------------------------------+------------------------------------- Reporter: dolio | Owner: Type: task | Status: closed Priority: normal | Milestone: 8.0.1 Component: libraries/base | Version: 7.10.1 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D1124 Wiki Page: | -------------------------------------+------------------------------------- Comment (by nomeata): JFTR: This instance of the regression has been fixed, because the `State` implementation got a sensible `Applicative` instance. The change in changeset:60297486/ghc still has the ability to regress any user code that has `(<*>) = ap`. OTOH, there is little we can do about it besides telling people to implement their Applicatives properly. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10457#comment:20 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC