packages feed

ten-0.1.0.0: src/Data/Ten/Functor.hs

-- Copyright 2018-2021 Google LLC
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

-- | Provides an analog of 'Functor' over arity-1 type constructors.

{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

module Data.Ten.Functor
         ( Functor10(..), (<$!), (<$>!), void10
         ) where

import Data.Kind (Type)
import Data.Proxy (Proxy(..))
import GHC.Generics
         ( Generic1(..)
         , (:.:)(..), (:*:)(..), (:+:)(..)
         , M1(..), Rec1(..), U1(..), V1, K1(..)
         )

import Data.Wrapped (Wrapped1(..))

import Data.Ten.Ap (Ap10(..))

-- | 'Functor' over arity-1 type constructors.
--
-- Whereas 'Functor' maps @a :: Type@ values to @b :: Type@ values, 'Functor10'
-- maps @(m :: k -> Type) a@ values to @m b@ values, parametrically in @a@.
-- That is, the type parameter of 'Functor' has arity 0, and the type
-- parameter of 'Functor10' has arity 1.
class Functor10 (f :: (k -> Type) -> Type) where
  -- | Map each @m a@ value in @f m@ parametrically to @n a@ to get @f m@.
  fmap10 :: (forall a. m a -> n a) -> f m -> f n

instance (Generic1 f, Functor10 (Rep1 f))
      => Functor10 (Wrapped1 Generic1 f) where
  fmap10 f = Wrapped1 . to1 . fmap10 f . from1 . unWrapped1

instance Functor10 (Ap10 a) where
  fmap10 f (Ap10 x) = Ap10 (f x)

instance Functor10 (K1 i a) where
  fmap10 _ (K1 x) = K1 x

instance Functor10 V1 where
  fmap10 _ x = case x of {}

instance Functor10 U1 where
  fmap10 _ U1 = U1

deriving instance Functor10 f => Functor10 (Rec1 f)
deriving instance Functor10 f => Functor10 (M1 i c f)

instance (Functor10 f, Functor10 g) => Functor10 (f :+: g) where
  fmap10 f (L1 x) = L1 (fmap10 f x)
  fmap10 f (R1 x) = R1 (fmap10 f x)

instance (Functor10 f, Functor10 g) => Functor10 (f :*: g) where
  fmap10 f (l :*: r) = fmap10 f l :*: fmap10 f r

instance (Functor f, Functor10 g) => Functor10 (f :.: g) where
  fmap10 f (Comp1 x) = Comp1 $ fmap (fmap10 f) x

infixl 4 <$!
-- | ('<$') for 'Functor10'.
(<$!) :: Functor10 f => (forall a. n a) -> f m -> f n
x <$! f = fmap10 (const x) f

infixl 4 <$>!
-- | ('<$>') for 'Functor10'.
(<$>!) :: Functor10 f => (forall a. m a -> n a) -> f m -> f n
(<$>!) = fmap10

-- | 'Data.Functor.void' for 'Functor10'.
--
-- This returns @f 'Proxy'@ because @Proxy :: k -> Type@ has the right kind and
-- carries no runtime information.  It's isomorphic to @Const ()@ but easier to
-- spell.
void10 :: Functor10 f => f m -> f Proxy
void10 = fmap10 (const Proxy)