{-# LANGUAGE FlexibleContexts #-}
-- |This module exports all necessarry data types and functions for
-- expressing and executing commands which allow modifying and
-- querying the state of JSON in local replica
module Data.Crjdt
(
-- * Expressions
iter
, next
, key
, doc
, var
-- * Commands
, Command(..)
, yield
, keys
, values
, insert
, delete
, bind
, (-<)
, assign
, (=:)
-- * Values
, string
, emptyMap
, emptyList
-- * Evaluation and Execution
, Eval.eval
, Eval.execute
-- * Re-exports
, Void
, (.>)
, (&)
-- * Others
, sync
, module Core
) where
import Data.Text as T
import Data.Set (Set)
import Data.Void
import Data.Function
import Data.Bifunctor (bimap)
import Control.Monad.Free (liftF)
import Control.Applicative (liftA2)
import Data.Crjdt.Context as Core
import Data.Crjdt.Types as Core
import qualified Data.Crjdt.Eval as Eval (execute, eval)
import Data.Crjdt.Eval as Core hiding (execute, eval)
import Data.Crjdt.Internal
(.>) :: b -> (b -> a) -> a
(.>) = (&)
infixl 6 .>
-- |'emptyMap' corresponds to {}.
emptyMap :: Val
emptyMap = EmptyObject
-- |'emptyList' corresponds to [].
emptyList :: Val
emptyList = EmptyArray
-- |Apply commands received from other replicas to current replica.
yield :: Command ()
yield = liftF (Yield ())
{-| String literal.
@
'doc' '.>' "planet" '.=' 'string' \"Earth\"
@
-}
string :: Text -> Val
string = StringLit
{-| Starts iterating over the list.
@
do
let planetList = 'doc' '.>' "planet" .> 'iter'
insert \"Earth\" planetList
@
-}
iter :: Expr -> Expr
iter = Iter
{-| Moves to the next element in the list.
-}
next :: Expr -> Expr
next = Next
{-| Get value of given key in given expression.
-}
key :: Key Void -> Expr -> Expr
key = flip GetKey
{-| The root of the JSON object.
-}
doc :: Expr
doc = Doc
-- |Variable.
var :: Text -> Expr
var = Var . Variable
-- |Insert a @Val@ in the list.
insert :: Val -> Expr -> Command ()
insert v e = liftF (InsertAfter e v ())
-- |Delete the list element.
delete :: Expr -> Command ()
delete e = liftF (Delete e ())
-- |Get all keys of object pointed by given 'Expr'.
keys :: Expr -> Command (Set (Key Void))
keys e = liftF (Keys e id)
-- |Get all values of object pointed by given 'Expr'.
values :: Expr -> Command [Val]
values e = liftF (Values e id)
-- |Assign value to the key pointed by given 'Expr'.
assign, (=:) :: Expr -> Val -> Command ()
assign e v = liftF (Assign e v ())
(=:) = assign
infixr 5 =:
-- |Let binding.
bind, (-<) :: Text -> Expr -> Command Expr
bind t e = liftF (Let t e id)
(-<) = bind
-----------------------------------------------------------------------------------------------
-- Utility functions
sync :: (ReplicaId, Command a) -> (ReplicaId, Command b) -> Either EvalError (Eval a, Eval b)
sync (rid1, first) (rid2, second) =
let (rFirst, sFirst) = run rid1 (Eval.execute first)
(rSecond, sSecond) = run rid2 (Eval.execute second)
synced which replica = do
Eval.execute which
addReceivedOps (queue replica)
Eval.execute yield
in case (liftA2 (,) rFirst rSecond) of
Right (a, b) -> pure $ bimap (a <$) (b <$) (synced first sSecond, synced second sFirst)
Left ex -> Left ex