overloaded-records-0.3.0.0: src/Data/OverloadedRecords.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- |
-- Module: $HEADER$
-- Description: Magic classes for OverloadedRecordFields.
-- Copyright: (c) 2016, Peter Trško
-- License: BSD3
--
-- Maintainer: peter.trsko@gmail.com
-- Stability: experimental
-- Portability: DataKinds, FlexibleInstances, FunctionalDependencies,
-- MagicHash, MultiParamTypeClasses, NoImplicitPrelude,
-- TypeFamilies, UndecidableInstances
--
-- Magic classes for OverloadedRecordFields.
--
-- Implementation is based on:
-- <https://github.com/adamgundry/records-prototype/blob/master/CoherentPrototype.hs>
-- by Adam Gundry under MIT License.
module Data.OverloadedRecords
(
-- * Oveloaded Labels
module Data.OverloadedLabels
-- * Overloaded Record Fields
--
-- ** Getter
, FieldType
, HasField(..)
-- ** Setter
, UpdateType
, SetField(..)
, Setter
, set
-- ** IsLabel For Getter and Lens
, FromArrow
, IsFieldAccessor(..)
)
where
import Data.Bool (Bool(False, True))
import Data.Functor (Functor, (<$>))
import GHC.TypeLits (Symbol)
import GHC.Exts (Proxy#)
import Data.OverloadedLabels
-- | When accessing field named @l :: Symbol@ of a record @s :: *@, then the
-- type of the value in that field is @'FieldType' l s@.
type family FieldType (l :: Symbol) (s :: *) :: *
-- | If field @l :: Symbol@ of a record @s :: *@ is set to new value which has
-- type @a :: *@, then the modified record will have type @'UpdateType' l s a@.
type family UpdateType (l :: Symbol) (s :: *) (a :: *) :: *
-- | Definition of this class is based on:
-- <https://phabricator.haskell.org/D1687>
class HasField (l :: Symbol) s a | l s -> a where
-- | Get value of a field.
getField :: Proxy# l -> s -> a
class
( HasField l s b
, FieldType l s ~ b
) => SetField l s b
where
-- | Set value of a field.
setField :: Proxy# l -> s -> b -> UpdateType l s b
-- | Returns 'True' if type @a@ is a function.
type family FromArrow (a :: *) :: Bool where
FromArrow (x -> y) = 'True
FromArrow t = 'False
-- | Distinguish between getter and lens.
class
( z ~ FromArrow x
) => IsFieldAccessor (l :: Symbol) x y (z :: Bool) | l y -> x
where
fieldAccessor :: Proxy# l -> x -> y
instance IsFieldAccessor l x y (FromArrow x) => IsLabel l (x -> y) where
fromLabel = fieldAccessor
-- | Overloaded lens:
--
-- @
-- 'Functor' f => 'Proxy#' l -> (a -> f b) -> s -> f t
-- @
instance
( Functor f
, HasField l s a
, SetField l s b
, FieldType l s ~ a
, UpdateType l s b ~ t
) => IsFieldAccessor l (a -> f b) (s -> f t) 'True
where
fieldAccessor proxy f s = setField proxy s <$> f (getField proxy s)
-- | Overloaded getter:
--
-- @
-- 'Proxy#' l -> r -> a
-- @
instance
( HasField l s a
, FieldType l s ~ a
, FromArrow s ~ 'False
) => IsFieldAccessor l s a 'False
where
fieldAccessor = getField
-- | Wrapper for a set function, lens naming convention is used for type
-- variables. Its instance for 'IsLabel' forces overloaded label to behave as a
-- setter.
newtype Setter s t b = Setter (s -> b -> t)
-- | Extract set function from 'Setter'. Using 'Setter' instance for 'IsLabel'
-- forces overloaded label to behave as a setter.
--
-- Usage example:
--
-- @
-- newtype Bar a = Bar {_bar :: a}
-- deriving Show
--
-- overloadedRecord ''Bar
-- @
--
-- >>> set bar (Bar (Just False)) Nothing
-- Bar {_bar = Nothing}
set :: Setter s t b -> s -> b -> t
set (Setter f) = f
instance
( SetField l s b
, UpdateType l s b ~ t
) => IsLabel l (Setter s t b)
where
fromLabel _proxy = Setter (setField _proxy)