ten-0.1.0.0: src/Data/Ten/Ap.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 a field wrapper type to make @Generic1@ work with @Functor10@ etc.
--
-- GHC can't derive 'GHC.Generics.Generic1' instances for types that apply
-- their type parameter to a constant type (e.g. @data Thing f = Thing (f
-- Int)@, but it can handle the equivalent type when the application is hidden
-- under a newtype: @data Thing f = Thing (Ap10 Int f)@. So, by wrapping each
-- field in this newtype and providing the appropriate instances, we can use
-- Generics to derive instances for the whole hierarchy of
-- 'Data.Ten.Functor.Functor10' and related classes.
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Ten.Ap
( -- * Field Wrapper
Ap10(..)
-- * Instances
-- $ap10_instances
) where
import Data.Kind (Constraint, Type)
import GHC.Generics (Generic)
import Control.DeepSeq (NFData)
import Data.Default.Class (Default(..))
import Data.Hashable (Hashable(..))
import Data.Portray (Portray(..))
import Data.Portray.Diff (Diff)
import Data.Wrapped (Wrapped(..))
-- | A 'Data.Ten.Functor.Functor10' made by applying the argument to some type.
newtype Ap10 (a :: k) (f :: k -> Type) = Ap10 { unAp10 :: f a }
deriving Generic
-- $ap10_instances
--
-- [Note: Ap10 instances]
--
-- Since @Ap10 a f@ is a newtype over @f a@, it can adopt any instance that
-- @f a@ has, e.g. @Eq (f a) => Eq (Ap10 a f)@. This doesn't play very nicely
-- with inference of derived instance contexts, though: if you say @deriving
-- Eq@ on a type with an @f@ type parameter with an @Ap10 T f@ field, GHC will
-- complain about the missing instance @Eq (f T)@ rather than adding it to the
-- context. However, if we can arrange for this to be expressed as a
-- Haskell98-looking constraint of the form @C f@, GHC will be willing to add
-- that to the inferred context.
--
-- We can do this by adding a new class @EqAp f@ with the instance we really
-- want as a superclass, and using that as the context of 'Ap10'\'s @Eq@
-- instance. Now when trying to solve @Eq (Ap10 T f)@, GHC will simplify to
-- @(EqAp f, EqCtx f T)@. However, if we have just a catch-all instance for
-- @EqAp@, GHC will simplify it further to the instance context of that
-- instance, which would bring us back to a constraint GHC won't add to the
-- context, @forall a. Eq a => Eq (f a)@. We have to prevent GHC from doing
-- that simplification, which we can achieve by overlapping it with some other
-- instance, so that GHC can't choose the catch-all instance without knowing
-- more about @f@. To avoid weird behavior from the overlap, we make an
-- otherwise-unused type 'Decoy' to carry the instance.
--
-- Finally, because @Ap10@ is poly-kinded, if we used @Eq@ directly as the
-- context of that quantified constraint, we'd be saying that @Ap10@ can only
-- be @Eq@ when its hidden kind parameter is @Type@. Instead, we generalize it
-- to an associated type family 'EqCtx'. This might be e.g.
-- 'GHC.TypeNats.KnownNat' for 'GHC.TypeNats.Nat's, or simply nothing for
-- phantom type parameters. I'm not yet sure how to approach the instances for
-- other kinds -- for instance, should we provide stock ones, or expect users
-- to write kind-level newtypes and provide their own instances?
--
-- This trickery is applied to all the instances of Ap10. In particular this
-- means @deriving (Eq, Ord, Read, Show, Default, NFData)@ and
-- @deriving (Portray, Diff) via Wrapped Generic T@ will all work.
newtype Decoy a = Decoy ()
deriving stock (Eq, Ord, Read, Show, Generic)
deriving newtype (Default, NFData, Hashable)
deriving (Diff, Portray) via Wrapped Generic (Decoy a)
-- See [Note: Ap10 instances]
class (forall a. PortrayCtx f a => Portray (f a))
=> PortrayAp (f :: k -> Type) where
type PortrayCtx f :: k -> Constraint
instance (forall a. Portray a => Portray (f a)) => PortrayAp f where
type PortrayCtx f = Portray
instance {-# OVERLAPS #-} PortrayAp (Decoy :: Type -> Type) where
type PortrayCtx Decoy = Portray
deriving newtype instance (PortrayCtx f a, PortrayAp f) => Portray (Ap10 a f)
-- See [Note: Ap10 instances]
class (forall a. DiffCtx f a => Diff (f a)) => DiffAp (f :: k -> Type) where
type DiffCtx f :: k -> Constraint
instance (forall a. Diff a => Diff (f a)) => DiffAp f where
type DiffCtx f = Diff
instance {-# OVERLAPS #-} DiffAp (Decoy :: Type -> Type) where
type DiffCtx Decoy = Diff
deriving newtype instance (DiffCtx f a, DiffAp f) => Diff (Ap10 a f)
-- See [Note: Ap10 instances]
class (forall a. EqCtx f a => Eq (f a)) => EqAp (f :: k -> Type) where
type EqCtx f :: k -> Constraint
instance (forall a. Eq a => Eq (f a)) => EqAp f where
type EqCtx f = Eq
instance {-# OVERLAPS #-} EqAp (Decoy :: Type -> Type) where
type EqCtx Decoy = Eq
deriving newtype instance (EqCtx f a, EqAp f) => Eq (Ap10 a f)
-- See [Note: Ap10 instances]
class (forall a. OrdCtx f a => Ord (f a)) => OrdAp (f :: k -> Type) where
type OrdCtx f :: k -> Constraint
instance (forall a. Ord a => Ord (f a)) => OrdAp f where
type OrdCtx f = Ord
instance {-# OVERLAPS #-} OrdAp (Decoy :: Type -> Type) where
type OrdCtx Decoy = Ord
deriving newtype
instance (OrdCtx f a, OrdAp f, EqCtx f a, EqAp f) => Ord (Ap10 a f)
-- See [Note: Ap10 instances]
class (forall a. ReadCtx f a => Read (f a)) => ReadAp (f :: k -> Type) where
type ReadCtx f :: k -> Constraint
instance (forall a. Read a => Read (f a)) => ReadAp f where
type ReadCtx f = Read
instance {-# OVERLAPS #-} ReadAp (Decoy :: Type -> Type) where
type ReadCtx Decoy = Read
deriving newtype instance (ReadCtx f a, ReadAp f) => Read (Ap10 a f)
-- See [Note: Ap10 instances]
class (forall a. ShowCtx f a => Show (f a)) => ShowAp (f :: k -> Type) where
type ShowCtx f :: k -> Constraint
instance (forall a. Show a => Show (f a)) => ShowAp f where
type ShowCtx f = Show
instance {-# OVERLAPS #-} ShowAp (Decoy :: Type -> Type) where
type ShowCtx Decoy = Show
deriving newtype instance (ShowCtx f a, ShowAp f) => Show (Ap10 a f)
-- See [Note: Ap10 instances]
class (forall a. DefaultCtx f a => Default (f a)) => DefaultAp (f :: k -> Type) where
type DefaultCtx f :: k -> Constraint
instance (forall a. Default a => Default (f a)) => DefaultAp f where
type DefaultCtx f = Default
instance {-# OVERLAPS #-} DefaultAp (Decoy :: Type -> Type) where
type DefaultCtx Decoy = Default
deriving newtype instance (DefaultCtx f a, DefaultAp f) => Default (Ap10 a f)
-- See [Note: Ap10 instances]
class (forall a. NFDataCtx f a => NFData (f a)) => NFDataAp (f :: k -> Type) where
type NFDataCtx f :: k -> Constraint
instance (forall a. NFData a => NFData (f a)) => NFDataAp f where
type NFDataCtx f = NFData
instance {-# OVERLAPS #-} NFDataAp (Decoy :: Type -> Type) where
type NFDataCtx Decoy = NFData
deriving newtype instance (NFDataCtx f a, NFDataAp f) => NFData (Ap10 a f)
-- See [Note: Ap10 instances]
class (forall a. HashableCtx f a => Hashable (f a)) => HashableAp (f :: k -> Type) where
type HashableCtx f :: k -> Constraint
instance (forall a. Hashable a => Hashable (f a)) => HashableAp f where
type HashableCtx f = Hashable
instance {-# OVERLAPS #-} HashableAp (Decoy :: Type -> Type) where
type HashableCtx Decoy = Hashable
deriving newtype instance (HashableCtx f a, HashableAp f) => Hashable (Ap10 a f)