packages feed

crdt-event-fold 1.5.1.1 → 1.8.0.0

raw patch · 6 files changed

+1615/−1349 lines, 6 filesdep +bytestringdep ~aesondep ~basedep ~containersPVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring

Dependency ranges changed: aeson, base, containers, hspec

API changes (from Hackage documentation)

- Data.CRDT.EventFold: instance (Data.Aeson.Types.ToJSON.ToJSON o, Data.Aeson.Types.ToJSON.ToJSON p, Data.Aeson.Types.ToJSON.ToJSON (Data.CRDT.EventFold.State e), Data.Aeson.Types.ToJSON.ToJSON (f (Data.CRDT.EventFold.Delta p e))) => Data.Aeson.Types.ToJSON.ToJSON (Data.CRDT.EventFold.EventFoldF o p e f)
- Data.CRDT.EventFold: instance (Data.Aeson.Types.ToJSON.ToJSON o, Data.Aeson.Types.ToJSON.ToJSON p, Data.Aeson.Types.ToJSON.ToJSON e, Data.Aeson.Types.ToJSON.ToJSON (Data.CRDT.EventFold.Output e), Data.Aeson.Types.ToJSON.ToJSON (Data.CRDT.EventFold.State e)) => Data.Aeson.Types.ToJSON.ToJSON (Data.CRDT.EventFold.EventFold o p e)
- Data.CRDT.EventFold: instance (Data.Binary.Class.Binary (f (Data.CRDT.EventFold.Delta p e)), Data.Binary.Class.Binary o, Data.Binary.Class.Binary p, Data.Binary.Class.Binary e, Data.Binary.Class.Binary (Data.CRDT.EventFold.State e), Data.Binary.Class.Binary (Data.CRDT.EventFold.Output e)) => Data.Binary.Class.Binary (Data.CRDT.EventFold.EventFoldF o p e f)
- Data.CRDT.EventFold: instance (Data.Binary.Class.Binary o, Data.Binary.Class.Binary p, Data.Binary.Class.Binary e, Data.Binary.Class.Binary (Data.CRDT.EventFold.Output e), Data.Binary.Class.Binary (Data.CRDT.EventFold.State e)) => Data.Binary.Class.Binary (Data.CRDT.EventFold.EventFold o p e)
- Data.CRDT.EventFold: instance (Data.CRDT.EventFold.Event a, Data.CRDT.EventFold.Event b) => Data.CRDT.EventFold.Event (Data.Either.Either a b)
- Data.CRDT.EventFold: instance (GHC.Classes.Eq (f (Data.CRDT.EventFold.Delta p e)), GHC.Classes.Eq (Data.CRDT.EventFold.Output e), GHC.Classes.Eq o, GHC.Classes.Eq p, GHC.Classes.Eq e) => GHC.Classes.Eq (Data.CRDT.EventFold.EventFoldF o p e f)
- Data.CRDT.EventFold: instance (GHC.Classes.Eq o, GHC.Classes.Eq p, GHC.Classes.Eq e, GHC.Classes.Eq (Data.CRDT.EventFold.Output e)) => GHC.Classes.Eq (Data.CRDT.EventFold.EventFold o p e)
- Data.CRDT.EventFold: instance (GHC.Classes.Ord p, Data.Aeson.Types.FromJSON.FromJSON o, Data.Aeson.Types.FromJSON.FromJSON p, Data.Aeson.Types.FromJSON.FromJSON (f (Data.CRDT.EventFold.Delta p e)), Data.Aeson.Types.FromJSON.FromJSON (Data.CRDT.EventFold.State e)) => Data.Aeson.Types.FromJSON.FromJSON (Data.CRDT.EventFold.EventFoldF o p e f)
- Data.CRDT.EventFold: instance (GHC.Show.Show (f (Data.CRDT.EventFold.Delta p e)), GHC.Show.Show o, GHC.Show.Show p, GHC.Show.Show (Data.CRDT.EventFold.State e)) => GHC.Show.Show (Data.CRDT.EventFold.EventFoldF o p e f)
- Data.CRDT.EventFold: instance (GHC.Show.Show o, GHC.Show.Show p, GHC.Show.Show e, GHC.Show.Show (Data.CRDT.EventFold.Output e), GHC.Show.Show (Data.CRDT.EventFold.State e)) => GHC.Show.Show (Data.CRDT.EventFold.EventFold o p e)
- Data.CRDT.EventFold: instance Data.CRDT.EventFold.Event ()
- Data.CRDT.EventFold: instance GHC.Generics.Generic (Data.CRDT.EventFold.EventFoldF o p e f)
- Data.CRDT.EventFold.Monad: instance (GHC.Classes.Eq (Data.CRDT.EventFold.Output e), GHC.Classes.Eq e, GHC.Classes.Eq o, Data.CRDT.EventFold.Event e, GHC.Base.Monad m, GHC.Classes.Ord p) => Data.CRDT.EventFold.Monad.MonadUpdateEF o p e (Data.CRDT.EventFold.Monad.EventFoldT o p e m)
+ Data.CRDT.EventFold: diffMerge_ :: forall o p e. (Eq (Output e), Eq e, Eq o, Event p e, Ord p) => EventFold o p e -> Diff o p e -> Either (MergeError o p e) (UpdateResult o p e)
+ Data.CRDT.EventFold: fullMerge_ :: (Eq (Output e), Eq e, Eq o, Event p e, Ord p) => EventFold o p e -> EventFold o p e -> Either (MergeError o p e) (UpdateResult o p e)
+ Data.CRDT.EventFold: instance (Data.Aeson.Types.ToJSON.ToJSON o, Data.Aeson.Types.ToJSON.ToJSON p, Data.Aeson.Types.ToJSON.ToJSON e, Data.Aeson.Types.ToJSON.ToJSON (Data.CRDT.EventFold.State e), Data.Aeson.Types.ToJSON.ToJSON (Data.CRDT.EventFold.Output e)) => Data.Aeson.Types.ToJSON.ToJSON (Data.CRDT.EventFold.EventFold o p e)
+ Data.CRDT.EventFold: instance (Data.Binary.Class.Binary o, Data.Binary.Class.Binary p, Data.Binary.Class.Binary e, Data.Binary.Class.Binary (Data.CRDT.EventFold.State e), Data.Binary.Class.Binary (Data.CRDT.EventFold.Output e)) => Data.Binary.Class.Binary (Data.CRDT.EventFold.EventFold o p e)
+ Data.CRDT.EventFold: instance (Data.CRDT.EventFold.Event p a, Data.CRDT.EventFold.Event p b) => Data.CRDT.EventFold.Event p (Data.Either.Either a b)
+ Data.CRDT.EventFold: instance (GHC.Classes.Eq (Data.CRDT.EventFold.Output e), GHC.Classes.Eq o, GHC.Classes.Eq p, GHC.Classes.Eq e) => GHC.Classes.Eq (Data.CRDT.EventFold.EventFold o p e)
+ Data.CRDT.EventFold: instance (GHC.Classes.Eq o, GHC.Classes.Eq p, GHC.Classes.Eq e, GHC.Classes.Eq (Data.CRDT.EventFold.Output e)) => GHC.Classes.Eq (Data.CRDT.EventFold.Diff o p e)
+ Data.CRDT.EventFold: instance (GHC.Show.Show (Data.CRDT.EventFold.Output e), GHC.Show.Show (Data.CRDT.EventFold.State e), GHC.Show.Show e, GHC.Show.Show o, GHC.Show.Show p) => GHC.Show.Show (Data.CRDT.EventFold.UpdateResult o p e)
+ Data.CRDT.EventFold: instance (GHC.Show.Show (Data.CRDT.EventFold.Output e), GHC.Show.Show o, GHC.Show.Show p, GHC.Show.Show e, GHC.Show.Show (Data.CRDT.EventFold.State e)) => GHC.Show.Show (Data.CRDT.EventFold.EventFold o p e)
+ Data.CRDT.EventFold: instance Data.CRDT.EventFold.Event p ()
+ Data.CRDT.EventFold: instance GHC.Generics.Generic (Data.CRDT.EventFold.EventFold o p e)
+ Data.CRDT.EventFold: join :: Event p e => p -> State e -> State e
+ Data.CRDT.EventFold: source :: EventId p -> Maybe p
+ Data.CRDT.EventFold: unjoin :: Event p e => p -> State e -> State e
+ Data.CRDT.EventFold.Monad: instance (GHC.Classes.Eq (Data.CRDT.EventFold.Output e), GHC.Classes.Eq e, GHC.Classes.Eq o, Data.CRDT.EventFold.Event p e, GHC.Base.Monad m, GHC.Classes.Ord p) => Data.CRDT.EventFold.Monad.MonadUpdateEF o p e (Data.CRDT.EventFold.Monad.EventFoldT o p e m)
- Data.CRDT.EventFold: acknowledge :: (Eq (Output e), Eq e, Eq o, Event e, Ord p) => p -> EventFold o p e -> UpdateResult o p e
+ Data.CRDT.EventFold: acknowledge :: (Eq (Output e), Eq e, Eq o, Event p e, Ord p) => p -> EventFold o p e -> UpdateResult o p e
- Data.CRDT.EventFold: apply :: Event e => e -> State e -> EventResult e
+ Data.CRDT.EventFold: apply :: Event p e => e -> State e -> EventResult e
- Data.CRDT.EventFold: class Event e where {
+ Data.CRDT.EventFold: class Event p e where {
- Data.CRDT.EventFold: diffMerge :: (Eq (Output e), Eq e, Eq o, Event e, Ord p) => p -> EventFold o p e -> Diff o p e -> Either (MergeError o p e) (UpdateResult o p e)
+ Data.CRDT.EventFold: diffMerge :: (Eq (Output e), Eq e, Eq o, Event p e, Ord p) => p -> EventFold o p e -> Diff o p e -> Either (MergeError o p e) (UpdateResult o p e)
- Data.CRDT.EventFold: disassociate :: forall o p e. (Event e, Ord p) => p -> EventFold o p e -> (EventId p, UpdateResult o p e)
+ Data.CRDT.EventFold: disassociate :: forall o p e. (Event p e, Ord p) => p -> EventFold o p e -> (EventId p, UpdateResult o p e)
- Data.CRDT.EventFold: event :: (Ord p, Event e) => p -> e -> EventFold o p e -> (Output e, EventId p, UpdateResult o p e)
+ Data.CRDT.EventFold: event :: forall o p e. (Event p e, Ord p) => p -> e -> EventFold o p e -> (Output e, EventId p, UpdateResult o p e)
- Data.CRDT.EventFold: events :: Ord p => p -> EventFold o p e -> Diff o p e
+ Data.CRDT.EventFold: events :: forall o p e. Ord p => p -> EventFold o p e -> Maybe (Diff o p e)
- Data.CRDT.EventFold: fullMerge :: (Eq (Output e), Eq e, Eq o, Event e, Ord p) => p -> EventFold o p e -> EventFold o p e -> Either (MergeError o p e) (UpdateResult o p e)
+ Data.CRDT.EventFold: fullMerge :: (Eq (Output e), Eq e, Eq o, Event p e, Ord p) => p -> EventFold o p e -> EventFold o p e -> Either (MergeError o p e) (UpdateResult o p e)
- Data.CRDT.EventFold: new :: (Default (State e), Ord p) => o -> p -> EventFold o p e
+ Data.CRDT.EventFold: new :: forall o p e. (Default (State e), Event p e, Ord p) => o -> p -> EventFold o p e
- Data.CRDT.EventFold: participate :: forall o p e. (Ord p, Event e) => p -> p -> EventFold o p e -> (EventId p, UpdateResult o p e)
+ Data.CRDT.EventFold: participate :: forall o p e. (Ord p, Event p e) => p -> p -> EventFold o p e -> (EventId p, UpdateResult o p e)
- Data.CRDT.EventFold: projectedValue :: Event e => EventFold o p e -> State e
+ Data.CRDT.EventFold: projectedValue :: forall o p e. Event p e => EventFold o p e -> State e

Files

crdt-event-fold.cabal view
@@ -1,6 +1,6 @@ cabal-version:       3.0 name:                crdt-event-fold-version:             1.5.1.1+version:             1.8.0.0 synopsis:            Garbage collected event folding CRDT. description:         Garbage collected event folding CRDT. Consistently                      apply arbitrary operations to replicated data.@@ -16,10 +16,10 @@  common dependencies   build-depends:-    , aeson              >= 1.5.6.0 && < 1.6-    , base               >= 4.14    && < 4.15+    , aeson              >= 2.0.3.0 && < 2.1+    , base               >= 4.15    && < 4.16     , binary             >= 0.8.8.0 && < 0.9-    , containers         >= 0.6.5.1 && < 0.7+    , containers         >= 0.6.4.1 && < 0.7     , data-default-class >= 0.1.2.0 && < 0.2     , data-dword         >= 0.3.2.1 && < 0.4     , exceptions         >= 0.10.4  && < 0.11@@ -27,8 +27,16 @@     , mtl                >= 2.2.2   && < 2.3     , transformers       >= 0.5.6.2 && < 0.6 +common warnings+  ghc-options:+    -Wmissing-deriving-strategies+    -Wmissing-export-lists+    -Wmissing-import-lists+    -Wredundant-constraints+    -Wall+ library-  import: dependencies+  import: dependencies, warnings   exposed-modules:          Data.CRDT.EventFold     Data.CRDT.EventFold.Monad@@ -38,13 +46,34 @@   default-language: Haskell2010  test-suite tests-  import: dependencies+  import: dependencies, warnings   main-is: test.hs   type: exitcode-stdio-1.0   hs-source-dirs: test   default-language: Haskell2010   build-depends:     , crdt-event-fold-    , containers >= 0.6.5.1 && < 0.7-    , hspec      >= 2.7.10 && < 2.8+    , hspec >= 2.8.5 && < 2.9+++benchmark many-outstanding-events+  import: dependencies, warnings+  type: exitcode-stdio-1.0+  main-is: benchmark-many-events.hs+  hs-source-dirs: test+  default-language: Haskell2010+  build-depends:+    , crdt-event-fold+++benchmark serialization+  import: dependencies, warnings+  type: exitcode-stdio-1.0+  main-is: benchmark-serialization.hs+  hs-source-dirs: test+  default-language: Haskell2010+  build-depends:+    , crdt-event-fold+    , bytestring >= 0.10.12.1 && < 0.11+ 
src/Data/CRDT/EventFold.hs view
@@ -1,1290 +1,1445 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS_GHC -Wmissing-deriving-strategies #-}-{-# OPTIONS_GHC -Wmissing-import-lists #-}--{- | Description: Garbage collected event folding CRDT. -}-module Data.CRDT.EventFold (-  -- * Overview-  {- |-    This module provides a CRDT data structure that collects and applies-    operations (called "events") that mutate an underlying data structure.--    It is "Garbage Collected" in the sense that the number of operations-    accumulated in the structure will not grow unbounded, assuming that-    participants manage to sync their data once in a while. The size of-    the data (as measured by the number of operations we have to store)-    is allowed to shrink.--    In addition to mutating the underlying data, each operation can-    also produce an output that can be obtained by the client. The-    output can be either totally consistent across all replicas (which-    is slower), or it can be returned immediately and possibly reflect-    an inconsistent state.-  -}--  -- ** Garbage Collection-  {- |-    Unlike many traditional CRDTs which always grow and never shrink,-    'EventFold' has a mechanism for determining what consensus-    has been reached by all of the participants, which allows us to-    "garbage collect" events that achieved total consensus. Perhaps more-    importantly, this allows us to produce the totally consistent output-    for events for which total consensus has been achieved.--    But there are trade offs. The big downside is that participation in the-    distributed replication of the 'EventFold' must be strictly managed.--    - The process of participating itself involves registering with an-      existing participant, using 'participate'. You can't just send the-      data off to some other computer and expect that now that computer-      is participating in the CRDT. It isn't.-    - Participants can not "restore from backup". Once they have-      incorporated data received from other participants or generated-      new data themselves, and that data has been transmitted to any-      other participant, they are committed to using that result going-      forward. Doing anything that looks like "restoring from an older-      version" would destroy the idea that participants have reached-      consensus on anything, and the results would be undefined and-      almost certainly completely wrong. This library is written with-      some limited capability to detect this situation, but it is not-      always possible to detect it all cases. Many times you will just-      end up with undefined behavior.-  -}--  -- ** A Belabored Analogy-  {- |-    The 'EventFold' name derives from a loose analogy to folding over-    a list of events using plain old 'foldl'. The component parts of-    'foldl' are:--    - A binary operator, analogous to 'apply'.--    - An accumulator value, analogous to 'infimumValue'.--    - A list of values to fold over, loosely analogous to "the list of-      all future calls to 'event'".--    - A return value.  There is no real analogy for the "return value".-      Similarly to how you never actually obtain a return value if you-      try to 'foldl' over an infinite list, 'EventFold's are meant to be-      long-lived objects that accommodate an infinite number of calls-      to 'event'. What you can do is inspect the current value of the-      accumulator using 'infimumValue', or the "projected" value of-      the accumulator using 'projectedValue' (where "projected" means-      "taking into account all of the currently known calls to 'event'-      that have not yet been folded into the accumulator, and which may-      yet turn out to to have other events inserted into the middle or-      beginning of the list").--    The 'EventFold' value itself can be thought of as an intermediate,-    replicated, current state of the fold of an infinite list of events-    that has not yet been fully generated.  So you can, for instance,-    check the current accumulator value.--    In a little more detail, consider the type signature of 'foldl'-    (for lists).--    > foldl-    >   :: (b -> a -> b) -- Analogous to 'apply', where 'a' is your 'Event'-    >                    -- instance, and 'b' is 'State a'.-    >-    >   -> b             -- Loosely analogous to 'infimumValue' where-    >                    -- progressive applications are accumulated.-    >-    >   -> [a]           -- Analogous to all outstanding or future calls to-    >                    -- 'event'.-    >-    >   -> b             -  -}-  -- * Basic API-  -- ** Creating new CRDTs-  new,--  -- ** Adding new events-  event,--  -- ** Coordinating replica updates-  {- |-    Functions in this section are used to help merge foreign copies of-    the CRDT, and transmit our own copy. (This library does not provide-    any kind of transport support, except that all the relevant types-    have 'Binary' instances. Actually arranging for these things to get-    shipped across a wire is left to the user.)--    In principal, the only function you need is 'fullMerge'. Everything-    else in this section is an optimization.  You can ship the full-    'EventFold' value to a remote participant and it can incorporate-    any changes using 'fullMerge', and vice versa. You can receive an-    'EventFold' value from another participant and incorporate its-    changes locally using 'fullMerge'.--    However, if your underlying data structure is large, it may be more-    efficient to just ship a sort of diff containing the information-    that the local participant thinks the remote participant might be-    missing. That is what 'events' and 'diffMerge' are for.-  -}-  fullMerge,-  UpdateResult(..),-  events,-  diffMerge,-  MergeError(..),-  acknowledge,--  -- ** Participation-  participate,-  disassociate,--  -- ** Defining your state and events-  Event(..),-  EventResult(..),--  -- * Inspecting the 'EventFold'-  isBlockedOnError,-  projectedValue,-  infimumValue,-  infimumId,-  infimumParticipants,-  allParticipants,-  projParticipants,-  origin,-  divergent,--  -- * Underlying Types-  EventFold,-  EventId,-  bottomEid,-  Diff,--) where---import Control.Exception (Exception)-import Data.Aeson (FromJSON(parseJSON), ToJSON(toEncoding, toJSON),-  FromJSONKey, ToJSONKey)-import Data.Bifunctor (first)-import Data.Binary (Binary(get, put))-import Data.Default.Class (Default(def))-import Data.Functor.Identity (Identity(Identity), runIdentity)-import Data.Map (Map, keys, toAscList, toDescList, unionWith)-import Data.Maybe (catMaybes)-import Data.Set ((\\), Set, member, union)-import GHC.Generics (Generic)-import Type.Reflection (Typeable)-import qualified Data.DoubleWord as DW-import qualified Data.Map as Map-import qualified Data.Map.Merge.Lazy as Map.Merge-import qualified Data.Set as Set---data EventFoldF o p e f = EventFoldF {-     psOrigin :: o,-    psInfimum :: Infimum (State e) p,-     psEvents :: Map (EventId p) (f (Delta p e), Set p)-  }-  deriving stock (Generic)-deriving anyclass instance (ToJSON o, ToJSON p, ToJSON (State e), ToJSON (f (Delta p e))) => ToJSON (EventFoldF o p e f)-deriving anyclass instance (Ord p, FromJSON o, FromJSON p, FromJSON (f (Delta p e)), FromJSON (State e)) => FromJSON (EventFoldF o p e f)-deriving stock instance-    ( Eq (f (Delta p e))-    , Eq (Output e)-    , Eq o-    , Eq p-    , Eq e-    )-  =>-    Eq (EventFoldF o p e f)-instance-    (-      Binary (f (Delta p e)),-      Binary o,-      Binary p,-      Binary e,-      Binary (State e),-      Binary (Output e)-    )-  =>-    Binary (EventFoldF o p e f)-deriving stock instance-    ( Show (f (Delta p e))-    , Show o-    , Show p-    , Show (State e)-    )-  => Show (EventFoldF o p e f)---{- |-  This type is a-  <https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type CRDT>-  into which participants can add 'Event's that are folded into a-  base 'State'. You can also think of the "events" as operations that-  mutate the base state, and the point of this CRDT is to coordinate-  the application of the operations across all participants so that-  they are applied consistently even if the operations themselves are-  not commutative, idempotent, or monotonic.--  Variables are:--  - @o@ - Origin-  - @p@ - Participant-  - @e@ - Event--  The "Origin" is a value that is more or less meant to identify the-  "thing" being replicated, and in particular identify the historical-  lineage of the 'EventFold'. The idea is that it is meaningless to-  try and merge two 'EventFold's that do not share a common history-  (identified by the origin value) and doing so is a programming error. It-  is only used to try and check for this type of programming error and-  throw an exception if it happens instead of producing undefined (and-  difficult to detect) behavior.--}-newtype EventFold o p e = EventFold { unEventFold :: EventFoldF o p e Identity}-deriving newtype instance (ToJSON o, ToJSON p, ToJSON e, ToJSON (Output e), ToJSON (State e)) => ToJSON (EventFold o p e)-deriving newtype instance (Ord p, FromJSON o, FromJSON p, FromJSON e, FromJSON (Output e), FromJSON (State e)) => FromJSON (EventFold o p e)-deriving stock instance-    (Show o, Show p, Show e, Show (Output e), Show (State e))-  =>-    Show (EventFold o p e)-deriving newtype instance-    (Binary o, Binary p, Binary e, Binary (Output e), Binary (State e))-  =>-    Binary (EventFold o p e)-deriving newtype instance-    (Eq o, Eq p, Eq e, Eq (Output e))-  =>-    Eq (EventFold o p e)---{- |-  `Infimum` is the infimum, or greatest lower bound, of the possible-  values of @s@.--}-data Infimum s p = Infimum {-         eventId :: EventId p,-    participants :: Set p,-      stateValue :: s-  }-  deriving stock (Generic, Show)-  deriving anyclass (ToJSON, FromJSON)-instance (Binary s, Binary p) => Binary (Infimum s p)-instance (Eq p) => Eq (Infimum s p) where-  Infimum s1 _ _ == Infimum s2 _ _ = s1 == s2-instance (Ord p) => Ord (Infimum s p) where-  compare (Infimum s1 _ _) (Infimum s2 _ _) = compare s1 s2---{- |-  `EventId` is a monotonically increasing, totally ordered identification-  value which allows us to lend the attribute of monotonicity to event-  application operations which would not naturally be monotonic.--}-data EventId p-  = BottomEid-  | Eid Word256 p-  deriving stock (Generic, Eq, Ord, Show)-  deriving anyclass (ToJSON, FromJSON, ToJSONKey, FromJSONKey, Binary)-instance Default (EventId p) where-  def = BottomEid---{- | Newtype around 'DW.Word256' to supply typeclass instances. -}-newtype Word256 = Word256 {-    unWord256 :: DW.Word256-  }-  deriving stock (Generic)-  deriving newtype (Eq, Ord, Show, Enum, Num)-instance FromJSON Word256 where-  parseJSON v = do-    (a, b, c, d) <- parseJSON v-    pure (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d)))-instance ToJSON Word256 where-  toJSON (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d))) =-    toJSON (a, b, c, d)-  toEncoding (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d))) =-    toEncoding (a, b, c, d)-instance Binary Word256 where-  put (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d))) =-    put (a, b, c, d)-  get = do-    (a, b, c, d) <- get-    pure (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d)))---{- |-  This is the exception type for illegal merges. These errors indicate-  serious programming bugs.--}-data MergeError o p e-  = DifferentOrigins o o-    {- ^-      The 'EventFold's do not have the same origin. It makes no sense-      to merge 'EventFold's that have different origins because they-      do not share a common history.-    -}-  | DiffTooNew (EventFold o p e) (Diff o p e)-    {- ^-      The `Diff`'s infimum is greater than any event known to 'EventFold'-      into which it is being merged. This should be impossible and-      indicates that either the local 'EventFold' has rolled back an-      event that it had previously acknowledged, or else the source of-      the 'Diff' moved the infimum forward without a full acknowledgement-      from all participants. Both of these conditions should be regarded-      as serious bugs.-    -}-  | DiffTooSparse (EventFold o p e) (Diff o p e)-    {- ^-      The 'Diff' assumes we know about events that we do not in fact know-      about. This is only possible if we rolled back our copy of the state-      somehow and "forgot" about state that we had previous acknowledged,-      or else some other participant erroneously acknowledged some events-      on our behalf.-    -}-  deriving stock (Generic)-deriving anyclass instance (Ord p, FromJSON o, FromJSON p, FromJSON e, FromJSON (State e), FromJSON (Output e)) => FromJSON (MergeError o p e)-deriving anyclass instance (ToJSON o, ToJSON p, ToJSON e, ToJSON (Output e), ToJSON (State e)) => ToJSON (MergeError o p e)-deriving stock instance-    ( Show (Output e)-    , Show o-    , Show p-    , Show e-    , Show (State e)-    )-  =>-    Show (MergeError o p e)-instance (Typeable o, Typeable p, Typeable e, Show (Output e), Show o, Show p, Show e, Show (State e)) => Exception (MergeError o p e)---{- | `Delta` is how we represent mutations to the event fold state. -}-data Delta p e-  = Join p-  | UnJoin p-  | Event e-  | Error (Output e) (Set p)-  deriving stock (Generic)-deriving anyclass instance (ToJSON p, ToJSON e, ToJSON (Output e)) => ToJSON (Delta p e)-deriving anyclass instance (Ord p, FromJSON p, FromJSON e, FromJSON (Output e)) => (FromJSON (Delta p e))-deriving stock instance (Eq p, Eq e, Eq (Output e)) => Eq (Delta p e)-deriving stock instance (Show p, Show e, Show (Output e)) => Show (Delta p e)-instance (Binary p, Binary e, Binary (Output e)) => Binary (Delta p e)---{- |-  Instances of this class define the particular "events" being "folded"-  over in a distributed fashion. In addition to the event type itself,-  there are a couple of type families which define the 'State' into which-  folded events are accumulated, and the 'Output' which application of-  a particular event can generate.--  TL;DR: This is how users define their own custom operations.--}-class Event e where-  type Output e-  type State e-  {- | Apply an event to a state value. **This function MUST be total!!!** -}-  apply :: e -> State e -> EventResult e-{- | The most trivial event type. -}-instance Event () where-  type Output () = ()-  type State () = ()-  apply () () = Pure () ()-{- | The union of two event types. -}-instance (Event a, Event b) => Event (Either a b) where-  type Output (Either a b) = Either (Output a) (Output b)-  type State (Either a b) = (State a, State b)--  apply (Left e) (a, b) = -    case apply e a of-      SystemError o -> SystemError (Left o)-      Pure o s -> Pure (Left o) (s, b)-  apply (Right e) (a, b) = -    case apply e b of-      SystemError o -> SystemError (Right o)-      Pure o s -> Pure (Right o) (a, s)---{- |-  The result of applying an event.--  Morally speaking, events are always pure functions. However, mundane-  issues like finite memory constraints and finite execution time can-  cause referentially opaque behavior. In a normal Haskell program, this-  usually leads to a crash or an exception, and the crash or exception-  can itself, in a way, be thought of as being referentially transparent,-  because there is no way for it to both happen and, simultaneously,-  not happen.--  However, in our case we are replicating computations across many-  different pieces of hardware, so there most definitely is a way-  for these aberrant system failures to both happen and not happen-  simultaneously. What happens if the computation of the event runs out-  of memory on one machine, but not on another?--  There exists a strategy for dealing with these problems: if the-  computation of an event experiences a failure on every participant, then-  the event is pushed into the infimum as a failure (i.e. a no-op), but if-  any single participant successfully computes the event then all other-  participants can (somehow) request a "Full Merge" from the successful-  participant. The Full Merge will include the infimum __value__ computed-  by the successful participant, which will include the successful-  application of the problematic event. The error participants can thus-  bypass computation of the problem event altogether, and can simply-  overwrite their infimum with the infimum provided by the Full Merge.--  Doing a full merge can be much more expensive than doing a simple-  'Diff' merge, because it requires transmitting the full value of the-  'EventFold' instead of just the outstanding operations.--  This type represents how computation of the event finished; with either a-  pure result, or some kind of system error.--  TL;DR:--  In general 'SystemError' is probably only ever useful for when your-  event type somehow executes untrusted code (for instance when your event-  type is a Turing-complete DSL that allows users to submit their own-  custom-programmed "events") and you want to limit the resources that-  can be consumed by such untrusted code.  It is much less useful when-  you are encoding some well defined business logic directly in Haskell.--}-data EventResult e-  = SystemError (Output e)-  | Pure (Output e) (State e)---{- |-  Construct a new 'EventFold' with the given origin and initial-  participant.--}-new-  :: (Default (State e), Ord p)-  => o {- ^ The "origin", identifying the historical lineage of this CRDT. -}-  -> p {- ^ The initial participant. -}-  -> EventFold o p e-new o participant =-  EventFold-    EventFoldF {-        psOrigin = o,-        psInfimum = Infimum {-            eventId = def,-            participants = Set.singleton participant,-            stateValue = def-          },-        psEvents = mempty-      }---{- |-  Get the outstanding events that need to be propagated to a particular-  participant.--}-events :: (Ord p) => p -> EventFold o p e -> Diff o p e-events peer (EventFold ef) =-    Diff {-      diffEvents = omitAcknowledged <$> psEvents ef,-      diffOrigin = psOrigin ef,-      diffInfimum = eventId (psInfimum ef)-    }-  where-    {- |-      Don't send the event data to participants which have already-      acknowledged it, saving network and cpu resources.-    -}-    omitAcknowledged (d, acks) =-      (-        case (d, peer `member` acks) of-          (Identity Error {}, _) -> Just (runIdentity d)-          (_, False) -> Just (runIdentity d)-          _ -> Nothing,-        acks-      )---{- | A package containing events that can be merged into an event fold. -}-data Diff o p e = Diff {-     diffEvents :: Map (EventId p) (Maybe (Delta p e), Set p),-     diffOrigin :: o,-    diffInfimum :: EventId p-  }-  deriving stock (Generic)-deriving anyclass instance (ToJSON o, ToJSON p, ToJSON e, ToJSON (Output e)) => ToJSON (Diff o p e)-deriving anyclass instance (Ord p, FromJSON o, FromJSON p, FromJSON e, FromJSON (Output e)) => FromJSON (Diff o p e)-deriving stock instance (-    Show o, Show p, Show e, Show (Output e)-  ) =>-    Show (Diff o p e)-instance (-    Binary o, Binary p, Binary e, Binary (Output e)-  ) =>-    Binary (Diff o p e)---{- |-  Like 'fullMerge', but merge a remote 'Diff' instead of a full remote-  'EventFold'.--}-diffMerge-  :: ( Eq (Output e)-     , Eq e-     , Eq o-     , Event e-     , Ord p-     )-  => p {- ^ The "local" participant doing the merge. -}-  -> EventFold o p e {- ^ The local copy of the 'EventFold'. -}-  -> Diff o p e {- ^ The 'Diff' provided by the remote participant. -}-  -> Either-       (MergeError o p e)-       (UpdateResult o p e)--diffMerge-    _-    (EventFold EventFoldF {psOrigin = o1})-    Diff {diffOrigin = o2}-  | o1 /= o2 =-    Left (DifferentOrigins o1 o2)--diffMerge _ ef pak | tooNew =-    Left (DiffTooNew ef pak)-  where-    maxState =-      maximum-      . Set.insert (eventId . psInfimum . unEventFold $ ef)-      . Map.keysSet-      . psEvents-      . unEventFold-      $ ef--    tooNew :: Bool-    tooNew = maxState < diffInfimum pak--diffMerge-    participant-    orig@(EventFold (EventFoldF o infimum d1))-    ep@(Diff d2 _ i2)-  =-    case-      reduce-        i2-        EventFoldF {-          psOrigin = o,-          psInfimum = infimum,-          psEvents =-            Map.Merge.merge-              (Map.Merge.mapMissing (const (first Just)))-              Map.Merge.preserveMissing-              (Map.Merge.zipWithMatched (const mergeAcks))-              (first runIdentity <$> d1)-              d2-        }-    of-      Nothing -> Left (DiffTooSparse orig ep)-      Just (ef1, outputs1) ->-        let (ef2, outputs2) = acknowledge_ participant ef1-        in-          Right (-            UpdateResult-              (EventFold ef2)-              (Map.union outputs1 outputs2)-              (-                i2 /= eventId infimum-                || not (Map.null d2)-                || ef2 /= unEventFold orig-              )-          )-  where-    mergeAcks :: (Ord p)-      => (Delta p e, Set p)-      -> (Maybe (Delta p e), Set p)-      -> (Maybe (Delta p e), Set p)-    mergeAcks-        (Error output eacks1, acks1)-        (Just (Error _ eacks2), acks2)-      =-        (Just (Error output (eacks1 `union` eacks2)), acks1 `union` acks2)-    mergeAcks-        (Error {}, acks1)-        (d, acks2)-      =-        (d, acks1 `union` acks2)-    mergeAcks-        (d, acks1)-        (Just _, acks2)-      =-        (Just d, acks1 `union` acks2)-    mergeAcks-        (d, acks1)-        (Nothing, acks2)-      =-        (Just d, acks1 `union` acks2)---{- |-  Monotonically merge the information in two 'EventFold's.  The resulting-  'EventFold' may have a higher infimum value, but it will never have a-  lower one (where "higher" and "lower" are measured by 'infimumId' value,-  not the value of the underlying data structure). Only 'EventFold's-  that originated from the same 'new' call can be merged. If the origins-  are mismatched, or if there is some other programming error detected,-  then an error will be returned.--  Returns the new 'EventFold' value, along with the output for all of-  the events that can now be considered "fully consistent".--}-fullMerge-  :: ( Eq (Output e)-     , Eq e-     , Eq o-     , Event e-     , Ord p-     )-  => p {- ^ The "local" participant doing the merge. -}-  -> EventFold o p e {- ^ The local copy of the 'EventFold'. -}-  -> EventFold o p e {- ^ The remote copy of the 'Eventfold'. -}-  -> Either (MergeError o p e) (UpdateResult o p e)-fullMerge participant (EventFold left) (EventFold right@(EventFoldF o2 i2 d2)) =-  case-    diffMerge-      participant-      (-        EventFold-          left {-            psInfimum = max (psInfimum left) i2-          }-      )-      Diff {-        diffOrigin = o2,-        diffEvents = first (Just . runIdentity) <$> d2,-        diffInfimum = eventId i2-      }-  of-    Left err -> Left err-    Right (UpdateResult ef outputs _prop) ->-      let (ef2, outputs2) = acknowledge_ participant (unEventFold ef)-      in-        Right (-          UpdateResult-            (EventFold ef2)-            (Map.union outputs outputs2)-            (ef2 /= left || ef2 /= right)-        )---{- |-  The result updating the 'EventFold', which contains:--  - The new 'EventFold' value,-  - The outputs of events that have reached the infimum as a result of-    the update (i.e. "totally consistent outputs"),-  - And a flag indicating whether the other participants need to hear-    about the changes.--}-data UpdateResult o p e =-    UpdateResult {-             urEventFold :: EventFold o p e,-                            {- ^ The new 'EventFold' value -}-               urOutputs :: Map (EventId p) (Output e),-                            {- ^-                              Any consistent outputs resulting from-                              the update.-                            -}-      urNeedsPropagation :: Bool-                            {- ^-                              'True' if any new information was added to-                              the 'EventFold' which might need propagating-                              to other participants.-                            -}-    }---{- |-  Record the fact that the participant acknowledges the information-  contained in the 'EventFold'. The implication is that the participant-  __must__ base all future operations on the result of this function.--  Returns the new 'EventFold' value, along with the output for all of-  the events that can now be considered "fully consistent".--}-acknowledge-  :: ( Eq (Output e)-     , Eq e-     , Eq o-     , Event e-     , Ord p-     )-  => p-  -> EventFold o p e-  -> UpdateResult o p e-acknowledge p (EventFold ef) =-  let (ef2, outputs) = acknowledge_ p ef-  in-    UpdateResult {-      urEventFold = EventFold ef2,-      urOutputs = outputs,-      urNeedsPropagation = ef /= ef2-    }---{- | Internal version of 'acknowledge'. -}-acknowledge_ :: (Event e, Ord p)-  => p-  -> EventFoldF o p e Identity-  -> (EventFoldF o p e Identity, Map (EventId p) (Output e))-acknowledge_ p ef =-    {--      First do a normal reduction, then do a special acknowledgement of the-      reduction error, if any.-    -}-    let-      (ps2, outputs) =-        runIdentity $-          reduce-            (eventId (psInfimum ef))-            ef {psEvents = fmap ackOne (psEvents ef)}-      (ps3, outputs2) = ackErr p ps2-    in-      (ps3, outputs <> outputs2)-  where-    ackOne (e, acks) = (e, Set.insert p acks)---{- | Acknowledge the reduction error, if one exists. -}-ackErr :: (Event e, Ord p)-  => p-  -> EventFoldF o p e Identity-  -> (EventFoldF o p e Identity, Map (EventId p) (Output e))-ackErr p ef =-  runIdentity $-    reduce-      (eventId (psInfimum ef))-      ef {-        psEvents =-          case Map.minViewWithKey (psEvents ef) of-            Just ((eid, (Identity (Error o eacks), acks)), deltas) ->-              Map.insert-                eid-                (Identity (Error o (Set.insert p eacks)), acks)-                deltas-            _ -> psEvents ef-      }---{- |-  Allow a participant to join in the distributed nature of the-  'EventFold'. Return the 'EventId' at which the participation is-  recorded, and the resulting 'EventFold'. The purpose of returning the-  'EventId' is so that you can use it to tell when the participation-  event has reached the infimum. See also: 'infimumId'--}-participate :: forall o p e. (Ord p, Event e)-  => p {- ^ The local participant. -}-  -> p {- ^ The participant being added. -}-  -> EventFold o p e-  -> (EventId p, UpdateResult o p e)-participate self peer (EventFold ef) =-    (-      eid,-      let-        (ef2, outputs) =-          acknowledge_-            self-            ef {-              psEvents =-                Map.insert-                  eid-                  (Identity (Join peer), mempty)-                  (psEvents ef)-            }-      in-        UpdateResult {-          urEventFold = EventFold ef2,-          urOutputs = outputs,-          {- -            By definition, we have added some new information that-            needs propagating.-          -}-          urNeedsPropagation = True-        }-    )-  where-    eid :: EventId p-    eid = nextId self ef---{- |-  Indicate that a participant is removing itself from participating in-  the distributed 'EventFold'.--}-disassociate :: forall o p e. (Event e, Ord p)-  => p {- ^ The peer removing itself from participation. -}-  -> EventFold o p e-  -> (EventId p, UpdateResult o p e)-disassociate peer (EventFold ef) =-    let-      (ef2, outputs) =-        acknowledge_-          peer-          ef {-            psEvents =-              Map.insert-                eid-                (Identity (UnJoin peer), mempty)-                (psEvents ef)-          }-    in-      (-        eid,-        UpdateResult {-          urEventFold = EventFold ef2,-          urOutputs = outputs,-          {- -            By definition, we have added some new information that-            needs propagating.-          -}-          urNeedsPropagation = True-        }-      )-  where-    eid :: EventId p-    eid = nextId peer ef---{- |-  Introduce a change to the EventFold on behalf of the participant.-  Return the new 'EventFold', along with the projected output of the-  event, along with an 'EventId' which can be used to get the fully-  consistent event output at a later time.--}-event :: (Ord p, Event e)-  => p-  -> e-  -> EventFold o p e-  -> (Output e, EventId p, UpdateResult o p e)-event p e ef =-  let-    eid = nextId p (unEventFold ef)-  in-    (-      case apply e (projectedValue ef) of-        Pure output _ -> output-        SystemError output -> output,-      eid,-      let-        (ef2, outputs) =-          acknowledge_-            p-            (-              (unEventFold ef) {-                psEvents =-                  Map.insert-                    eid-                    (Identity (Event e), mempty)-                    (psEvents (unEventFold ef))-              }-            )-      in-        UpdateResult {-          urEventFold = EventFold ef2,-          urOutputs = outputs,-          urNeedsPropagation =-            {--              An event is, by definition, adding information to the 'EventFold',-              and the only time we might not need to propagate this this-              information is if the local participant is the only participant.-            -}-            allParticipants (EventFold ef2) /= Set.singleton p-        }-    )---{- | Return the current projected value of the 'EventFold'. -}-projectedValue :: (Event e) => EventFold o p e -> State e-projectedValue-    (-      EventFold-        EventFoldF {-          psInfimum = Infimum {stateValue},-          psEvents-        }-    )-  =-    foldr-      (\ e s ->-        case apply e s of-          Pure _ newState -> newState-          SystemError _ -> s-      )-      stateValue-      changes-  where-    changes = foldMap getDelta (toDescList psEvents)-    getDelta :: (EventId p, (Identity (Delta p e), Set p)) -> [e]-    getDelta (_, (Identity (Event e), _)) = [e]-    getDelta _ = mempty---{- | Return the current infimum value of the 'EventFold'. -}-infimumValue :: EventFold o p e -> State e-infimumValue (EventFold EventFoldF {psInfimum = Infimum {stateValue}}) =-  stateValue---{- | Return the 'EventId' of the infimum value. -}-infimumId :: EventFold o p e -> EventId p-infimumId = eventId . psInfimum . unEventFold---{- |-  Gets the known participants at the infimum.--}-infimumParticipants :: EventFold o p e -> Set p-infimumParticipants-    (-      EventFold-        EventFoldF {-          psInfimum = Infimum {participants}-        }-    )-  =-    participants---{- |-  Get all known participants. This includes participants that are-  projected for removal.--}-allParticipants :: (Ord p) => EventFold o p e -> Set p-allParticipants-    (-      EventFold-        EventFoldF {-          psInfimum = Infimum {participants},-          psEvents-        }-    )-  =-    foldr updateParticipants participants (toDescList psEvents)-  where-    updateParticipants :: (Ord p)-      => (EventId p, (Identity (Delta p e), Set p))-      -> Set p-      -> Set p-    updateParticipants (_, (Identity (Join p), _)) = Set.insert p-    updateParticipants _ = id---{- |-  Get all the projected participants. This does not include participants that-  are projected for removal.--}-projParticipants :: (Ord p) => EventFold o p e -> Set p-projParticipants-    (-      EventFold-        EventFoldF {-          psInfimum = Infimum {participants},-          psEvents-        }-    )-  =-    foldr updateParticipants participants (toDescList psEvents)-  where-    updateParticipants :: (Ord p)-      => (EventId p, (Identity (Delta p e), Set p))-      -> Set p-      -> Set p-    updateParticipants (_, (Identity (Join p), _)) = Set.insert p-    updateParticipants (_, (Identity (UnJoin p), _)) = Set.delete p-    updateParticipants _ = id---{- |-  Returns the participants that we think might be diverging. In-  this context, a participant is "diverging" if there is an event-  that the participant has not acknowledged but we are expecting it-  to acknowledge. Along with the participant, return the last known-  `EventId` which that participant has acknowledged, or 'BottomEid'-  if the participant has a acknowledged no events, as may be the case-  immediately after the participant joined replication.--}-divergent :: forall o p e. (Ord p) => EventFold o p e -> Map p (EventId p)-divergent-    (-      EventFold-        EventFoldF {-          psInfimum = Infimum {participants, eventId},-          psEvents-        }-    )-  =-    let (byParticipant, maxEid) = eidByParticipant-    in Map.filter (< maxEid) byParticipant--  where-    eidByParticipant :: (Map p (EventId p), EventId p)-    eidByParticipant =-      foldr-        accum-        (Map.fromList [(p, eventId) | p <- Set.toList participants], eventId)-        (-          let flatten (a, (Identity b, c)) = (a, b, c)-          in (flatten <$> toAscList psEvents)-        )--    accum-      :: (EventId p, Delta p e, Set p)-      -> (Map p (EventId p), EventId p)-      -> (Map p (EventId p), EventId p)--    accum (eid, Join p, acks) (acc, maxEid) =-      (-        unionWith-          max-          (Map.insert p BottomEid acc)-          (Map.fromList [(a, eid) | a <- Set.toList acks]),-        max maxEid eid-      )--    accum (eid, _, acks) (acc, maxEid) =-      (-        unionWith-          max-          acc-          (Map.fromList [(a, eid) | a <- Set.toList acks]),-        max maxEid eid-      )---{- | Return the origin value of the 'EventFold'. -}-origin :: EventFold o p e -> o-origin = psOrigin . unEventFold---{- |-  This helper function is responsible for figuring out if the 'EventFold'-  has enough information to derive a new infimum value. In other words,-  this is where garbage collection happens.--}-reduce-  :: forall o p e f.-     ( Event e-     , Monad f-     , Ord p-     )-  => EventId p-     {- ^-       The infimum 'EventId' as known by some node in the cluster. "Some-       node" can be different than "this node" in the case where another-       node advanced the infimum before we did (because it knew about our-       acknowledgement, but we didn't know about its acknowledgement)-       and sent us an 'Diff' with this value of the infimum. In this-       case, this infimum value acts as a universal acknowledgement of-       all events coming before it.-     -}-  -> EventFoldF o p e f-  -> f (EventFoldF o p e Identity, Map (EventId p) (Output e))-reduce-    infState-    ef@EventFoldF {-      psInfimum = infimum@Infimum {participants, stateValue},-      psEvents-    }-  =-    case Map.minViewWithKey psEvents of-      Nothing ->-        pure-          (-            EventFoldF {-              psOrigin = psOrigin ef,-              psInfimum = psInfimum ef,-              psEvents = mempty-            },-            mempty-          )-      Just ((eid, (getUpdate, acks)), newDeltas)-        | eid <= eventId infimum -> {- The event is obsolete. Ignore it. -}-            reduce infState ef {-              psEvents = newDeltas-            }-        | isRenegade eid -> {- This is a renegade event. Ignore it. -}-            reduce infState ef {-              psEvents = newDeltas-            }-        | otherwise -> do-            implicitAcks <- unjoins eid--            update <- getUpdate-            let-              {- |-                Join events must be acknowledged by the joining-                participant before moving into the infimum.-              -}-              joining =-                case update of-                  Join p -> Set.singleton p-                  _ -> mempty-            if-                Set.null (((participants `union` joining) \\ acks) \\ implicitAcks)-                || eid <= infState-              then-                case update of-                  Join p ->-                    reduce infState ef {-                      psInfimum = infimum {-                          eventId = eid,-                          participants = Set.insert p participants-                        },-                      psEvents = newDeltas-                    }-                  UnJoin p ->-                    reduce infState ef {-                      psInfimum = infimum {-                          eventId = eid,-                          participants = Set.delete p participants-                        },-                      psEvents = newDeltas-                    }-                  Error output eacks-                    | Set.null (participants \\ eacks) -> do-                        (ps2, outputs) <--                          reduce infState ef {-                            psInfimum = infimum {-                              eventId = eid-                            }-                          }-                        pure (ps2, Map.insert eid output outputs)-                    | otherwise -> do-                        events_ <- runEvents psEvents-                        pure-                          (-                            EventFoldF {-                              psOrigin = psOrigin ef,-                              psInfimum = psInfimum ef,-                              psEvents = events_-                            },-                            mempty-                          )-                  Event e ->-                    case apply e stateValue of-                      SystemError output -> do-                        events_ <- runEvents newDeltas-                        pure-                          (-                            EventFoldF {-                              psOrigin = psOrigin ef,-                              psInfimum = infimum,-                              psEvents =-                                Map.insert-                                  eid-                                  (Identity (Error output mempty), acks)-                                  events_-                            },-                            mempty-                          )-                      Pure output newState -> do-                        (ps2, outputs) <--                          reduce infState ef {-                            psInfimum = infimum {-                                eventId = eid,-                                stateValue = newState-                              },-                            psEvents = newDeltas-                          }-                        pure (ps2, Map.insert eid output outputs)-              else do-                events_ <- runEvents psEvents-                pure-                  (-                    EventFoldF {-                      psOrigin = psOrigin ef,-                      psInfimum = psInfimum ef,-                      psEvents = events_-                    },-                    mempty-                  )-  where-    {- | Unwrap the events from their monad. -}-    runEvents-      :: Map (EventId p) (f (Delta p e), Set p)-      -> f (Map (EventId p) (Identity (Delta p e), Set p))-    runEvents events_ =-      Map.fromList <$> sequence [-        do-          d <- fd-          pure (eid, (Identity d, acks))-        | (eid, (fd, acks)) <- Map.toList events_-      ]--    {- | Figure out which nodes have upcoming unjoins. -}-    unjoins-      :: EventId p-         {- ^-           The even under consideration, unjoins only after which we-           are interested.-         -}-      -> f (Set p)-    unjoins eid =-      Set.fromList-      . Map.elems-      . Map.filterWithKey (\k _ -> eid <= k)-      <$> unjoinMap--    {- | The static map of unjoins. -}-    unjoinMap :: f (Map (EventId p) p)-    unjoinMap =-      Map.fromList . catMaybes <$> sequence [-          update >>= \case-            UnJoin p -> pure (Just (eid, p))-            _ -> pure Nothing-          | (eid, (update, _acks)) <- Map.toList psEvents-        ]--    {- |-      Renegade events are events that originate from a non-participating-      peer.  This might happen in a network partition situation, where-      the cluster ejected a peer that later reappears on the network,-      broadcasting updates.-    -}-    isRenegade BottomEid = False-    isRenegade (Eid _ p) = not (p `member` participants)---{- |-  A utility function that constructs the next `EventId` on behalf of-  a participant.--}-nextId :: (Ord p) => p -> EventFoldF o p e f -> EventId p-nextId p EventFoldF {psInfimum = Infimum {eventId}, psEvents} =-  case maximum (eventId:keys psEvents) of-    BottomEid -> Eid 0 p-    Eid ord _ -> Eid (succ ord) p---{- |-  Return 'True' if progress on the 'EventFold' is blocked on a-  'SystemError'.--  The implication here is that if the local copy is blocked on a-  'SystemError', it needs to somehow arrange for remote copies to send-  full 'EventFold's, not just 'Diff's. A 'diffMerge' is not sufficient-  to get past the block. Only a 'fullMerge' will suffice.-  -  If your system is not using 'SystemError' or else not using 'Diff's,-  then you don't ever need to worry about this function.--}-isBlockedOnError :: EventFold o p e -> Bool-isBlockedOnError (EventFold ef) =-  case Map.minView (psEvents ef) of-    Just ((Identity (Error _ _), _), _) -> True+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++{- | Description: Garbage collected event folding CRDT. -}+module Data.CRDT.EventFold (+  -- * Overview+  {- |+    This module provides a CRDT data structure that collects and applies+    operations (called "events") that mutate an underlying data structure.++    It is "Garbage Collected" in the sense that the number of operations+    accumulated in the structure will not grow unbounded, assuming that+    participants manage to sync their data once in a while. The size of+    the data (as measured by the number of operations we have to store)+    is allowed to shrink.++    In addition to mutating the underlying data, each operation can+    also produce an output that can be obtained by the client. The+    output can be either totally consistent across all replicas (which+    is slower), or it can be returned immediately and possibly reflect+    an inconsistent state.+  -}++  -- ** Garbage Collection+  {- |+    Unlike many traditional CRDTs which always grow and never shrink,+    'EventFold' has a mechanism for determining what consensus+    has been reached by all of the participants, which allows us to+    "garbage collect" events that achieved total consensus. Perhaps more+    importantly, this allows us to produce the totally consistent output+    for events for which total consensus has been achieved.++    But there are trade offs. The big downside is that participation in the+    distributed replication of the 'EventFold' must be strictly managed.++    - The process of participating itself involves registering with an+      existing participant, using 'participate'. You can't just send the+      data off to some other computer and expect that now that computer+      is participating in the CRDT. It isn't.+    - Participants can not "restore from backup". Once they have+      incorporated data received from other participants or generated+      new data themselves, and that data has been transmitted to any+      other participant, they are committed to using that result going+      forward. Doing anything that looks like "restoring from an older+      version" would destroy the idea that participants have reached+      consensus on anything, and the results would be undefined and+      almost certainly completely wrong. This library is written with+      some limited capability to detect this situation, but it is not+      always possible to detect it all cases. Many times you will just+      end up with undefined behavior.+  -}++  -- ** A Belabored Analogy+  {- |+    The 'EventFold' name derives from a loose analogy to folding over+    a list of events using plain old 'foldl'. The component parts of+    'foldl' are:++    - A binary operator, analogous to 'apply'.++    - An accumulator value, analogous to 'infimumValue'.++    - A list of values to fold over, loosely analogous to "the list of+      all future calls to 'event'".++    - A return value.  There is no real analogy for the "return value".+      Similarly to how you never actually obtain a return value if you+      try to 'foldl' over an infinite list, 'EventFold's are meant to be+      long-lived objects that accommodate an infinite number of calls+      to 'event'. What you can do is inspect the current value of the+      accumulator using 'infimumValue', or the "projected" value of+      the accumulator using 'projectedValue' (where "projected" means+      "taking into account all of the currently known calls to 'event'+      that have not yet been folded into the accumulator, and which may+      yet turn out to to have other events inserted into the middle or+      beginning of the list").++    The 'EventFold' value itself can be thought of as an intermediate,+    replicated, current state of the fold of an infinite list of events+    that has not yet been fully generated.  So you can, for instance,+    check the current accumulator value.++    In a little more detail, consider the type signature of 'foldl'+    (for lists).++    > foldl+    >   :: (b -> a -> b) -- Analogous to 'apply', where 'a' is your 'Event'+    >                    -- instance, and 'b' is 'State a'.+    >+    >   -> b             -- Loosely analogous to 'infimumValue' where+    >                    -- progressive applications are accumulated.+    >+    >   -> [a]           -- Analogous to all outstanding or future calls to+    >                    -- 'event'.+    >+    >   -> b             +  -}+  -- * Basic API+  -- ** Creating new CRDTs+  new,++  -- ** Adding new events+  event,++  -- ** Coordinating replica updates+  {- |+    Functions in this section are used to help merge foreign copies of+    the CRDT, and transmit our own copy. (This library does not provide+    any kind of transport support, except that all the relevant types+    have 'Binary' instances. Actually arranging for these things to get+    shipped across a wire is left to the user.)++    In principal, the only function you need is 'fullMerge'. Everything+    else in this section is an optimization.  You can ship the full+    'EventFold' value to a remote participant and it can incorporate+    any changes using 'fullMerge', and vice versa. You can receive an+    'EventFold' value from another participant and incorporate its+    changes locally using 'fullMerge'.++    However, if your underlying data structure is large, it may be more+    efficient to just ship a sort of diff containing the information+    that the local participant thinks the remote participant might be+    missing. That is what 'events' and 'diffMerge' are for.+  -}+  fullMerge,+  fullMerge_,+  UpdateResult(..),+  events,+  diffMerge,+  diffMerge_,+  MergeError(..),+  acknowledge,++  -- ** Participation+  participate,+  disassociate,++  -- ** Defining your state and events+  Event(..),+  EventResult(..),++  -- * Inspecting the 'EventFold'+  isBlockedOnError,+  projectedValue,+  infimumValue,+  infimumId,+  infimumParticipants,+  allParticipants,+  projParticipants,+  origin,+  divergent,+  source,++  -- * Underlying Types+  EventFold,+  EventId,+  bottomEid,+  Diff,++) where++import Control.Exception (Exception)+import Data.Aeson (FromJSON(parseJSON), ToJSON(toEncoding, toJSON),+  FromJSONKey, ToJSONKey)+import Data.Bifunctor (first)+import Data.Binary (Binary(get, put))+import Data.Default.Class (Default(def))+import Data.Map (Map, toAscList, toDescList, unionWith)+import Data.Set ((\\), Set, member, union)+import GHC.Generics (Generic)+import Type.Reflection (Typeable)+import qualified Data.DoubleWord as DW+import qualified Data.Map as Map+import qualified Data.Map.Merge.Lazy as Map.Merge+import qualified Data.Set as Set++{-# ANN module "HLint: ignore Redundant if" #-}+{-# ANN module "HLint: ignore Use catMaybes" #-}++{- |+  This type is a+  <https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type CRDT>+  into which participants can add 'Event's that are folded into a+  base 'State'. You can also think of the "events" as operations that+  mutate the base state, and the point of this CRDT is to coordinate+  the application of the operations across all participants so that+  they are applied consistently even if the operations themselves are+  not commutative, idempotent, or monotonic.++  Variables are:++  - @o@ - Origin+  - @p@ - Participant+  - @e@ - Event++  The "Origin" is a value that is more or less meant to identify the+  "thing" being replicated, and in particular identify the historical+  lineage of the 'EventFold'. The idea is that it is meaningless to+  try and merge two 'EventFold's that do not share a common history+  (identified by the origin value) and doing so is a programming error. It+  is only used to try and check for this type of programming error and+  throw an exception if it happens instead of producing undefined (and+  difficult to detect) behavior.+-}+data EventFold o p e = EventFold {+     psOrigin :: o,+    psInfimum :: Infimum (State e) p,+     psEvents :: Map (EventId p) (Delta p e, Set p),+    psUnjoins :: Set (EventId p)+                 {- ^+                   The set of events that perform an unjoin with+                   unjoins that have not reached the infimum. This is+                   an optimization so that 'reduce' doesn't have to+                   recompute this every time.+                 -}+  }+  deriving stock (Generic)+deriving anyclass instance (ToJSON o, ToJSON p, ToJSON e, ToJSON (State e), ToJSON (Output e)) => ToJSON (EventFold o p e)+deriving anyclass instance (Ord p, FromJSON o, FromJSON p, FromJSON e, FromJSON (Output e), FromJSON (State e)) => FromJSON (EventFold o p e)+deriving stock instance+    ( Eq (Output e)+    , Eq o+    , Eq p+    , Eq e+    )+  =>+    Eq (EventFold o p e)+instance+    (+      Binary o,+      Binary p,+      Binary e,+      Binary (State e),+      Binary (Output e)+    )+  =>+    Binary (EventFold o p e)+deriving stock instance+    ( Show (Output e)+    , Show o+    , Show p+    , Show e+    , Show (State e)+    )+  => Show (EventFold o p e)+++{- |+  `Infimum` is the infimum, or greatest lower bound, of the possible+  values of @s@.+-}+data Infimum s p = Infimum+  {      eventId :: EventId p+  , participants :: Set p+  ,   stateValue :: s+  }+  deriving stock (Generic, Show)+  deriving anyclass (ToJSON, FromJSON)+instance (Binary s, Binary p) => Binary (Infimum s p)+instance (Eq p) => Eq (Infimum s p) where+  Infimum s1 _ _ == Infimum s2 _ _ = s1 == s2+instance (Ord p) => Ord (Infimum s p) where+  compare (Infimum s1 _ _) (Infimum s2 _ _) = compare s1 s2+++{- |+  `EventId` is a monotonically increasing, totally ordered identification+  value which allows us to lend the attribute of monotonicity to event+  application operations which would not naturally be monotonic.+-}+data EventId p+  = BottomEid+  | Eid Word256 p+  deriving stock (Generic, Eq, Ord, Show)+  deriving anyclass (ToJSON, FromJSON, ToJSONKey, FromJSONKey, Binary)+instance Default (EventId p) where+  def = BottomEid+++{- |+  The participant the created an event, if there is one (which there+  isn't for 'bottomEid').+-}+source :: EventId p -> Maybe p+source = \case+  BottomEid -> Nothing+  Eid _ p -> Just p+++{- | Newtype around 'DW.Word256' to supply typeclass instances. -}+newtype Word256 = Word256 {+    unWord256 :: DW.Word256+  }+  deriving stock (Generic)+  deriving newtype (Eq, Ord, Show, Enum, Num)+instance FromJSON Word256 where+  parseJSON v = do+    (a, b, c, d) <- parseJSON v+    pure (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d)))+instance ToJSON Word256 where+  toJSON (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d))) =+    toJSON (a, b, c, d)+  toEncoding (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d))) =+    toEncoding (a, b, c, d)+instance Binary Word256 where+  put (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d))) =+    put (a, b, c, d)+  get = do+    (a, b, c, d) <- get+    pure (Word256 (DW.Word256 (DW.Word128 a b) (DW.Word128 c d)))+++{- |+  This is the exception type for illegal merges. These errors indicate+  serious programming bugs.+-}+data MergeError o p e+  = DifferentOrigins o o+    {- ^+      The 'EventFold's do not have the same origin. It makes no sense+      to merge 'EventFold's that have different origins because they+      do not share a common history.+    -}+  | DiffTooNew (EventFold o p e) (Diff o p e)+    {- ^+      The `Diff`'s infimum is greater than any event known to 'EventFold'+      into which it is being merged. This should be impossible and+      indicates that either the local 'EventFold' has rolled back an+      event that it had previously acknowledged, or else the source of+      the 'Diff' moved the infimum forward without a full acknowledgement+      from all participants. Both of these conditions should be regarded+      as serious bugs.+    -}+  | DiffTooSparse (EventFold o p e) (Diff o p e)+    {- ^+      The 'Diff' assumes we know about events that we do not in fact know+      about. This is only possible if we rolled back our copy of the state+      somehow and "forgot" about state that we had previous acknowledged,+      or else some other participant erroneously acknowledged some events+      on our behalf.+    -}+  deriving stock (Generic)+deriving anyclass instance (Ord p, FromJSON o, FromJSON p, FromJSON e, FromJSON (State e), FromJSON (Output e)) => FromJSON (MergeError o p e)+deriving anyclass instance (ToJSON o, ToJSON p, ToJSON e, ToJSON (Output e), ToJSON (State e)) => ToJSON (MergeError o p e)+deriving stock instance+    ( Show (Output e)+    , Show o+    , Show p+    , Show e+    , Show (State e)+    )+  =>+    Show (MergeError o p e)+instance (Typeable o, Typeable p, Typeable e, Show (Output e), Show o, Show p, Show e, Show (State e)) => Exception (MergeError o p e)+++{- | `Delta` is how we represent mutations to the event fold state. -}+data Delta p e+  = Join p+  | UnJoin p+  | EventD e+  | Error (Output e) (Set p)+  deriving stock (Generic)+deriving anyclass instance (ToJSON p, ToJSON e, ToJSON (Output e)) => ToJSON (Delta p e)+deriving anyclass instance (Ord p, FromJSON p, FromJSON e, FromJSON (Output e)) => (FromJSON (Delta p e))+deriving stock instance (Eq p, Eq e, Eq (Output e)) => Eq (Delta p e)+deriving stock instance (Show p, Show e, Show (Output e)) => Show (Delta p e)+instance (Binary p, Binary e, Binary (Output e)) => Binary (Delta p e)+++{- |+  Instances of this class define the particular "events" being "folded"+  over in a distributed fashion. In addition to the event type itself,+  there are a couple of type families which define the 'State' into which+  folded events are accumulated, and the 'Output' which application of+  a particular event can generate.++  TL;DR: This is how users define their own custom operations.+-}+class Event p e where+  type Output e+  type State e+  {- | Apply an event to a state value. **This function MUST be total!!!** -}+  apply :: e -> State e -> EventResult e++  join :: p -> State e -> State e+  join _ s = s++  unjoin :: p -> State e -> State e+  unjoin _ s = s+{- | The most trivial event type. -}+instance Event p () where+  type Output () = ()+  type State () = ()+  apply () () = Pure () ()+{- | The union of two event types. -}+instance (Event p a, Event p b) => Event p (Either a b) where+  type Output (Either a b) = Either (Output a) (Output b)+  type State (Either a b) = (State a, State b)++  apply (Left e) (a, b) = +    case apply @p e a of+      SystemError o -> SystemError (Left o)+      Pure o s -> Pure (Left o) (s, b)+  apply (Right e) (a, b) = +    case apply @p e b of+      SystemError o -> SystemError (Right o)+      Pure o s -> Pure (Right o) (a, s)++  join p (a, b) =+    (join @p @a p a, join @p @b p b)+  +  unjoin p (a, b) =+    (unjoin @p @a p a, unjoin @p @b p b)+++{- |+  The result of applying an event.++  Morally speaking, events are always pure functions. However, mundane+  issues like finite memory constraints and finite execution time can+  cause referentially opaque behavior. In a normal Haskell program, this+  usually leads to a crash or an exception, and the crash or exception+  can itself, in a way, be thought of as being referentially transparent,+  because there is no way for it to both happen and, simultaneously,+  not happen.++  However, in our case we are replicating computations across many+  different pieces of hardware, so there most definitely is a way+  for these aberrant system failures to both happen and not happen+  simultaneously. What happens if the computation of the event runs out+  of memory on one machine, but not on another?++  There exists a strategy for dealing with these problems: if the+  computation of an event experiences a failure on every participant, then+  the event is pushed into the infimum as a failure (i.e. a no-op), but if+  any single participant successfully computes the event then all other+  participants can (somehow) request a "Full Merge" from the successful+  participant. The Full Merge will include the infimum __value__ computed+  by the successful participant, which will include the successful+  application of the problematic event. The error participants can thus+  bypass computation of the problem event altogether, and can simply+  overwrite their infimum with the infimum provided by the Full Merge.++  Doing a full merge can be much more expensive than doing a simple+  'Diff' merge, because it requires transmitting the full value of the+  'EventFold' instead of just the outstanding operations.++  This type represents how computation of the event finished; with either a+  pure result, or some kind of system error.++  TL;DR:++  In general 'SystemError' is probably only ever useful for when your+  event type somehow executes untrusted code (for instance when your event+  type is a Turing-complete DSL that allows users to submit their own+  custom-programmed "events") and you want to limit the resources that+  can be consumed by such untrusted code.  It is much less useful when+  you are encoding some well defined business logic directly in Haskell.+-}+data EventResult e+  = SystemError (Output e)+  | Pure (Output e) (State e)+++{- |+  Construct a new 'EventFold' with the given origin and initial+  participant.+-}+new+  :: forall o p e.+     ( Default (State e)+     , Event p e+     , Ord p+     )+  => o {- ^ The "origin", identifying the historical lineage of this CRDT. -}+  -> p {- ^ The initial participant. -}+  -> EventFold o p e+new o participant =+  EventFold {+    psOrigin = o,+    psInfimum = Infimum {+        eventId = def,+        participants = Set.singleton participant,+        stateValue = join @p @e participant def+      },+    psEvents = mempty,+    psUnjoins = mempty+  }+++{- |+  Get the outstanding events that need to be propagated to a particular+  participant.++  It isn't always the case that a less expensive 'diffMerge' is sufficient+  to maintain consistency. For instance, if the initial 'participate'+  for a participant hasn't reached the infimum yet then there is no way+  to guarantee that the target will receive /every/ new event from every+  participant (because an old participant might not even know about the+  new participant, because being part of the infimum is the /definition/+  of all participants knowing a thing).++  If the new participant doesn't receive every event, then it obviously+  can't 'apply' the missing events. Therefore, until it's 'participate'+  event is part of the infimum, it must receive infimum values that have+  the missing events pre-applied by some other participant.+-}+events+  :: forall o p e. (Ord p)+  => p {- ^ The participant to which we are sending the 'Diff'. -}+  -> EventFold o p e {- ^ The EventFold being propagated. -}+  -> Maybe (Diff o p e)+     {- ^+       'Nothing' if the participant must perform a 'fullMerge' in order+       to maintain consistency. 'Just' if a less expensive 'diffMerge'+       will suffice.+     -}+events peer ef =+    if+      diffOk+        (participants (psInfimum ef))+        (snd <$> Map.toAscList (psEvents ef))+    then+      Just+        Diff {+          diffEvents = omitAcknowledged <$> psEvents ef,+          diffOrigin = psOrigin ef,+          diffInfimum = eventId (psInfimum ef),+          diffUnjoins = psUnjoins ef+        }+    else+      Nothing+  where+    {- |+      Return 'True' if it is ok to send a diff. 'False' if a full merge+      must be performed.+    -}+    diffOk :: Set p -> [(Delta p e, Set p)] -> Bool+    diffOk accPeers someEvents =+        if peer `member` accPeers then+          {-+            Even if the target is part of the infimum, we still have+            to make sure the target doesn't have an upcoming 'UnJoin'+            (regardless if it is followed by another 'Join', otherwise+            we would just use 'projParticipants').+          -}+          case someEvents of+            (e, _):more ->+              diffOk (accumulatePeers e) more+            [] -> True+        else+          False+      where+        accumulatePeers :: Delta p e -> Set p+        accumulatePeers = \case+          UnJoin p -> Set.delete p accPeers+          _ -> accPeers++    {- |+      Don't send the event data to participants which have already+      acknowledged it, saving network and cpu resources.+    -}+    omitAcknowledged (d, acks) =+      (+        case (d, peer `member` acks) of+          (Error {}, _) -> Just d+          (_, False) -> Just d+          _ -> Nothing,+        acks+      )+++{- | A package containing events that can be merged into an event fold. -}+data Diff o p e = Diff {+     diffEvents :: Map (EventId p) (Maybe (Delta p e), Set p),+     diffOrigin :: o,+    diffInfimum :: EventId p,+    diffUnjoins :: Set (EventId p)+  }+  deriving stock (Generic)+deriving stock instance (Eq o, Eq p, Eq e, Eq (Output e)) => Eq (Diff o p e)+deriving anyclass instance (ToJSON o, ToJSON p, ToJSON e, ToJSON (Output e)) => ToJSON (Diff o p e)+deriving anyclass instance (Ord p, FromJSON o, FromJSON p, FromJSON e, FromJSON (Output e)) => FromJSON (Diff o p e)+deriving stock instance (+    Show o, Show p, Show e, Show (Output e)+  ) =>+    Show (Diff o p e)+instance (+    Binary o, Binary p, Binary e, Binary (Output e)+  ) =>+    Binary (Diff o p e)+++{- |+  Like 'fullMerge', but merge a remote 'Diff' instead of a full remote+  'EventFold'.+-}+diffMerge+  :: ( Eq (Output e)+     , Eq e+     , Eq o+     , Event p e+     , Ord p+     )+  => p {- ^ The "local" participant doing the merge. -}+  -> EventFold o p e {- ^ The local copy of the 'EventFold'. -}+  -> Diff o p e {- ^ The 'Diff' provided by the remote participant. -}+  -> Either+       (MergeError o p e)+       (UpdateResult o p e)++diffMerge participant orig ep =+  case diffMerge_ orig ep of+    Left err -> Left err+    Right (UpdateResult ef1 outputs1 prop1) ->+      let UpdateResult ef2 outputs2 prop2 = acknowledge participant ef1+      in+        Right (+          UpdateResult+            ef2+            (Map.union outputs1 outputs2)+            (prop1 || prop2)+        )+++{- | Like 'diffMerge', but without automatic acknowledgement. -}+diffMerge_+  :: forall o p e.+     ( Eq (Output e)+     , Eq e+     , Eq o+     , Event p e+     , Ord p+     )+  => EventFold o p e {- ^ The local copy of the 'EventFold'. -}+  -> Diff o p e {- ^ The 'Diff' provided by the remote participant. -}+  -> Either+       (MergeError o p e)+       (UpdateResult o p e)++diffMerge_+    (EventFold {psOrigin = o1})+    Diff {diffOrigin = o2}+  | o1 /= o2 =+    Left (DifferentOrigins o1 o2)++diffMerge_ ef pak | tooNew =+    Left (DiffTooNew ef pak)+  where+    maxState =+      maximum+      . Set.insert (eventId . psInfimum $ ef)+      . Map.keysSet+      . psEvents+      $ ef++    tooNew :: Bool+    tooNew = maxState < diffInfimum pak++diffMerge_+    orig@(EventFold o infimum d1 unjoins)+    diff@(Diff d2 _ i2 diffUnjoins)+  =+    let+      mergedEvents :: Maybe (Map (EventId p) (Delta p e, Set p))+      mergedEvents =+        Map.Merge.mergeA+          Map.Merge.preserveMissing+          (Map.Merge.traverseMaybeMissing includeDiffEvents)+          (Map.Merge.zipWithMatched (const mergeAcks))+          d1+          d2+    in+      case mergedEvents of+        Nothing -> Left (DiffTooSparse orig diff)+        Just events_ ->+          let+            (ef, outputs) =+              reduce+                i2+                EventFold {+                  psOrigin = o,+                  psInfimum = infimum,+                  psEvents = events_,+                  psUnjoins = unjoins `Set.union` diffUnjoins+                }+          in+            Right (+              UpdateResult+                ef+                outputs+                (+                  i2 /= eventId infimum+                  || not (Map.null d2)+                  || ef /= orig+                )+            )+  where+    includeDiffEvents :: EventId p -> (Maybe a, b) -> Maybe (Maybe (a, b))+    includeDiffEvents eid (md, acks) =+      {-+        Don't consider diff events that are behind the infimum of the+        EventFold being updated.+      -}+      if eid <= eventId infimum+        then+          {- Don't incude the key, but also don't fail the merge. -}+          Just Nothing+        else+          case md of+            Nothing ->+              {-+                The diff assumes this replica has knowledge of the event,+                but it does not. Fail the merge.+              -}+              Nothing+            Just d ->+              Just $ Just (d, acks)++    mergeAcks+      :: (Delta p e, Set p)+      -> (Maybe (Delta p e), Set p)+      -> (Delta p e, Set p)+    mergeAcks+        (Error output eacks1, acks1)+        (Just (Error _ eacks2), acks2)+      =+        (Error output (eacks1 `union` eacks2), acks1 `union` acks2)+    mergeAcks+        left@(Error {}, acks1)+        (md, acks2)+      =+        case md of+          Nothing -> left+          Just d ->+            (d, acks1 `union` acks2)+    mergeAcks+        (d, acks1)+        (Just _, acks2)+      =+        (d, acks1 `union` acks2)+    mergeAcks+        (d, acks1)+        (Nothing, acks2)+      =+        (d, acks1 `union` acks2)+++{- |+  Monotonically merge the information in two 'EventFold's.  The resulting+  'EventFold' may have a higher infimum value, but it will never have a+  lower one (where "higher" and "lower" are measured by 'infimumId' value,+  not the value of the underlying data structure). Only 'EventFold's+  that originated from the same 'new' call can be merged. If the origins+  are mismatched, or if there is some other programming error detected,+  then an error will be returned.++  Returns the new 'EventFold' value, along with the output for all of+  the events that can now be considered "fully consistent".+-}+fullMerge+  :: ( Eq (Output e)+     , Eq e+     , Eq o+     , Event p e+     , Ord p+     )+  => p {- ^ The "local" participant doing the merge. -}+  -> EventFold o p e {- ^ The local copy of the 'EventFold'. -}+  -> EventFold o p e {- ^ The remote copy of the 'Eventfold'. -}+  -> Either (MergeError o p e) (UpdateResult o p e)+fullMerge participant left right =+  case fullMerge_ left right of+    Left err -> Left err+    Right (UpdateResult ef1 outputs1 _) ->+      let UpdateResult ef2 outputs2 _ = acknowledge participant ef1+      in+        Right (+          UpdateResult+            ef2+            (Map.union outputs1 outputs2)+            (ef2 /= left || ef2 /= right)+        )+++{- | Like 'fullMerge', but without the automatic acknowlegement.  -}+fullMerge_+  :: ( Eq (Output e)+     , Eq e+     , Eq o+     , Event p e+     , Ord p+     )+  => EventFold o p e {- ^ The local copy of the 'EventFold'. -}+  -> EventFold o p e {- ^ The remote copy of the 'Eventfold'. -}+  -> Either (MergeError o p e) (UpdateResult o p e)+fullMerge_ left right@(EventFold o2 i2 d2 unjoins) =+  case+    diffMerge_+      left {+        psInfimum = max (psInfimum left) i2+      }+      Diff {+        diffOrigin = o2,+        diffEvents = first Just <$> d2,+        diffInfimum = eventId i2,+        diffUnjoins = unjoins+      }+  of+    Left err -> Left err+    Right (UpdateResult ef outputs _prop) ->+      Right (+        UpdateResult+          ef+          outputs+          (ef /= left || ef /= right)+      )+++{- |+  The result updating the 'EventFold', which contains:++  - The new 'EventFold' value,+  - The outputs of events that have reached the infimum as a result of+    the update (i.e. "totally consistent outputs"),+  - And a flag indicating whether the other participants need to hear+    about the changes.+-}+data UpdateResult o p e = UpdateResult+  {        urEventFold :: EventFold o p e+                          {- ^ The new 'EventFold' value -}+  ,          urOutputs :: Map (EventId p) (Output e)+                          {- ^+                            Any consistent outputs resulting from+                            the update.+                          -}+  , urNeedsPropagation :: Bool+                          {- ^+                            'True' if any new information was added to+                            the 'EventFold' which might need propagating+                            to other participants.+                          -}+  }+deriving stock instance+  ( Show (Output e)+  , Show (State e)+  , Show e+  , Show o+  , Show p+  )+  => Show (UpdateResult o p e)+++{- |+  Record the fact that the participant acknowledges the information+  contained in the 'EventFold'. The implication is that the participant+  __must__ base all future operations on the result of this function.++  Returns the new 'EventFold' value, along with the output for all of+  the events that can now be considered "fully consistent".+-}+acknowledge+  :: ( Eq (Output e)+     , Eq e+     , Eq o+     , Event p e+     , Ord p+     )+  => p+  -> EventFold o p e+  -> UpdateResult o p e+acknowledge p ef =+  let (ef2, outputs) = acknowledge_ p ef+  in+    UpdateResult {+      urEventFold = ef2,+      urOutputs = outputs,+      urNeedsPropagation = ef /= ef2+    }+++{- | Internal version of 'acknowledge'. -}+acknowledge_ :: (Event p e, Ord p)+  => p+  -> EventFold o p e+  -> (EventFold o p e, Map (EventId p) (Output e))+acknowledge_ p ef =+    {-+      First do a normal reduction, then do a special acknowledgement of the+      reduction error, if any.+    -}+    let+      (ps2, outputs) =+        reduce+          (eventId (psInfimum ef))+          ef {psEvents = fmap ackOne (psEvents ef)}+      (ps3, outputs2) = ackErr p ps2+    in+      (ps3, outputs <> outputs2)+  where+    ackOne (e, acks) = (e, Set.insert p acks)+++{- | Acknowledge the reduction error, if one exists. -}+ackErr :: (Event p e, Ord p)+  => p+  -> EventFold o p e+  -> (EventFold o p e, Map (EventId p) (Output e))+ackErr p ef =+  case Map.minViewWithKey (psEvents ef) of+    Just ((eid, (Error o eacks, acks)), deltas) ->+      reduce+        (eventId (psInfimum ef))+        ef {+          psEvents =+            Map.insert+              eid+              (Error o (Set.insert p eacks), acks)+              deltas+        }+    _ -> (ef, mempty)+++{- |+  Allow a participant to join in the distributed nature of the+  'EventFold'. Return the 'EventId' at which the participation is+  recorded, and the resulting 'EventFold'. The purpose of returning the+  'EventId' is so that you can use it to tell when the participation+  event has reached the infimum. See also: 'infimumId'+-}+participate :: forall o p e. (Ord p, Event p e)+  => p {- ^ The local participant. -}+  -> p {- ^ The participant being added. -}+  -> EventFold o p e+  -> (EventId p, UpdateResult o p e)+participate self peer ef =+    (+      eid,+      let+        (ef2, outputs) =+          acknowledge_+            self+            ef {+              psEvents =+                Map.insert+                  eid+                  (Join peer, mempty)+                  (psEvents ef)+            }+      in+        UpdateResult {+          urEventFold = ef2,+          urOutputs = outputs,+          {- +            By definition, we have added some new information that+            needs propagating.+          -}+          urNeedsPropagation = True+        }+    )+  where+    eid :: EventId p+    eid = nextId self ef+++{- |+  Indicate that a participant is removing itself from participating in+  the distributed 'EventFold'.+-}+disassociate :: forall o p e. (Event p e, Ord p)+  => p {- ^ The peer removing itself from participation. -}+  -> EventFold o p e+  -> (EventId p, UpdateResult o p e)+disassociate peer ef =+    let+      (ef2, outputs) =+        acknowledge_+          peer+          ef {+            psEvents =+              Map.insert+                eid+                (UnJoin peer, mempty)+                (psEvents ef),+            psUnjoins = Set.insert eid (psUnjoins ef)+          }+    in+      (+        eid,+        UpdateResult {+          urEventFold = ef2,+          urOutputs = outputs,+          {- +            By definition, we have added some new information that+            needs propagating.+          -}+          urNeedsPropagation = True+        }+      )+  where+    eid :: EventId p+    eid = nextId peer ef+++{- |+  Introduce a change to the EventFold on behalf of the participant.+  Return the new 'EventFold', along with the projected output of the+  event, along with an 'EventId' which can be used to get the fully+  consistent event output at a later time.+-}+event+  :: forall o p e.+     ( Event p e+     , Ord p+     )+  => p+  -> e+  -> EventFold o p e+  -> (Output e, EventId p, UpdateResult o p e)+event p e ef =+  let+    eid = nextId p ef+  in+    (+      case apply @p e (projectedValue ef) of+        Pure output _ -> output+        SystemError output -> output,+      eid,+      let+        (ef2, outputs) =+          reduce+            (eventId (psInfimum ef))+            (+              ef {+                psEvents =+                  Map.insert+                    eid+                    (EventD e, Set.singleton p)+                    (psEvents ef)+              }+            )+      in+        UpdateResult {+          urEventFold = ef2,+          urOutputs = outputs,+          urNeedsPropagation =+            {-+              An event is, by definition, adding information to the 'EventFold',+              and the only time we might not need to propagate this this+              information is if the local participant is the only participant.+            -}+            allParticipants ef2 /= Set.singleton p+        }+    )+++{- | Return the current projected value of the 'EventFold'. -}+projectedValue :: forall o p e. (Event p e) => EventFold o p e -> State e+projectedValue+    EventFold {+      psInfimum = Infimum {stateValue},+      psEvents+    }+  =+    foldr+      applyDelta+      stateValue+      changes+  where+    applyDelta :: Delta p e -> State e -> State e+    applyDelta d s =+      case d of+        Join p -> join @p @e p s+        UnJoin p -> unjoin @p @e p s+        EventD e ->+          case apply @p e s of+            Pure _ newState -> newState+            SystemError _ -> s+        Error{} -> s++    changes :: [Delta p e]+    changes = fst . snd <$> toDescList psEvents+++{- | Return the current infimum value of the 'EventFold'. -}+infimumValue :: EventFold o p e -> State e+infimumValue EventFold {psInfimum = Infimum {stateValue}} =+  stateValue+++{- | Return the 'EventId' of the infimum value. -}+infimumId :: EventFold o p e -> EventId p+infimumId = eventId . psInfimum+++{- | Gets the known participants at the infimum. -}+infimumParticipants :: EventFold o p e -> Set p+infimumParticipants+    EventFold {+      psInfimum = Infimum {participants}+    }+  =+    participants+++{- |+  Get all known participants. This includes participants that are+  projected for removal.+-}+allParticipants :: (Ord p) => EventFold o p e -> Set p+allParticipants+    EventFold {+      psInfimum = Infimum {participants},+      psEvents+    }+  =+    foldr updateParticipants participants (toDescList psEvents)+  where+    updateParticipants :: (Ord p)+      => (EventId p, (Delta p e, Set p))+      -> Set p+      -> Set p+    updateParticipants (_, (Join p, _)) = Set.insert p+    updateParticipants _ = id+++{- |+  Get all the projected participants. This does not include participants that+  are projected for removal.+-}+projParticipants :: (Ord p) => EventFold o p e -> Set p+projParticipants+    EventFold {+      psInfimum = Infimum {participants},+      psEvents+    }+  =+    foldr updateParticipants participants (toDescList psEvents)+  where+    updateParticipants :: (Ord p)+      => (EventId p, (Delta p e, Set p))+      -> Set p+      -> Set p+    updateParticipants (_, (Join p, _)) = Set.insert p+    updateParticipants (_, (UnJoin p, _)) = Set.delete p+    updateParticipants _ = id+++{- |+  Returns the participants that we think might be diverging. In+  this context, a participant is "diverging" if there is an event+  that the participant has not acknowledged but we are expecting it+  to acknowledge. Along with the participant, return the last known+  `EventId` which that participant has acknowledged, or 'BottomEid'+  if the participant has a acknowledged no events, as may be the case+  immediately after the participant joined replication.+-}+divergent :: forall o p e. (Ord p) => EventFold o p e -> Map p (EventId p)+divergent+    EventFold {+      psInfimum = Infimum {participants, eventId},+      psEvents+    }+  =+    let (byParticipant, maxEid) = eidByParticipant+    in Map.filter (< maxEid) byParticipant++  where+    eidByParticipant :: (Map p (EventId p), EventId p)+    eidByParticipant =+      foldr+        accum+        (+          Map.fromList [(p, eventId) | p <- Set.toList participants],+          eventId+        )+        (+          let flatten (a, (b, c)) = (a, b, c)+          in (flatten <$> toAscList psEvents)+        )++    accum+      :: (EventId p, Delta p e, Set p)+      -> (Map p (EventId p), EventId p)+      -> (Map p (EventId p), EventId p)++    accum (eid, Join p, acks) (acc, maxEid) =+      (+        unionWith+          max+          (Map.insert p BottomEid acc)+          (Map.fromList [(a, eid) | a <- Set.toList acks]),+        max maxEid eid+      )++    accum (eid, _, acks) (acc, maxEid) =+      (+        unionWith+          max+          acc+          (Map.fromList [(a, eid) | a <- Set.toList acks]),+        max maxEid eid+      )+++{- | Return the origin value of the 'EventFold'. -}+origin :: EventFold o p e -> o+origin = psOrigin+++{- |+  This helper function is responsible for figuring out if the 'EventFold'+  has enough information to derive a new infimum value. In other words,+  this is where garbage collection happens.+-}+reduce+  :: forall o p e.+     ( Event p e+     , Ord p+     )+  => EventId p+     {- ^+       The infimum 'EventId' as known by some node in the cluster. "Some+       node" can be different than "this node" in the case where another+       node advanced the infimum before we did (because it knew about our+       acknowledgement, but we didn't know about its acknowledgement)+       and sent us an 'Diff' with this value of the infimum. In this+       case, this infimum value acts as a universal acknowledgement of+       all events coming before it.+     -}+  -> EventFold o p e+  -> (EventFold o p e, Map (EventId p) (Output e))+reduce+    infState+    baseEF+  =+    go baseEF+  where+    go+      :: EventFold o p e+      -> (EventFold o p e, Map (EventId p) (Output e))+    go+        ef@EventFold+          { psInfimum = infimum@Infimum {participants, stateValue}+          , psEvents+          , psUnjoins+          }+      =+        case Map.minViewWithKey psEvents of+          Nothing ->+            (+              EventFold {+                psOrigin = psOrigin ef,+                psInfimum = psInfimum ef,+                psEvents = mempty,+                psUnjoins = mempty+              },+              mempty+            )+          Just ((eid, (update, acks)), newDeltas)+            | eid <= eventId infimum -> {- The event is obsolete. Ignore it. -}+                go ef {+                  psEvents = newDeltas,+                  psUnjoins = dropObsoleteUnjoins eid psUnjoins+                }+            | isRenegade eid -> {- This is a renegade event. Ignore it. -}+                go ef {+                  psEvents = newDeltas,+                  psUnjoins = dropObsoleteUnjoins eid psUnjoins+                }+            | otherwise ->+                let+                  implicitAcks =+                    Set.fromList+                      [ p | Just p <- source <$> Set.toList psUnjoins ]+                  {- |+                    Join events must be acknowledged by the joining+                    participant before moving into the infimum.+                  -}+                  joining =+                    case update of+                      Join p -> Set.singleton p+                      _ -> mempty+                in+                  if+                      Set.null (((participants `union` joining) \\ acks) \\ implicitAcks)+                      || eid <= infState+                    then+                      {-+                        This branch means to roll the update into the+                        infimum. The @else@ branch means we do not.+                      -}+                      case update of+                        Join p ->+                          go ef {+                            psInfimum = infimum {+                                eventId = eid,+                                participants = Set.insert p participants,+                                stateValue = join @p @e p stateValue+                              },+                            psEvents = newDeltas,+                            psUnjoins = dropObsoleteUnjoins eid psUnjoins+                          }+                        UnJoin p ->+                          go ef {+                            psInfimum = infimum {+                                eventId = eid,+                                participants = Set.delete p participants,+                                stateValue = unjoin @p @e p stateValue+                              },+                            psEvents = newDeltas,+                            psUnjoins = dropObsoleteUnjoins eid psUnjoins+                          }+                        Error output eacks+                          | Set.null (participants \\ eacks) ->+                              let+                                (ps2, outputs) =+                                  go ef {+                                    psInfimum = infimum {+                                      eventId = eid+                                    },+                                    psUnjoins = dropObsoleteUnjoins eid psUnjoins+                                  }+                              in+                                (ps2, Map.insert eid output outputs)+                          | otherwise ->+                              (+                                EventFold {+                                  psOrigin = psOrigin ef,+                                  psInfimum = psInfimum ef,+                                  psEvents,+                                  psUnjoins+                                },+                                mempty+                              )+                        EventD e ->+                          case apply @p e stateValue of+                            SystemError output ->+                              (+                                EventFold {+                                  psOrigin = psOrigin ef,+                                  psInfimum = infimum,+                                  psEvents =+                                    Map.insert+                                      eid+                                      (Error output mempty, acks)+                                      newDeltas,+                                  psUnjoins = dropObsoleteUnjoins eid psUnjoins+                                },+                                mempty+                              )+                            Pure output newState ->+                              let+                                (ps2, outputs) =+                                  go ef {+                                    psInfimum = infimum {+                                        eventId = eid,+                                        stateValue = newState+                                      },+                                    psEvents = newDeltas,+                                    psUnjoins = dropObsoleteUnjoins eid psUnjoins+                                  }+                              in+                                (ps2, Map.insert eid output outputs)+                    else+                      (+                        EventFold {+                          psOrigin = psOrigin ef,+                          psInfimum = psInfimum ef,+                          psEvents,+                          psUnjoins+                        },+                        mempty+                      )++    dropObsoleteUnjoins :: EventId p -> Set (EventId p) -> Set (EventId p)+    dropObsoleteUnjoins newInfimumEid unjoins =+      let (_, gt) = Set.split newInfimumEid unjoins+      in gt++    {- |+      Renegade events are events that originate from a non-participating+      peer.  This might happen in a network partition situation, where+      the cluster ejected a peer that later reappears on the network,+      broadcasting updates.+    -}+    isRenegade BottomEid = False+    isRenegade (Eid _ p) = not (p `member` participants (psInfimum baseEF))+++{- |+  A utility function that constructs the next `EventId` on behalf of+  a participant.+-}+nextId+  :: forall o p e.+     p+  -> EventFold o p e+  -> EventId p+nextId p EventFold {psInfimum = Infimum {eventId}, psEvents} =+  let+    maxEid :: EventId p+    maxEid =+      case Map.maxViewWithKey psEvents of+        Just ((eid, _), _) -> eid+        Nothing -> eventId+  in+    case maxEid of+      BottomEid -> Eid 0 p+      Eid ord _ -> Eid (succ ord) p+++{- |+  Return 'True' if progress on the 'EventFold' is blocked on a+  'SystemError'.++  The implication here is that if the local copy is blocked on a+  'SystemError', it needs to somehow arrange for remote copies to send+  full 'EventFold's, not just 'Diff's. A 'diffMerge' is not sufficient+  to get past the block. Only a 'fullMerge' will suffice.+  +  If your system is not using 'SystemError' or else not using 'Diff's,+  then you don't ever need to worry about this function.+-}+isBlockedOnError :: EventFold o p e -> Bool+isBlockedOnError ef =+  case Map.minView (psEvents ef) of+    Just ((Error _ _, _), _) -> True     _ -> False  
src/Data/CRDT/EventFold/Monad.hs view
@@ -97,7 +97,7 @@     ( Eq (Output e)     , Eq e     , Eq o-    , Event e+    , Event p e     , Monad m     , Ord p     )@@ -169,7 +169,7 @@        of the consistent outputs, and a flag indicating whether the new        'EventFold' value should be propagated to the other participants.      -}-runEventFoldT self ef = do+runEventFoldT self ef =   flip runReaderT self   . flip runStateT (UpdateResult ef mempty False)   . unEventFoldT 
+ test/benchmark-many-events.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE NumericUnderscores #-}++module Main (main) where++import Data.CRDT.EventFold (UpdateResult(UpdateResult, urEventFold),+  acknowledge, new)+import Data.CRDT.EventFold.Monad (MonadUpdateEF(event, participate),+  runEventFoldT)+++main :: IO ()+main = do+  ((), UpdateResult { urEventFold = ef }) <-+    runEventFoldT 'a' (new () 'a') $ do+      _ <- participate 'b'+      sequence_ $ replicate 500_000 (event ())++  let ur = acknowledge 'b' ef+  print ur++
+ test/benchmark-serialization.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE NumericUnderscores #-}++module Main (main) where++import Data.CRDT.EventFold (UpdateResult(UpdateResult, urEventFold), new)+import Data.CRDT.EventFold.Monad (MonadUpdateEF(event, participate),+  runEventFoldT)+import qualified Data.Binary as Binary+import qualified Data.ByteString.Lazy as BSL+++main :: IO ()+main = do+  ((), UpdateResult { urEventFold = ef }) <-+    runEventFoldT 'a' (new () 'a') $ do+      _ <- participate 'b'+      sequence_ $ replicate 15_000 (event ())++  let bytes = Binary.encode ef+  print ("Encoding size", BSL.length bytes)++  let ef2 = Binary.decode bytes+  print ("round trip equality", ef2 == ef)+  print ("decoded value", ef2)++
test/test.hs view
@@ -1,6 +1,8 @@+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}-{-# OPTIONS_GHC -Wmissing-export-lists #-}-{-# OPTIONS_GHC -Wmissing-import-lists #-}++ {-# OPTIONS_GHC -Wno-name-shadowing #-}  {- | crdt-event-fold package tests. -}@@ -13,6 +15,7 @@   EventResult(Pure), UpdateResult(urEventFold, urNeedsPropagation,   urOutputs), EventFold, acknowledge, bottomEid, diffMerge, disassociate,   divergent, event, events, fullMerge, infimumValue, new, participate)+import Data.Maybe (fromJust) import Test.Hspec (describe, hspec, it, shouldBe, shouldNotBe) import qualified Data.Map as Map @@ -20,9 +23,9 @@ data Ops   = Inc   | Dec-  deriving (Eq, Show)+  deriving stock (Eq, Show) -instance Event Ops where+instance Event Char Ops where   type State Ops = Int   type Output Ops = Int   apply Inc state = Pure state (succ state)@@ -47,7 +50,7 @@        let r = acknowledge 'b' b       let b = urEventFold r-      let Right r = diffMerge 'a' a (events 'a' b)+      let Right r = diffMerge 'a' a (fromJust (events 'a' b))       let a = urEventFold r       show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList []}}"       show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList []}}"@@ -60,7 +63,7 @@       {- 'a' decrements. -}       let (o, _, r) = event 'a' Dec a       let a = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (Event Dec),fromList \"a\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (EventD Dec),fromList \"a\"))]}}"       show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList []}}"       o `shouldBe` 0       urOutputs r `shouldBe` mempty@@ -71,7 +74,7 @@       {- 'a' decrements again.  -}       let (o, _, r) = event 'a' Dec a       let a = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (Event Dec),fromList \"a\")),(Eid 2 'a',(Identity (Event Dec),fromList \"a\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (EventD Dec),fromList \"a\")),(Eid 2 'a',(Identity (EventD Dec),fromList \"a\"))]}}"       show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList []}}"       o `shouldBe` negate 1       urOutputs r `shouldBe` mempty@@ -82,8 +85,8 @@       {- 'b' decrements.  -}       let (o, _, r) = event 'b' Dec b       let b = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (Event Dec),fromList \"a\")),(Eid 2 'a',(Identity (Event Dec),fromList \"a\"))]}}"-      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'b',(Identity (Event Dec),fromList \"b\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (EventD Dec),fromList \"a\")),(Eid 2 'a',(Identity (EventD Dec),fromList \"a\"))]}}"+      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'b',(Identity (EventD Dec),fromList \"b\"))]}}"       o `shouldBe` 0       urOutputs r `shouldBe` mempty       urNeedsPropagation r `shouldBe` True@@ -91,19 +94,19 @@       show (divergent b) `shouldBe` "fromList [('a',Eid 0 'a')]"        {- | 'b' sends update to 'a'.  -}-      let Right r = diffMerge 'a' a (events 'a' b)+      let Right r = diffMerge 'a' a (fromJust (events 'a' b))       let a = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (Event Dec),fromList \"a\")),(Eid 1 'b',(Identity (Event Dec),fromList \"ab\")),(Eid 2 'a',(Identity (Event Dec),fromList \"a\"))]}}"-      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'b',(Identity (Event Dec),fromList \"b\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (EventD Dec),fromList \"a\")),(Eid 1 'b',(Identity (EventD Dec),fromList \"ab\")),(Eid 2 'a',(Identity (EventD Dec),fromList \"a\"))]}}"+      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'b',(Identity (EventD Dec),fromList \"b\"))]}}"       urOutputs r `shouldBe` mempty       urNeedsPropagation r `shouldBe` True       show (divergent a) `shouldBe` "fromList [('b',Eid 1 'b')]"       show (divergent b) `shouldBe` "fromList [('a',Eid 0 'a')]"        {- | 'a' sends update to 'b'.  -}-      let Right r = diffMerge 'b' b (events 'b' a)+      let Right r = diffMerge 'b' b (fromJust (events 'b' a))       let b = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (Event Dec),fromList \"a\")),(Eid 1 'b',(Identity (Event Dec),fromList \"ab\")),(Eid 2 'a',(Identity (Event Dec),fromList \"a\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 0 'a', participants = fromList \"ab\", stateValue = 0}, psEvents = fromList [(Eid 1 'a',(Identity (EventD Dec),fromList \"a\")),(Eid 1 'b',(Identity (EventD Dec),fromList \"ab\")),(Eid 2 'a',(Identity (EventD Dec),fromList \"a\"))]}}"       show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList []}}"       show (urOutputs r) `shouldBe` "fromList [(Eid 1 'a',0),(Eid 1 'b',-1),(Eid 2 'a',-2)]"       urNeedsPropagation r `shouldBe` True@@ -111,7 +114,7 @@       show (divergent b) `shouldBe` "fromList []"        {- | 'b' sends update to 'a'.  -}-      let Right r = diffMerge 'a' a (events 'a' b)+      let Right r = diffMerge 'a' a (fromJust (events 'a' b))       let a = urEventFold r       show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList []}}"       show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList []}}"@@ -122,7 +125,7 @@       show a `shouldBe` show b        {- | 'b' sends update to 'a' (again).  -}-      let Right r = diffMerge 'a' a (events 'a' b)+      let Right r = diffMerge 'a' a (fromJust (events 'a' b))       let a = urEventFold r       show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList []}}"       show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList []}}"@@ -135,7 +138,7 @@       {- 'a' increments. -}       let (o, _, r) = event 'a' Inc a       let a = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"a\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"a\"))]}}"       show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList []}}"       o `shouldBe` negate 3       urOutputs r `shouldBe` mempty@@ -146,8 +149,8 @@       {- 'b' increments. -}       let (o, _, r) = event 'b' Inc b       let b = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"a\"))]}}"-      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (Event Inc),fromList \"b\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"a\"))]}}"+      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (EventD Inc),fromList \"b\"))]}}"       o `shouldBe` negate 3       urOutputs r `shouldBe` mempty       urNeedsPropagation r `shouldBe` True@@ -160,9 +163,9 @@       let r = acknowledge 'c' a       let a = urEventFold r       let c = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"-      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (Event Inc),fromList \"b\"))]}}"-      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"+      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (EventD Inc),fromList \"b\"))]}}"+      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"       a `shouldBe` c       urOutputs r `shouldBe` mempty       urNeedsPropagation r `shouldBe` True@@ -173,9 +176,9 @@       {- 'a' increments. -}       let (o, _, r) = event 'a' Inc a       let a = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 5 'a',(Identity (Event Inc),fromList \"a\"))]}}"-      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (Event Inc),fromList \"b\"))]}}"-      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"a\"))]}}"+      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (EventD Inc),fromList \"b\"))]}}"+      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"       o `shouldBe` negate 2       urOutputs r `shouldBe` mempty       urNeedsPropagation r `shouldBe` True@@ -186,9 +189,9 @@       {- 'b' increments. -}       let (o, _, r) = event 'b' Inc b       let b = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 5 'a',(Identity (Event Inc),fromList \"a\"))]}}"-      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (Event Inc),fromList \"b\")),(Eid 4 'b',(Identity (Event Inc),fromList \"b\"))]}}"-      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"a\"))]}}"+      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (EventD Inc),fromList \"b\")),(Eid 4 'b',(Identity (EventD Inc),fromList \"b\"))]}}"+      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"       o `shouldBe` negate 2       urOutputs r `shouldBe` mempty       urNeedsPropagation r `shouldBe` True@@ -197,11 +200,11 @@       show (divergent c) `shouldBe` "fromList [('b',Eid 2 'a')]"        {- | 'b' sends update to 'a'.  -}-      let Right r = diffMerge 'a' a (events 'a' b)+      let Right r = diffMerge 'a' a (fromJust (events 'a' b))       let a = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 3 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"a\"))]}}"-      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (Event Inc),fromList \"b\")),(Eid 4 'b',(Identity (Event Inc),fromList \"b\"))]}}"-      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 3 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"a\"))]}}"+      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'b',(Identity (EventD Inc),fromList \"b\")),(Eid 4 'b',(Identity (EventD Inc),fromList \"b\"))]}}"+      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"       show (urOutputs r) `shouldBe` "fromList []"       urNeedsPropagation r `shouldBe` True       show (divergent a) `shouldBe` "fromList [('b',Eid 4 'b'),('c',Eid 4 'a')]"@@ -210,14 +213,14 @@        do {- A world where c leaves.  -}         let (_eid, r) = disassociate 'c' c-        show (urEventFold r) `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 5 'c',(Identity (UnJoin 'c'),fromList \"c\"))]}}"+        show (urEventFold r) `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 5 'c',(Identity (UnJoin 'c'),fromList \"c\"))]}}"        {- | 'a' sends update to 'b'.  -}-      let Right r = diffMerge 'b' b (events 'b' a)+      let Right r = diffMerge 'b' b (fromJust (events 'b' a))       let b = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 3 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"a\"))]}}"-      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"ab\"))]}}"-      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 3 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\")),(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"a\"))]}}"+      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"ab\"))]}}"+      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"       show (urOutputs r) `shouldBe` "fromList [(Eid 3 'a',-3),(Eid 3 'b',-2)]"       urNeedsPropagation r `shouldBe` True       show (divergent a) `shouldBe` "fromList [('b',Eid 4 'b'),('c',Eid 4 'a')]"@@ -225,11 +228,11 @@       show (divergent c) `shouldBe` "fromList [('b',Eid 2 'a')]"        {- | 'b' sends update to 'a'.  -}-      let Right r = diffMerge 'a' a (events 'a' b)+      let Right r = diffMerge 'a' a (fromJust (events 'a' b))       let a = urEventFold r-      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"ab\"))]}}"-      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"ab\"))]}}"-      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (Event Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"+      show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"ab\"))]}}"+      show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"ab\"))]}}"+      show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 2 'a', participants = fromList \"ab\", stateValue = -3}, psEvents = fromList [(Eid 3 'a',(Identity (EventD Inc),fromList \"ac\")),(Eid 4 'a',(Identity (Join 'c'),fromList \"ac\"))]}}"       show (urOutputs r) `shouldBe` "fromList [(Eid 3 'a',-3),(Eid 3 'b',-2)]"       urNeedsPropagation r `shouldBe` True       show (divergent a) `shouldBe` "fromList [('c',Eid 4 'a')]"@@ -247,10 +250,10 @@           instead of just doing the wrong thing.         -}         {- | 'b' sends update to 'c'.  -}-        let Right r = diffMerge 'c' c (events 'c' b)+        let Right r = diffMerge 'c' c (fromJust (events 'c' b))         let c = urEventFold r-        show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"ab\"))]}}"-        show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"ab\"))]}}"+        show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"ab\"))]}}"+        show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"ab\"))]}}"         show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 0}, psEvents = fromList []}}"         show (urOutputs r) `shouldBe` "fromList [(Eid 3 'a',-3),(Eid 4 'b',-2),(Eid 5 'a',-1)]"         urNeedsPropagation r `shouldBe` True@@ -259,10 +262,10 @@         show (divergent c) `shouldBe` "fromList []"          {- | 'c' sends update to 'a'.  -}-        let Right r = diffMerge 'a' a (events 'a' c)+        let Right r = diffMerge 'a' a (fromJust (events 'a' c))         let a = urEventFold r         show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 1}, psEvents = fromList []}}"-        show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"ab\"))]}}"+        show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"ab\"))]}}"         show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 0}, psEvents = fromList []}}"         show (urOutputs r) `shouldBe` "fromList [(Eid 4 'b',-1),(Eid 5 'a',0)]"         urNeedsPropagation r `shouldBe` True@@ -271,7 +274,7 @@         show (divergent c) `shouldBe` "fromList []"          {- | 'a' sends update to 'b'.  -}-        let Right r = diffMerge 'b' b (events 'b' a)+        let Right r = diffMerge 'b' b (fromJust (events 'b' a))         let b = urEventFold r         show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 1}, psEvents = fromList []}}"         show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 1}, psEvents = fromList []}}"@@ -288,8 +291,8 @@         {- | 'b' sends update to 'c'.  -}         let Right r = fullMerge 'c' c b         let c = urEventFold r-        show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"ab\"))]}}"-        show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"ab\"))]}}"+        show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"ab\"))]}}"+        show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"ab\"))]}}"         show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 1}, psEvents = fromList []}}"         show (urOutputs r) `shouldBe` "fromList [(Eid 4 'b',-1),(Eid 5 'a',0)]"         urNeedsPropagation r `shouldBe` True@@ -298,10 +301,10 @@         show (divergent c) `shouldBe` "fromList []"          {- | 'c' sends update to 'a'.  -}-        let Right r = diffMerge 'a' a (events 'a' c)+        let Right r = diffMerge 'a' a (fromJust (events 'a' c))         let a = urEventFold r         show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 1}, psEvents = fromList []}}"-        show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (Event Inc),fromList \"ab\")),(Eid 5 'a',(Identity (Event Inc),fromList \"ab\"))]}}"+        show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 4 'a', participants = fromList \"abc\", stateValue = -1}, psEvents = fromList [(Eid 4 'b',(Identity (EventD Inc),fromList \"ab\")),(Eid 5 'a',(Identity (EventD Inc),fromList \"ab\"))]}}"         show c `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 1}, psEvents = fromList []}}"         show (urOutputs r) `shouldBe` "fromList [(Eid 4 'b',-1),(Eid 5 'a',0)]"         urNeedsPropagation r `shouldBe` True@@ -310,7 +313,7 @@         show (divergent c) `shouldBe` "fromList []"          {- | 'a' sends update to 'b'.  -}-        let Right r = diffMerge 'b' b (events 'b' a)+        let Right r = diffMerge 'b' b (fromJust (events 'b' a))         let b = urEventFold r         show a `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 1}, psEvents = fromList []}}"         show b `shouldBe` "EventFold {unEventFold = EventFoldF {psOrigin = 0, psInfimum = Infimum {eventId = Eid 5 'a', participants = fromList \"abc\", stateValue = 1}, psEvents = fromList []}}"@@ -336,5 +339,37 @@         c = urEventFold . snd . participate 'a' 'c' $ b         d = urEventFold . acknowledge 'c' $ c       divergent d `shouldBe` Map.fromList [('b', bottomEid)]++  describe "events" $ do+    it "balks on infimum invalidity" $ do+      let+        b =+          let+            a = new 0 'a' :: EventFold Int Char Ops+          in+            urEventFold . snd . participate 'a' 'b' $ a+      events 'b' b `shouldBe` Nothing+      (const () <$> events 'a' b) `shouldBe` Just ()+    it "balkes on unjoin/join invalidity" $ do+      let+        d =+          let+            a = new 0 'a' :: EventFold Int Char Ops+            b = urEventFold . snd . participate 'a' 'b' $ a+            c = urEventFold . snd . disassociate 'b' $ b+          in+            urEventFold . snd . participate 'a' 'b' $ c+      events 'b' d `shouldBe` Nothing+      (const () <$> events 'a' d) `shouldBe` Just ()+    it "succeeds in the usual case" $ do+      let+        c =+          let+            a = new 0 'a' :: EventFold Int Char Ops+            b = urEventFold . snd . participate 'a' 'b' $ a+          in+            urEventFold . acknowledge 'b' $ b+      (const () <$> events 'a' c) `shouldBe` Just ()+      (const () <$> events 'b' c) `shouldBe` Just ()