-- |Applicative sequencing for RPC requests
module Ribosome.Host.Data.RpcCall where

import Ribosome.Host.Class.Msgpack.Decode (MsgpackDecode)
import Ribosome.Host.Data.Request (Request)

type RpcCall :: Type -> Type

-- |A wrapper for 'Request' that allows applicative sequencing of calls for batch processing, used for a declarative
-- representation of the Neovim API.
--
-- Neovim has an API function named @nvim_call_atomic@ that makes it possible to send multiple RPC requests at once,
-- reducing the communcation overhead.
-- Applicative sequences of 'RpcCall's are automatically batched into a single call by 'Ribosome.Rpc'.
--
-- This can be combined neatly with @ApplicativeDo@:
--
-- > import Ribosome
-- > import qualified Ribosome.Api.Data as Api
-- >
-- > sync do
-- >   a :: Int <- Api.nvimGetVar "number1"
-- >   b :: Int <- Api.nvimGetVar "number2"
-- >   pure (a + b)
data RpcCall a where
  RpcCallRequest :: MsgpackDecode a => Request -> RpcCall a
  RpcPure :: a -> RpcCall a
  RpcFmap :: (a -> b) -> RpcCall a -> RpcCall b
  RpcAtomic :: (a -> b -> c) -> RpcCall a -> RpcCall b -> RpcCall c

instance Functor RpcCall where
  fmap :: forall a b. (a -> b) -> RpcCall a -> RpcCall b
fmap a -> b
f = \case
    RpcCallRequest Request
req ->
      (a -> b) -> RpcCall a -> RpcCall b
forall a b. (a -> b) -> RpcCall a -> RpcCall b
RpcFmap a -> b
f (Request -> RpcCall a
forall a. MsgpackDecode a => Request -> RpcCall a
RpcCallRequest Request
req)
    RpcPure a
a ->
      b -> RpcCall b
forall a. a -> RpcCall a
RpcPure (a -> b
f a
a)
    RpcFmap a -> a
g RpcCall a
a ->
      (a -> b) -> RpcCall a -> RpcCall b
forall a b. (a -> b) -> RpcCall a -> RpcCall b
RpcFmap (a -> b
f (a -> b) -> (a -> a) -> a -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> a
g) RpcCall a
a
    RpcAtomic a -> b -> a
g RpcCall a
a RpcCall b
b ->
      (a -> b -> b) -> RpcCall a -> RpcCall b -> RpcCall b
forall a b c. (a -> b -> c) -> RpcCall a -> RpcCall b -> RpcCall c
RpcAtomic (\ a
x b
y -> a -> b
f (a -> b -> a
g a
x b
y)) RpcCall a
a RpcCall b
b

instance Applicative RpcCall where
  pure :: forall a. a -> RpcCall a
pure =
    a -> RpcCall a
forall a. a -> RpcCall a
RpcPure
  liftA2 :: forall a b c. (a -> b -> c) -> RpcCall a -> RpcCall b -> RpcCall c
liftA2 =
    (a -> b -> c) -> RpcCall a -> RpcCall b -> RpcCall c
forall a b c. (a -> b -> c) -> RpcCall a -> RpcCall b -> RpcCall c
RpcAtomic

instance (
    Semigroup a
  ) => Semigroup (RpcCall a) where
    <> :: RpcCall a -> RpcCall a -> RpcCall a
(<>) =
      (a -> a -> a) -> RpcCall a -> RpcCall a -> RpcCall a
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 a -> a -> a
forall a. Semigroup a => a -> a -> a
(<>)

instance (
    Monoid a
  ) => Monoid (RpcCall a) where
    mempty :: RpcCall a
mempty =
      a -> RpcCall a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
forall a. Monoid a => a
mempty