[GHC] #14855: Implementation of liftA2 for Const has high arity

#14855: Implementation of liftA2 for Const has high arity -------------------------------------+------------------------------------- Reporter: lyxia | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: | Version: 8.2.2 libraries/base | Keywords: | Operating System: Unknown/Multiple Architecture: | Type of failure: Runtime Unknown/Multiple | performance bug Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- {{{ instance Monoid m => Applicative (Const m) where pure _ = Const mempty liftA2 _ (Const x) (Const y) = Const (x `mappend` y) (<*>) = coerce (mappend :: m -> m -> m) }}} https://hackage.haskell.org/package/base-4.10.1.0/docs/src/Data.Functor.Cons... `(<*>)` is implemented with a `coerce` but `liftA2` isn't. Would the following not have better inlining behavior? {{{ liftA2 _ = coerce (mappend :: m -> m -> m) }}} Going further, should the unused argument also be moved to the RHS? What about `pure`? What are the pros and cons compared to this other alternative: {{{ pure = \_ -> mempty liftA2 = \_ -> coerce (mappend :: m -> m -> m) }}} This came up while implementing `Applicative` for `K1` in phab:D4447. `K1` is essentially the same type as `Const` and thus their instances should be identical for the sake of consistency. Is `Const`'s `Applicative` instance already optimal? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14855 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14855: Implementation of liftA2 for Const has high arity -------------------------------------+------------------------------------- Reporter: lyxia | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: libraries/base | Version: 8.2.2 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): Wiki Page: | -------------------------------------+------------------------------------- Changes (by lyxia): * cc: lyxia (added) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14855#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14855: Implementation of liftA2 for Const has high arity -------------------------------------+------------------------------------- Reporter: lyxia | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: libraries/base | Version: 8.2.2 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): Wiki Page: | -------------------------------------+------------------------------------- Description changed by lyxia: Old description:
{{{ instance Monoid m => Applicative (Const m) where pure _ = Const mempty liftA2 _ (Const x) (Const y) = Const (x `mappend` y) (<*>) = coerce (mappend :: m -> m -> m) }}}
https://hackage.haskell.org/package/base-4.10.1.0/docs/src/Data.Functor.Cons...
`(<*>)` is implemented with a `coerce` but `liftA2` isn't. Would the following not have better inlining behavior?
{{{ liftA2 _ = coerce (mappend :: m -> m -> m) }}}
Going further, should the unused argument also be moved to the RHS? What about `pure`? What are the pros and cons compared to this other alternative:
{{{ pure = \_ -> mempty liftA2 = \_ -> coerce (mappend :: m -> m -> m) }}}
This came up while implementing `Applicative` for `K1` in phab:D4447. `K1` is essentially the same type as `Const` and thus their instances should be identical for the sake of consistency. Is `Const`'s `Applicative` instance already optimal?
New description: {{{ instance Monoid m => Applicative (Const m) where pure _ = Const mempty liftA2 _ (Const x) (Const y) = Const (x `mappend` y) (<*>) = coerce (mappend :: m -> m -> m) }}} https://hackage.haskell.org/package/base-4.10.1.0/docs/src/Data.Functor.Cons... `(<*>)` is implemented with a `coerce` but `liftA2` isn't. Would the following not have better inlining behavior? {{{ liftA2 _ = coerce (mappend :: m -> m -> m) }}} Going further, should the unused argument also be moved to the RHS? What about `pure`? What are the pros and cons compared to this other alternative: {{{ pure = \_ -> mempty liftA2 = \_ -> coerce (mappend :: m -> m -> m) }}} This came up while implementing `Applicative` for `K1` in phab:D4447. `K1` is essentially the same type as `Const` and thus their instances should be identical for the sake of consistency. But it's not clear to me whether `Const`'s `Applicative` instance is already optimal. Is it? -- -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14855#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14855: Implementation of liftA2 for Const has high arity -------------------------------------+------------------------------------- Reporter: lyxia | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: libraries/base | Version: 8.2.2 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): Wiki Page: | -------------------------------------+------------------------------------- Description changed by lyxia: Old description:
{{{ instance Monoid m => Applicative (Const m) where pure _ = Const mempty liftA2 _ (Const x) (Const y) = Const (x `mappend` y) (<*>) = coerce (mappend :: m -> m -> m) }}}
https://hackage.haskell.org/package/base-4.10.1.0/docs/src/Data.Functor.Cons...
`(<*>)` is implemented with a `coerce` but `liftA2` isn't. Would the following not have better inlining behavior?
{{{ liftA2 _ = coerce (mappend :: m -> m -> m) }}}
Going further, should the unused argument also be moved to the RHS? What about `pure`? What are the pros and cons compared to this other alternative:
{{{ pure = \_ -> mempty liftA2 = \_ -> coerce (mappend :: m -> m -> m) }}}
This came up while implementing `Applicative` for `K1` in phab:D4447. `K1` is essentially the same type as `Const` and thus their instances should be identical for the sake of consistency. But it's not clear to me whether `Const`'s `Applicative` instance is already optimal. Is it?
New description: {{{ instance Monoid m => Applicative (Const m) where pure _ = Const mempty liftA2 _ (Const x) (Const y) = Const (x `mappend` y) (<*>) = coerce (mappend :: m -> m -> m) }}} https://hackage.haskell.org/package/base-4.10.1.0/docs/src/Data.Functor.Cons... `(<*>)` is implemented with a `coerce` but `liftA2` isn't. Would the following not have better inlining behavior? {{{ liftA2 _ = coerce (mappend :: m -> m -> m) }}} Going further, should the unused argument also be moved to the RHS? What about `pure`? What are the pros and cons compared to this other alternative: {{{ pure = \_ -> Const mempty liftA2 = \_ -> coerce (mappend :: m -> m -> m) }}} This came up while implementing `Applicative` for `K1` in phab:D4447. `K1` is essentially the same type as `Const` and thus their instances should be identical for the sake of consistency. But it's not clear to me whether `Const`'s `Applicative` instance is already optimal. Is it? -- -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14855#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14855: Implementation of liftA2 for Const has high arity -------------------------------------+------------------------------------- Reporter: lyxia | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: libraries/base | Version: 8.2.2 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): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): I think you'll get the same code in all these cases, but you could `-ddump-simpl` to check. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14855#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14855: Implementation of liftA2 for Const has high arity -------------------------------------+------------------------------------- Reporter: lyxia | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: libraries/base | Version: 8.2.2 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): Wiki Page: | -------------------------------------+------------------------------------- Comment (by lyxia): The Core looks different when the `Monoid` remains abstract, but the same if it is specialized. So there is some optimization that makes it fine in the end. Example with abstract `Monoid`: {{{ {-# LANGUAGE ScopedTypeVariables #-} module W where import Data.Coerce import Data.Functor.Const xyz, abc :: forall x a b c. Monoid x => (a -> b -> c) -> Const x a -> Const x b -> Const x c xyz _ (Const a) (Const b) = Const (mappend a b) abc _ = coerce (mappend :: x -> x -> x) }}} Core: {{{ -- RHS size: {terms: 12, types: 24, coercions: 10, joins: 0/0} xyz1 xyz1 = \ @ x_a10q @ a_a10r @ b_a10s @ c_a10t $dMonoid_a10v _ ds1_d11x ds2_d11y -> mappend $dMonoid_a10v (ds1_d11x `cast` Co:5) (ds2_d11y `cast` Co:5) -- RHS size: {terms: 1, types: 0, coercions: 37, joins: 0/0} xyz xyz = xyz1 `cast` Co:37 -- RHS size: {terms: 8, types: 14, coercions: 0, joins: 0/0} abc1 abc1 = \ @ x_a105 @ a_a106 @ b_a107 @ c_a108 $dMonoid_a10a _ -> mappend $dMonoid_a10a -- RHS size: {terms: 1, types: 0, coercions: 39, joins: 0/0} abc abc = abc1 `cast` Co:39 }}} -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14855#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14855: Implementation of liftA2 for Const has high arity -------------------------------------+------------------------------------- Reporter: lyxia | Owner: (none) Type: bug | Status: closed Priority: normal | Milestone: Component: libraries/base | Version: 8.2.2 Resolution: invalid | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by lyxia): * status: new => closed * resolution: => invalid -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14855#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC