#14855 closed bug (invalid)

Implementation of liftA2 for Const has high arity

Reported by: lyxia Owned by:
Priority: normal Milestone:
Component: libraries/base Version: 8.2.2
Keywords: Cc: lyxia
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Runtime performance bug Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description (last modified by lyxia)

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.Const.html#line-69

(<*>) 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?

Change History (6)

comment:1 Changed 22 months ago by lyxia

Cc: lyxia added

comment:2 Changed 22 months ago by lyxia

Description: modified (diff)

comment:3 Changed 22 months ago by lyxia

Description: modified (diff)

comment:4 Changed 22 months ago by simonpj

I think you'll get the same code in all these cases, but you could -ddump-simpl to check.

comment:5 Changed 22 months ago 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>

comment:6 Changed 22 months ago by lyxia

Resolution: invalid
Status: newclosed
Note: See TracTickets for help on using tickets.