mirror of
https://github.com/zap-proto/haskell.git
synced 2026-10-30 15:05:00 +00:00
133 lines
4.1 KiB
Haskell
133 lines
4.1 KiB
Haskell
{-# LANGUAGE DuplicateRecordFields #-}
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE RecordWildCards #-}
|
|
module Examples.Rpc.CalculatorServer (main) where
|
|
|
|
import Prelude hiding (subtract)
|
|
|
|
import Data.Int
|
|
|
|
import Control.Concurrent.STM (STM, atomically)
|
|
import Control.Monad (when)
|
|
import Network.Simple.TCP (serve)
|
|
import Supervisors (Supervisor)
|
|
|
|
import qualified Data.Vector as V
|
|
|
|
import Capnp (def, defaultLimit)
|
|
import Capnp.Rpc
|
|
( ConnConfig(..)
|
|
, Server
|
|
, handleConn
|
|
, pureHandler
|
|
, socketTransport
|
|
, throwFailed
|
|
, toClient
|
|
, wait
|
|
, (?)
|
|
)
|
|
|
|
import Capnp.Gen.Calculator.Pure
|
|
|
|
newtype LitValue = LitValue Double
|
|
|
|
instance Server IO LitValue
|
|
|
|
instance Value'server_ IO LitValue where
|
|
value'read = pureHandler $ \(LitValue val) _ ->
|
|
pure Value'read'results { value = val }
|
|
|
|
newtype OpFunc = OpFunc (Double -> Double -> Double)
|
|
|
|
instance Server IO OpFunc
|
|
|
|
instance Function'server_ IO OpFunc where
|
|
function'call = pureHandler $ \(OpFunc op) Function'call'params{params} -> do
|
|
when (V.length params /= 2) $
|
|
throwFailed "Wrong number of parameters."
|
|
pure Function'call'results
|
|
{ value = (params V.! 0) `op` (params V.! 1) }
|
|
|
|
data ExprFunc = ExprFunc
|
|
{ paramCount :: !Int32
|
|
, body :: Expression
|
|
}
|
|
|
|
instance Server IO ExprFunc
|
|
|
|
instance Function'server_ IO ExprFunc where
|
|
function'call =
|
|
pureHandler $ \ExprFunc{..} Function'call'params{params} -> do
|
|
when (fromIntegral (V.length params) /= paramCount) $
|
|
throwFailed "Wrong number of parameters."
|
|
Function'call'results <$> eval params body
|
|
|
|
data MyCalc = MyCalc
|
|
{ add :: Function
|
|
, subtract :: Function
|
|
, multiply :: Function
|
|
, divide :: Function
|
|
, sup :: Supervisor
|
|
}
|
|
|
|
instance Server IO MyCalc
|
|
|
|
instance Calculator'server_ IO MyCalc where
|
|
calculator'evaluate =
|
|
pureHandler $ \MyCalc{sup} Calculator'evaluate'params{expression} ->
|
|
Calculator'evaluate'results <$>
|
|
(eval V.empty expression >>= atomically . export_Value sup . LitValue)
|
|
|
|
calculator'getOperator =
|
|
pureHandler $ \MyCalc{..} Calculator'getOperator'params{op} ->
|
|
Calculator'getOperator'results <$> case op of
|
|
Operator'add -> pure add
|
|
Operator'subtract -> pure subtract
|
|
Operator'multiply -> pure multiply
|
|
Operator'divide -> pure divide
|
|
|
|
Operator'unknown' _ ->
|
|
throwFailed "Unknown operator"
|
|
|
|
calculator'defFunction =
|
|
pureHandler $ \MyCalc{sup} Calculator'defFunction'params{..} ->
|
|
Calculator'defFunction'results <$>
|
|
atomically (export_Function sup ExprFunc{..})
|
|
|
|
newCalculator :: Supervisor -> STM Calculator
|
|
newCalculator sup = do
|
|
add <- export_Function sup $ OpFunc (+)
|
|
subtract <- export_Function sup $ OpFunc (-)
|
|
multiply <- export_Function sup $ OpFunc (*)
|
|
divide <- export_Function sup $ OpFunc (/)
|
|
export_Calculator sup MyCalc{..}
|
|
|
|
eval :: V.Vector Double -> Expression -> IO Double
|
|
eval _ (Expression'literal lit) =
|
|
pure lit
|
|
eval _ (Expression'previousResult val) = do
|
|
Value'read'results{value} <- value'read val ? def >>= wait
|
|
pure value
|
|
eval args (Expression'parameter idx)
|
|
| fromIntegral idx >= V.length args =
|
|
throwFailed "Parameter index out of bounds"
|
|
| otherwise =
|
|
pure $ args V.! fromIntegral idx
|
|
eval outerParams (Expression'call Expression'call'{function, params=innerParams}) = do
|
|
args' <- traverse (eval outerParams) innerParams
|
|
Function'call'results{value} <-
|
|
function'call function ? Function'call'params { params = args' }
|
|
>>= wait
|
|
pure value
|
|
eval _ (Expression'unknown' _) =
|
|
throwFailed "Unknown expression type"
|
|
|
|
main :: IO ()
|
|
main = serve "localhost" "4000" $ \(sock, _addr) ->
|
|
handleConn (socketTransport sock defaultLimit) def
|
|
{ getBootstrap = fmap (Just . toClient) . newCalculator
|
|
, debugMode = True
|
|
}
|