From 506e02923f8f50cf44dda69d8ca8f1df3e87ff2a Mon Sep 17 00:00:00 2001 From: Adithya Kumar Date: Sat, 1 Jun 2024 01:09:22 +0530 Subject: [PATCH] Add development notes regarding the design and implementation --- dev/build-time-preprocessing-issues.md | 27 +++++++ dev/design.md | 108 +++++++++++++++++++++++++ dev/restrictions.md | 8 ++ 3 files changed, 143 insertions(+) create mode 100644 dev/build-time-preprocessing-issues.md create mode 100644 dev/design.md create mode 100644 dev/restrictions.md diff --git a/dev/build-time-preprocessing-issues.md b/dev/build-time-preprocessing-issues.md new file mode 100644 index 0000000..568e0d0 --- /dev/null +++ b/dev/build-time-preprocessing-issues.md @@ -0,0 +1,27 @@ +# Issues with build time processing + +It is possible to make the workflow easier by processing the `.hs` files and +inducing the desired behaviour but the CPP macros are hard to handle. + +To handle the CPP macros we need to process the files after the CPP phase is +done. So we need to induce a hook after the CPP processing. + +The CPP processing is taken care by the GHC and hence the build tool cannot +handle this. Or atleast cabal cannot. + +To this hook needs to be incuced somewhere in the GHC pipeline itself. This is +too much of a hassle. and might not work as expected. + +We can possibly stick to the template haskell helpers. The only problem I have +is sometimes I add an rpc function but I forget to export it. We can maybe have +some linter to solve that problem? + +## Ignoring the CPP issue + +The current implementation pre-preocess the `.hs` files. + +There are many pitfalls given this implementation: +1. The changes are not transparent to the user. +2. The modifications are not robust. It fails in many cases. + - Fails with CPP + - Fails with recursive functions diff --git a/dev/design.md b/dev/design.md new file mode 100644 index 0000000..f8a2efd --- /dev/null +++ b/dev/design.md @@ -0,0 +1,108 @@ +# Design Document + +## Basic Design + +There are 3 parts to `simple-rpc`. + +1. Defination of the RPC function +2. Registering the RPC function in the server +3. Using the RPC function + + +## Current Implementation + +### Defination + +We define the RPC version of a function by using the template haskell helper +`rpcExport`. + +`rpcExport` creates the corresponding RPC function. + +Example, +``` +module Action.Users (ensureDirectoryForUser) where + +import Streamly.Internal.Unicode.String (str) +import qualified Streamly.Internal.System.Command as Cmd +import Simple.RPC.Server + +raw_ensureDirectoryForUser :: String -> FilePath -> IO () +raw_ensureDirectoryForUser user dir = do + Cmd.toStdout [str|sudo mkdir -p #{dir}|] + Cmd.toStdout [str|sudo chown -R #{user} #{dir}|] +$(rpcExport "raw_ensureDirectoryForUser" "ensureDirectoryForUser") +``` + +### Registeration + +The executable that registers all the RPC functions (aka. the server) should be +of the following form. + +``` +module Main + ( main + ) where + +import qualified Action.Users as Users +import Simple.RPC.Client + +main :: IO () +main = + mainWith "0.3.0" + [ evaluator Users.ensureDirectoryForUser ] +``` + +### Usage + +``` +module LocalLib (func) where + +import Data.Function ((&)) +import qualified Action.Users as Users + +rpcConfig :: RunningConfig +rpcConfig = exec exeOnRemote & onSSH sshConf + where + sshConf = SSHConfig "user@remote-machine.com" 22 + exeOnRemote = Executable "/opt/ssh-rpc/orchestrate" "0.3.0" + +func :: IO () +func = + ... + call Users.ensureDirectoryForUser rpcConfig "username" "~/.config" + ... +``` + +### Problems with the current implementation + +1. During the definations, we have to manually export the RPC versions. +2. During the creation of the server executable we have to make sure all the RPC + functions are exported. + +Both the above tasks can be automatic. Or atleast can be verified. + +## Design Goal + +Make the generation of rpc versions of exported functions more easier. + +### Scratch notes on one method of solving the above problem + +**User input:** + +- `.cabal` file template. Like a `config.in` file. Call it `.cabal.in` +- source code `.hs` files + +**User output:** + +- `RPC.*` versions of all the corresponding modules +- `.cabal` file that also contains all the RPC modules and an rpc-executable + +**Required components:** + +- A way to get the type signatures of all the exported functions + - Option 1: Parse the `.hi` files + - Option 2: the `.hs` files + +- A way to understand the `.cabal` file + - Parse the `.cabal` file and create the AST + - Modify the AST and create the final `.cabal` file diff --git a/dev/restrictions.md b/dev/restrictions.md new file mode 100644 index 0000000..9fd38f6 --- /dev/null +++ b/dev/restrictions.md @@ -0,0 +1,8 @@ +Any random function cannot be RPC-fied. +Only non-polymorphic functions whose input params can be serialized can be RPC-fied. + +We should make it easier to create an rpc server given the user specifies the +functions that need to be RPC-fied. +This includes: +1. Exporting the required RPC-fied functions automatically +2. Importing the required RPC-fied functions automatically and create a server