ten-0.1.0.0: src/Data/Ten/Traversable.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 'Traversable' over arity-1 type constructors.
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Ten.Traversable
( Traversable10(..), traverse10, sequenceA10, fsequenceA10
) where
import Data.Coerce (coerce)
import Data.Functor.Identity (Identity(..))
import Data.Kind (Type)
import GHC.Generics
( Generic1(..)
, (:.:)(..), (:*:)(..), (:+:)(..)
, M1(..), Rec1(..), U1(..), V1, K1(..)
)
import Data.Wrapped (Wrapped1(..))
import Data.Ten.Ap (Ap10(..))
import Data.Ten.Foldable (Foldable10)
import Data.Ten.Functor (Functor10)
(.:) :: (q -> r) -> (a -> b -> q) -> a -> b -> r
(.:) = (.) . (.)
-- | Analog of 'Traversable' over arity-1 type constructors.
--
-- This is defined in terms of 'mapTraverse10' for two reasons:
--
-- * First, it makes it possible to use with GeneralizedNewtypeDeriving and
-- DerivingVia. See
-- https://ryanglscott.github.io/2018/06/22/quantifiedconstraints-and-the-trouble-with-traversable/
-- for more details.
-- * Second, it uses fewer 'fmap's in some cases: when you need to re-apply a
-- constructor tag like 'L1' or 'R1' after calling 'traverse10' on the
-- payload, this would normally be an additional 'fmap', but with
-- 'mapTraverse10' it can be fused into the underlying recursive call. Less
-- crucially, the same trick applies when traversing multiple fields and
-- combining them back into a product type: the first call can use
-- 'mapTraverse10' to pre-apply the function, and use '<*>' rather than
-- 'Control.Applicative.liftA2' (which is often defined as an 'fmap' followed
-- by a '<*>').
class (Functor10 t, Foldable10 t)
=> Traversable10 (t :: (k -> Type) -> Type) where
-- | 'traverse10' with a built-in 'fmap' on the final result.
mapTraverse10
:: forall f m n r
. Applicative f
=> (t n -> r)
-> (forall a. m a -> f (n a))
-> t m -> f r
-- | Analog of 'traverse' for functors over arity-1 type constructors.
--
-- Given a parametric function that takes the wrapped type @m a@ to @n a@ in an
-- 'Applicative' @f@, visit all contained @m _@s to convert from @t m@ to @t n@.
--
-- @m@ and @n@ here play the role of @a@ and @b@ in the normal 'traverse' type;
-- that is, instead of traversing to change a @Type@, we're traversing to change
-- a type constructor of kind @k -> Type@:
--
-- @
-- traverse
-- :: (Traversable t, Applicative f)
-- => ( a -> f b ) -> t a -> f (t b)
-- traverse10
-- :: (Traversable10 t, Applicative f)
-- => (forall x. m x -> f (n x)) -> t m -> f (t n)
-- @
traverse10
:: forall t f m n
. (Traversable10 t, Applicative f)
=> (forall a. m a -> f (n a))
-> t m -> f (t n)
traverse10 = mapTraverse10 id
instance (Generic1 f, Traversable10 (Rep1 f))
=> Traversable10 (Wrapped1 Generic1 f) where
mapTraverse10 r f = mapTraverse10 (r . Wrapped1 . to1) f . from1 . unWrapped1
instance Traversable10 (Ap10 a) where
mapTraverse10 r f (Ap10 x) = r . Ap10 <$> f x
instance Traversable10 (K1 i a) where
mapTraverse10 r _ k = pure (r $ coerce k)
instance Traversable10 V1 where
mapTraverse10 _ _ x = case x of {}
instance Traversable10 U1 where
mapTraverse10 r _ U1 = pure (r U1)
instance Traversable10 f => Traversable10 (Rec1 f) where
mapTraverse10 r f (Rec1 x) = mapTraverse10 (r . Rec1) f x
instance Traversable10 f => Traversable10 (M1 i c f) where
mapTraverse10 r f (M1 x) = mapTraverse10 (r . M1) f x
instance (Traversable10 f, Traversable10 g) => Traversable10 (f :+: g) where
mapTraverse10 r f (L1 x) = mapTraverse10 (r . L1) f x
mapTraverse10 r f (R1 x) = mapTraverse10 (r . R1) f x
instance (Traversable10 f, Traversable10 g) => Traversable10 (f :*: g) where
mapTraverse10 r f (x :*: y) =
mapTraverse10 (r .: (:*:)) f x <*> traverse10 f y
instance (Traversable f, Traversable10 g) => Traversable10 (f :.: g) where
mapTraverse10 r f (Comp1 x) = r . Comp1 <$> traverse (traverse10 f) x
-- | 'sequenceA' for 'Traversable10'.
--
-- This variant expects just the plain @m@ actions at each field, and wraps the
-- results in @Identity.
sequenceA10
:: (Applicative m, Traversable10 f)
=> f m -> m (f Identity)
sequenceA10 = traverse10 (fmap Identity)
-- | 'sequenceA' for 'Traversable10'.
--
-- This variant expects the composition of the 'Applicative' being sequenced
-- with some inner type constructor at each field.
fsequenceA10
:: (Applicative m, Traversable10 f)
=> f (m :.: n) -> m (f n)
fsequenceA10 = traverse10 coerce