Copyright | (c) Justin Le 2020 |
---|---|
License | BSD3 |
Maintainer | justin@jle.im |
Stability | experimental |
Portability | non-portable |
Safe Haskell | None |
Language | Haskell2010 |
Tools for working with potential branches of piecewise-mutable values.
If Data.Mutable.Parts is for product types, then Data.Mutable.Branches is for sum types.
See https://mutable.jle.im/06-mutable-branches.html for an introduction to this module.
Synopsis
- data MutBranch s b a = MutBranch {}
- thawBranch :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> a -> m (Ref s b)
- freezeBranch :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> m (Maybe a)
- hasBranch :: (PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> m Bool
- hasn'tBranch :: (PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> m Bool
- moveBranch :: (Mutable s b, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> Ref s a -> m ()
- copyBranch :: (Mutable s b, Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> a -> m ()
- cloneBranch :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> m (Maybe (Ref s a))
- unsafeThawBranch :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> a -> m (Ref s b)
- unsafeFreezeBranch :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> m (Maybe a)
- withBranch :: (PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (Ref s a -> m r) -> m (Maybe r)
- withBranch_ :: (PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (Ref s a -> m r) -> m ()
- modifyBranch :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (a -> a) -> m ()
- modifyBranch' :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (a -> a) -> m ()
- updateBranch :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (a -> (a, r)) -> m (Maybe r)
- updateBranch' :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (a -> (a, r)) -> m (Maybe r)
- modifyBranchM :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (a -> m a) -> m ()
- modifyBranchM' :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (a -> m a) -> m ()
- updateBranchM :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (a -> m (a, r)) -> m (Maybe r)
- updateBranchM' :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> (a -> m (a, r)) -> m (Maybe r)
- compMB :: MutBranch s a b -> MutBranch s b c -> MutBranch s a c
- idMB :: MutBranch s a a
- constrMB :: forall ctor s b a. (Ref s b ~ GRef s b, GMutBranchConstructor ctor s (Rep b) a) => CLabel ctor -> MutBranch s b a
- data CLabel (ctor :: Symbol) = CLabel
- class (GMutable s f, Mutable s a) => GMutBranchConstructor (ctor :: Symbol) s f a | ctor f -> a
- type family MapRef s as where ...
- nilMB :: Mutable s a => MutBranch s [a] ()
- consMB :: Mutable s a => MutBranch s [a] (a, [a])
- nothingMB :: Mutable s a => MutBranch s (Maybe a) ()
- justMB :: Mutable s a => MutBranch s (Maybe a) a
- leftMB :: (Mutable s a, Mutable s b) => MutBranch s (Either a b) a
- rightMB :: (Mutable s a, Mutable s b) => MutBranch s (Either a b) b
Documentation
A
represents the information that MutBranch
s b ab
could
potentially be an a
. Similar in spirit to a Prism' b a
.
means that MutBranch
s b aa
is one potential option that b
could be in, or that b
is a sum type and a
is one of the
branches/constructors.
See https://mutable.jle.im/06-mutable-branches.html for an introduction to this module.
If MutPart
is for product types, then MutBranch
is for sum types.
In this case, "branch" means "potential option". For example, the
branches of Either
are Left
and Right
.
The simplest way to make these is by using constrMB
. For instance, to
get the two branches of an Either
:
constrMB #_Left :: MutBranch s (Either a b) a constrMB #_Right :: MutBranch s (Either a b) b
ghci> r <-thawRef
(Left 10) ghci>freezeBranch
(constrMB
#_Left) r Just 10 ghci> freezeBranch (constrMB #_Right) r Nothing
It uses OverloadedLabels, but requires an underscore before the constructor name due to limitations in the extension.
One nice way to use these is with withBranch_
:
ghci> r <-thawRef
(Just 10) ghci>withBranch_
(constrMB #_Just) $ i -> --i
is an Int ref .. modifyRef i (+ 1) ghci>freezeRef
r Just 11
ghci> r <- thawRef Nothing
ghci> withBranch_ (constrMB #_Just) $ i -> -- i
is an Int ref
.. modifyRef i (+ 1)
ghci> freezeRef r
Nothing
Perhaps the most useful usage of this abstraction is for recursive data types.
data List a = Nil | Cons a (List a) deriving Generic instance Mutable s a =>Mutable
s (List a) where type Ref s (List a) =GRef
s (List a)
is now a mutable linked list! Once we make the
GRef
s (List a)MutBranch
for the nil and cons cases:
nilBranch :: MutBranch s (List a) () nilBranch = constrMB #_Nil consBranch :: MutBranch s (List a) (a, List a) consBranch = constrMB #_Cons
Here is a function to check if a linked list is currently empty:
isEmpty :: (PrimMonad m, Mutable s a) => Ref s (List a) -> m Bool isEmpty = hasBranch nilBranch
Here is one to "pop" a mutable linked list, giving us the first value and shifting the rest of the list up.
popStack :: (PrimMonad m, Mutable s a) => Ref s (List a) -> m (Maybe a) popStack r = do c <- projectBranch consBranch r case c of Nothing -> pure Nothing Just (x, xs) -> do moveRef r xs Just $ freezeRef x
And here is a function to concatenate a second linked list to the end of a first one.
concatLists :: (PrimMonad m, Mutable s a) => Ref s (List a) -> Ref s (List a) -> m () concatLists l1 l2 = do c <- projectBranch consBranch l1 case c of Nothing -> moveRef l1 l2 Just (_, xs) -> concatLists xs l2
MutBranch | |
|
thawBranch :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> a -> m (Ref s b) Source #
With a MutBranch
, thaw an a
into a mutable s
on that branch.
ghci> r <-thawBranch
(constrMB
#_Left) 10 ghci>freezeRef
r Left 10
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if is |
-> Ref s b | Structure to read out of |
-> m (Maybe a) |
With a MutBranch
, read out a specific a
branch of an s
, if it exists.
ghci> r <-thawRef
(Left 10) ghci>freezeBranch
(constrMB
#_Left) r Just 10 ghci> freezeBranch (constrMB #_Right) r Nothing
hasBranch :: (PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> m Bool Source #
Check if an s
is currently a certain branch a
.
hasn'tBranch :: (PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> m Bool Source #
Check if an s
is not currently a certain branch a
.
moveBranch :: (Mutable s b, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> Ref s b -> Ref s a -> m () Source #
With a MutBranch
, overwrite an s
as an a
, on that branch.
ghci> r <- thawRef (Left 10) ghci> s <- thawRef 100 ghci> moveBranch (constrMB #_Left) r s ghci> freezeRef r Left 100 ghci> t <- thawRef True ghci> moveBranch (constrMB #_Right) r t ghci> freezeRef r Right True
:: (Mutable s b, Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to write into |
-> a | Value to set |
-> m () |
With a MutBranch
, set s
to have the branch a
.
ghci> r <-thawRef
(Left 10) ghci>copyBranch
(constrMB
#_Left) r 5678 ghci>freezeRef
r Left 5678 ghci> copyBranch (constrMB #_Right) r True ghci> freezeRef r Right True
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to read out of |
-> m (Maybe (Ref s a)) |
With a MutBranch
, attempt to clone out a branch of a mutable
s
, if possible.
ghci> r <- thawRef (Left 10) ghci> s <- cloneBranch (constrMB #_Left) ghci> case s of Just s' -> freezeRef s' 10
ghci> r <- thawRef (Right True) ghci> s <- cloneBranch (constrMB #_Left) ghci> case s of Nothing -> "it was Right" "it was Right"
unsafeThawBranch :: (Mutable s a, PrimMonad m, PrimState m ~ s) => MutBranch s b a -> a -> m (Ref s b) Source #
A non-copying version of thawBranch
that can be more efficient for
types where the mutable representation is the same as the immutable one
(like Vector
).
This is safe as long as you never again use the original pure value, since it can potentially directly mutate it.
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if is |
-> Ref s b | Structure to read out of |
-> m (Maybe a) |
A non-copying version of freezeBranch
that can be more efficient
for types where the mutable representation is the same as the immutable
one (like Vector
).
This is safe as long as you never again modify the mutable reference, since it can potentially directly mutate the frozen value magically.
:: (PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if is |
-> Ref s b | Structure to read out of and write into |
-> (Ref s a -> m r) | Action to perform on the |
-> m (Maybe r) |
With a MutBranch
, if an s
is on the a
branch, perform an action
on the a
reference and overwrite the s
with the modified a
.
Returns the result of the action, if a
was found.
ghci> r <-thawRef
(Just 10) ghci>withBranch_
(constrMB
#_Just) $ i -> --i
is an Int ref ..modifyRef
i (+ 1) ghci>freezeRef
r Just 11
ghci> r <- thawRef Nothing
ghci> withBranch_ (constrMB #_Just) $ i -> -- i
is an Int ref
.. modifyRef i (+ 1)
ghci> freezeRef r
Nothing
:: (PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if is |
-> Ref s b | Structure to read out of and write into |
-> (Ref s a -> m r) | Action to perform on the |
-> m () |
withBranch
, but discarding the returned value.
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to read out of and write into |
-> (a -> a) | Pure function modifying |
-> m () |
With a MutBranch
, run a pure function over a potential branch a
of
s
. If s
is not on that branch, leaves s
unchanged.
ghci> r <-thawRef
(Just 10) ghci>modifyBranch
(constrMB
#_Just) r (+ 1) ghci> freezeRef r Just 11
ghci> r <- thawRef Nothing ghci> modifyBranch (constrMB #_Just) r (+ 1) ghci> freezeRef r Nothing
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to read out of and write into |
-> (a -> a) | Pure function modifying |
-> m () |
modifyBranch
, but forces the result before storing it back in the
reference.
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to read out of and write into |
-> (a -> (a, r)) | |
-> m (Maybe r) |
With a MutBranch
, run a pure function over a potential branch a
of
s
. The function returns the updated a
and also an output value to
observe. If s
is not on that branch, leaves s
unchanged.
ghci> r <-thawRef
(Just 10) ghci>updateBranch
(constrMB
#_Just) r $ i -> (i + 1, show i) Just "10" ghci>freezeRef
r Just 11
ghci> r <- thawRef Nothing ghci> updateBranch (constrMB #_Just) r $ i -> (i + 1, show i) Nothing ghci> freezeRef r Nothing
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to read out of and write into |
-> (a -> (a, r)) | |
-> m (Maybe r) |
updateBranch
, but forces the result before storing it back in the
reference.
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to read out of and write into |
-> (a -> m a) | Monadic function modifying |
-> m () |
modifyBranch
but for a monadic function. Uses copyRef
into the
reference after the action is completed.
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to read out of and write into |
-> (a -> m a) | Monadic function modifying |
-> m () |
modifyBranchM
, but forces the result before storing it back in the
reference.
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to read out of and write into |
-> (a -> m (a, r)) | |
-> m (Maybe r) |
updateBranch
but for a monadic function. Uses copyRef
into the
reference after the action is completed.
:: (Mutable s a, PrimMonad m, PrimState m ~ s) | |
=> MutBranch s b a | How to check if |
-> Ref s b | Structure to read out of and write into |
-> (a -> m (a, r)) | |
-> m (Maybe r) |
updateBranchM
, but forces the result before storing it back in the
reference.
Built-in MutBranch
compMB :: MutBranch s a b -> MutBranch s b c -> MutBranch s a c Source #
Compose two MutBranch
s, to drill down on what is being focused.
idMB :: MutBranch s a a Source #
An identity MutBranch
, treating the item itself as a whole branch.
cloneBranch
will always "match".
Using GHC Generics
constrMB :: forall ctor s b a. (Ref s b ~ GRef s b, GMutBranchConstructor ctor s (Rep b) a) => CLabel ctor -> MutBranch s b a Source #
Create a MutBranch
for any data type with a Generic
instance by
specifying the constructor name using OverloadedLabels
ghci> r <-thawRef
(Left 10) ghci>freezeBranch
(constrMB
#_Left) r Just 10 ghci> freezeBranch (constrMB #_Right) r Nothing
Note that due to limitations in OverloadedLabels, you must prefix the constructor name with an undescore.
There also isn't currently any way to utilize OverloadedLabels with
operator identifiers, so using it with operator constructors (like :
and []
) requires explicit TypeApplications:
-- |MutBranch
focusing on the cons case of a list consMB :: (PrimMonad m, Mutable s a) => MutBranch s [a] (a, [a]) consMB =constrMB
(CLabel
@":")
data CLabel (ctor :: Symbol) Source #
A version of Label
that removes an underscore at
the beginning when used with -XOverloadedLabels. Used to specify
constructors, since labels are currently not able to start with capital
letters.
Instances
ctor_ ~ AppendSymbol "_" ctor => IsLabel ctor_ (CLabel ctor) Source # | |
Defined in Data.Mutable.Branches |
class (GMutable s f, Mutable s a) => GMutBranchConstructor (ctor :: Symbol) s f a | ctor f -> a Source #
Typeclass powering constrMB
using GHC Generics.
Heavily inspired by Data.Generics.Sum.Constructors.
gmbcProj, gmbcEmbed
Instances
(Mutable s a, GMutBranchSum ctor (HasCtorP ctor l) s l r a) => GMutBranchConstructor ctor s (l :+: r) a Source # | |
GMutBranchConstructor ctor m f a => GMutBranchConstructor ctor m (M1 D meta f) a Source # | |
(GMutable s f, Mutable s a, GIsList (GRef_ s f) (GRef_ s f) (MapRef s as) (MapRef s as), GIsList f f as as, ListTuple a a as as, ListRefTuple s b as, Ref s a ~ b) => GMutBranchConstructor ctor s (M1 C ('MetaCons ctor fixity fields) f) a Source # | |
type family MapRef s as where ... Source #
Useful type family to
over every item in a type-level listRef
m
ghci> :kind! MapRef IO '[Int, V.Vector Double] '[ MutVar RealWorld Int, MVector RealWorld Double ]
For common types
consMB :: Mutable s a => MutBranch s [a] (a, [a]) Source #
MutBranch
focusing on the cons case of a list