lorentz-0.4.0: src/Lorentz/UStore/Traversal.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
-- | UStore templates generic traversals.
--
-- Normally you work with functionality of this module as follows:
-- 1. Pick the function fitting most for your traversal, one of
-- 'traverseUStore', 'foldUStore' e.t.c.
-- 2. Create a custom datatype value of which will be put to that function.
-- 3. Implement a respective 'UStoreTemplateTraversable' instance for this
-- datatype.
module Lorentz.UStore.Traversal
( UStoreTraversalWay (..)
, UStoreTraversalFieldHandler (..)
, UStoreTraversalSubmapHandler (..)
, UStoreTraversable
, traverseUStore
, modifyUStore
, foldUStore
, genUStore
) where
import qualified Data.Kind as Kind
import GHC.Generics ((:*:)(..), (:+:))
import qualified GHC.Generics as G
import Lorentz.UStore.Types
import Util.Label
import Util.TypeLits
----------------------------------------------------------------------------
-- Interface
----------------------------------------------------------------------------
-- | Defines general parameters of UStore template traversal.
-- You need a separate @way@ datatype with an instance of this typeclass for each
-- type of traversal.
class ( Applicative (UStoreTraversalArgumentWrapper way)
, Applicative (UStoreTraversalMonad way)
) =>
UStoreTraversalWay (way :: Kind.Type) where
-- | Wrapper which will accompany the existing value of traversed template,
-- aka argument.
-- This is usually @'Identity'@ or @'Const' a@.
type UStoreTraversalArgumentWrapper way :: Kind.Type -> Kind.Type
-- | Additional constraints on monadic action used in traversal.
-- Common ones include 'Identity', @'Const'@, @(,) a@
type UStoreTraversalMonad way :: Kind.Type -> Kind.Type
-- | Declares a handler for UStore fields when given traversal way is applied.
class (UStoreTraversalWay way) =>
UStoreTraversalFieldHandler
(way :: Kind.Type) (marker :: UStoreMarkerType) (v :: Kind.Type) where
-- | How to process each of UStore fields.
ustoreTraversalFieldHandler
:: (KnownUStoreMarker marker)
=> way
-> Label name
-> UStoreTraversalArgumentWrapper way v
-> UStoreTraversalMonad way v
-- | Declares a handler for UStore submaps when given traversal way is applied.
class (UStoreTraversalWay way) =>
UStoreTraversalSubmapHandler
(way :: Kind.Type) (k :: Kind.Type) (v :: Kind.Type) where
-- | How to process each of UStore submaps.
ustoreTraversalSubmapHandler
:: way
-> Label name
-> UStoreTraversalArgumentWrapper way (Map k v)
-> UStoreTraversalMonad way (Map k v)
-- | Constraint for UStore traversal.
type UStoreTraversable way a =
(Generic a, GUStoreTraversable way (G.Rep a), UStoreTraversalWay way)
-- | Perform UStore traversal. The most general way to perform a traversal.
traverseUStore
:: forall way template.
(UStoreTraversable way template)
=> way
-> UStoreTraversalArgumentWrapper way template
-> UStoreTraversalMonad way template
traverseUStore way =
fmap G.to . gTraverseUStore way . fmap G.from
-- | Modify each UStore entry.
modifyUStore
:: ( UStoreTraversable way template
, UStoreTraversalArgumentWrapper way ~ Identity
, UStoreTraversalMonad way ~ Identity
)
=> way
-> template
-> template
modifyUStore way a =
runIdentity $ traverseUStore way (Identity a)
-- | Collect information about UStore entries into monoid.
foldUStore
:: ( UStoreTraversable way template
, UStoreTraversalArgumentWrapper way ~ Identity
, UStoreTraversalMonad way ~ Const res
)
=> way
-> template
-> res
foldUStore way a =
getConst $ traverseUStore way (Identity a)
-- | Fill UStore template with entries.
genUStore
:: ( UStoreTraversable way template
, UStoreTraversalArgumentWrapper way ~ Const ()
)
=> way -> UStoreTraversalMonad way template
genUStore way =
traverseUStore way (Const ())
-- Implementation
----------------------------------------------------------------------------
-- | Generic traversal of UStore template.
class GUStoreTraversable (way :: Kind.Type) (x :: Kind.Type -> Kind.Type) where
gTraverseUStore
:: (UStoreTraversalWay way)
=> way
-> UStoreTraversalArgumentWrapper way (x p)
-> UStoreTraversalMonad way (x p)
instance GUStoreTraversable way x =>
GUStoreTraversable way (G.D1 i x) where
gTraverseUStore way x =
G.M1 <$> gTraverseUStore way (G.unM1 <$> x)
instance GUStoreTraversable way x =>
GUStoreTraversable way (G.C1 i x) where
gTraverseUStore way x =
G.M1 <$> gTraverseUStore way (G.unM1 <$> x)
instance TypeError ('Text "Unexpected sum type in UStore template") =>
GUStoreTraversable way (x :+: y) where
gTraverseUStore _ = error "imposible"
instance TypeError ('Text "Unexpected void-like type in UStore template") =>
GUStoreTraversable way G.V1 where
gTraverseUStore _ = error "impossible"
instance ( GUStoreTraversable way x
, GUStoreTraversable way y
) =>
GUStoreTraversable way (x :*: y) where
gTraverseUStore way a =
(:*:) <$> gTraverseUStore way (a <&> \(x :*: _) -> x)
<*> gTraverseUStore way (a <&> \(_ :*: y) -> y)
instance GUStoreTraversable way G.U1 where
gTraverseUStore _ _ = pure G.U1
instance {-# OVERLAPPABLE #-}
UStoreTraversable way template =>
GUStoreTraversable way (G.S1 i (G.Rec0 template)) where
gTraverseUStore way sub =
G.M1 . G.K1 <$> traverseUStore way (G.unK1 . G.unM1 <$> sub)
instance ( UStoreTraversalFieldHandler way marker v, KnownUStoreMarker marker
, KnownSymbol ctor
) =>
GUStoreTraversable
way
(G.S1 ('G.MetaSel ('Just ctor) _1 _2 _3) (G.Rec0 (UStoreFieldExt marker v))) where
gTraverseUStore way entry =
G.M1 . G.K1 . UStoreField <$>
ustoreTraversalFieldHandler
@_
@marker
way
(Label @ctor)
(entry <&> \(G.M1 (G.K1 (UStoreField v))) -> v)
instance (UStoreTraversalSubmapHandler way k v, KnownSymbol ctor) =>
GUStoreTraversable
way
(G.S1 ('G.MetaSel ('Just ctor) _1 _2 _3) (G.Rec0 (k |~> v))) where
gTraverseUStore way entry =
G.M1 . G.K1 . UStoreSubMap <$>
ustoreTraversalSubmapHandler
way
(Label @ctor)
(entry <&> \(G.M1 (G.K1 (UStoreSubMap m))) -> m)