merge-0.3.1.1: src/Data/Merge.hs
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE RankNTypes #-}
{- |
Name: Data.Merge
Description: To describe merging of data types.
License: MIT
Copyright: Samuel Schlesinger 2021 (c)
-}
{-# LANGUAGE BlockArguments #-}
module Data.Merge
(
-- * A Validation Applicative
Validation(..)
, validation
-- * The Merge type
, Merge (Merge, runMerge)
-- * Construction
, merge
, optional
, required
, combine
, combineWith
, (.?)
, (.!)
, combineGen
, combineGenWith
, Alternative(..)
, Applicative(..)
-- * Modification
, flattenValidation
, Profunctor(..)
-- * Useful Semigroups
, Optional(..)
, Required(..)
, Last (..)
, First (..)
, Product (..)
, Sum (..)
, Dual (..)
, Max (..)
, Min (..)
) where
import GHC.Generics (Generic)
import Data.Typeable (Typeable)
import Control.Monad (join)
import Data.Coerce (Coercible, coerce)
import Control.Applicative (Alternative (..))
import Data.Profunctor (Profunctor (..))
import Data.Bifunctor (Bifunctor(..))
import Data.Semigroup (Last (..), First (..), Product (..), Sum (..), Dual (..), Max (..), Min (..))
-- | Like 'Either', but with an 'Applicative' instance which
-- accumulates errors using their 'Semigroup' operation.
data Validation e a =
Error e
| Success a
deriving (Functor, Eq, Ord, Show, Read, Generic, Typeable)
validation :: (e -> r) -> (a -> r) -> Validation e a -> r
validation f g = \case
Error e -> f e
Success a -> g a
instance Bifunctor Validation where
bimap f g (Error e) = Error (f e)
bimap f g (Success a) = Success (g a)
instance Semigroup e => Applicative (Validation e) where
pure = Success
Success f <*> Success x = Success (f x)
Error e <*> Error e' = Error (e <> e')
Error e <*> _ = Error e
_ <*> Error e' = Error e'
instance Monoid e => Alternative (Validation e) where
empty = Error mempty
Success a <|> x = Success a
Error e <|> x = x
-- | Describes the merging of two values of the same type
-- into some other type. Represented as a 'Maybe' valued
-- function, one can also think of this as a predicate
-- showing which pairs of values can be merged in this way.
--
-- > data Example = Whatever { a :: Int, b :: Maybe Bool }
-- > mergeExamples :: Merge Example Example
-- > mergeExamples = Example <$> required a <*> optional b
newtype Merge e x a = Merge { runMerge :: x -> x -> Validation e a }
-- | Appends some errors. Useful for the combinators provided by this library,
-- which use 'mempty' to provide the default error type.
(.?) :: Semigroup e => Merge e x a -> e -> Merge e x a
m .? e = Merge \x x' -> bimap (e <>) id (runMerge m x x')
-- | Applies an arbitrary function to the error.
(.!) :: Merge e x a -> (e -> e') -> Merge e' x a
m .! f = Merge \x x' -> bimap f id (runMerge m x x')
infixl 6 .?
-- | Flattens a 'Maybe' layer inside of a 'Merge'
flattenValidation :: Merge e x (Validation e a) -> Merge e x a
flattenValidation (Merge f) = Merge \x x' ->
case f x x' of
Error e -> Error e
Success (Error e) -> Error e
Success (Success a) -> Success a
-- | The most general combinator for constructing 'Merge's.
merge :: (x -> x -> Validation e a) -> Merge e x a
merge = Merge
instance Profunctor (Merge e) where
dimap l r (Merge f) = Merge \x x' -> r <$> f (l x) (l x')
instance Functor (Merge e x) where
fmap = rmap
instance Semigroup e => Applicative (Merge e x) where
pure x = Merge (\_ _ -> Success x)
fa <*> a = Merge \x x' -> runMerge fa x x' <*> runMerge a x x'
instance Monoid e => Alternative (Merge e x) where
empty = Merge \_ _ -> empty
Merge f <|> Merge g = Merge \x x' -> f x x' <|> g x x'
instance (Semigroup e, Semigroup a) => Semigroup (Merge e x a) where
a <> b = Merge \x x' -> (<>) <$> runMerge a x x' <*> runMerge b x x'
instance (Monoid e, Semigroup a) => Monoid (Merge e x a) where
mempty = Merge \_ _ -> Error mempty
-- | Meant to be used to merge optional fields in a record.
optional :: (Monoid e, Eq a) => (x -> Maybe a) -> Merge e x (Maybe a)
optional = combineGen (maybe (Optional (Success Nothing)) (Optional . Success . Just)) unOptional
-- | Meant to be used to merge required fields in a record.
required :: forall e a x. (Monoid e, Eq a) => (x -> a) -> Merge e x a
required = combineGen (Required . Success) unRequired
-- | Associatively combine original fields of the record.
combine :: forall e a x. Semigroup a => (x -> a) -> Merge e x a
combine = combineWith (<>)
-- | Combine original fields of the record with the given function.
combineWith :: forall e a x. (a -> a -> a) -> (x -> a) -> Merge e x a
combineWith c f = Merge (\x x' -> go (f x) (f x')) where
go x x' = Success (x `c` x')
-- | Sometimes, one can describe a merge strategy via a binary operator. 'Optional'
-- and 'Required' describe 'optional' and 'required', respectively, in this way.
combineGenWith :: forall e a x s. (s -> s -> s) -> (a -> s) -> (s -> Validation e a) -> (x -> a) -> Merge e x a
combineGenWith c g l f = flattenValidation $ fmap l $ combineWith c (g . f)
-- | 'combineGen' specialized to 'Semigroup' operations.
combineGen :: forall e a x s. Semigroup s => (a -> s) -> (s -> Validation e a) -> (x -> a) -> Merge e x a
combineGen = combineGenWith (<>)
-- | This type's 'Semigroup' instance encodes the simple,
-- discrete lattice generated by any given set, excluding the
-- bottom.
newtype Required e a = Required { unRequired :: Validation e a }
deriving (Eq, Show, Read, Ord, Generic, Typeable)
instance (Monoid e, Eq a) => Semigroup (Required e a) where
Required (Success a) <> Required (Success a')
| a == a' = Required (Success a)
| otherwise = Required (Error mempty)
Required (Error e) <> Required (Success _) = Required (Error e)
Required (Success _) <> Required (Error e') = Required (Error e')
Required (Error e) <> Required (Error e') = Required (Error (e <> e'))
-- | This type's 'Semigroup' instance encodes the simple,
-- deiscrete lattice generated by any given set.
newtype Optional e a = Optional { unOptional :: Validation e (Maybe a) }
deriving (Eq, Show, Read, Ord, Generic, Typeable)
instance (Monoid e, Eq a) => Semigroup (Optional e a) where
Optional (Success (Just a)) <> Optional (Success (Just a'))
| a == a' = Optional (Success (Just a))
| otherwise = Optional (Error mempty)
Optional (Success Nothing) <> x = x
x <> Optional (Success Nothing) = x
Optional (Error e) <> Optional (Error e') = Optional (Error (e <> e'))
x <> Optional (Error e) = Optional (Error e)
Optional (Error e) <> x = Optional (Error e)
instance (Monoid e, Eq a) => Monoid (Optional e a) where
mempty = Optional (Success Nothing)