{-# 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 (#.) #-}