{-# LANGUAGE NoImplicitPrelude #-} module Precursor.Coerce ( Coercible , coerce , (#.) ) where import Data.Coerce infixr 9 #. -- | It may be better to use @('#.')@ instead of -- @('Precursor.Control.Category..')@ to avoid potential efficiency -- problems relating to #7542. The problem, in a nutshell: -- -- If @N@ is a newtype constructor, then @N x@ will always have the same -- representation as @x@ (something similar applies for a newtype -- deconstructor). However, if @f@ is a function, -- -- @N 'Precursor.Control.Category..' f = \x -> N (f x)@ -- -- This looks almost the same as @f@, but the eta expansion lifts it--the -- lhs could be @_|_@, but the rhs never is. This can lead to very -- inefficient code. Thus we steal a technique from Shachaf and Edward -- Kmett and adapt it to the current (rather clean) setting. Instead of -- using @N 'Precursor.Control.Category..' f@, we use @N '.#' f@, which -- is just -- -- @'coerce' f `'Prelude.asTypeOf'` (N 'Precursor.Control.Category..' f)@ -- -- That is, we just *pretend* that @f@ has the right type, and thanks to -- the safety of 'coerce', the type checker guarantees that nothing really -- goes wrong. We still have to be a bit careful, though: remember that -- '#.' completely ignores the *value* of its left operand. (#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c) (#.) _ = coerce {-# INLINE (#.) #-}