packages feed

corecursive-main (empty) → 0.1.0.0

raw patch · 9 files changed

+306/−0 lines, 9 filesdep +basedep +corecursive-maindep +processsetup-changed

Dependencies added: base, corecursive-main, process, unix

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for corecursive-main++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2018++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,46 @@+# corecursive-main++Functional programmers are used to program with (co-)recursive functions.  What+if the main program was itself recursive? One could benefit the underlying+operating system scheduler to orchestrate resources. For instance, limiting+memory, file-descriptor on a per-recursive-call basis. Another instance is+supporting distributed computations across multiple machines.++## This package++This package demonstrates the idea of replacing the main entry-point of the+program (i.e., `main :: IO ()`) with a recursion-aware main. The program first+pattern matches on an "argument". This argument defines which action to take+for the rest of the program. The rest of the program runs with a+"Self-reference", which allows the program to call itself back. Explicitly+passing a Self-reference is very similar to how the `fix` function allows to+factor recursion out of the body of a self-referential function.++Unlike `fix`, which executes recursive-call in the same memory-space as the+call-site, developper using this package need to teach the program to transfer+arguments and return values back-and-forth the call-site and the execution+site. This package minimizes the developper effort to perform this translation.++Currently, one mode is supported: spawning a child process on the same OS while+serializing arguments over the command-line parameters.++Future versions of this package or sibling packages will likely provide:+- support for 'JSON' or 'Dhall' serialization format+- support for 'stdin' or networked serialization mechanisms+- remote calls using `ssh`++## Envisionned usages++Distributed computating:+- run a same copy of the program on many places, each on a subset of the data (much like Map/Reduce)++Dev/Ops:+- use a single binary to reconfigure the whole datacenter and services after a cataclysmic outage (see #History) as well++- orchestrate processes locally++## History++This package is factored out of code and findings made while writing+[DepTrack](https://github.com/lucasdicioccio/deptrack-project). If you like+this package you may find other interesting ideas in DepTrack.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,28 @@+module Main where++import System.Process.Corecursive+import System.Posix.Process++newtype Arg = Arg { getListOfThings :: [String] }++numberOfThings :: Arg -> Int+numberOfThings = length . getListOfThings++append :: Show a => a -> Arg -> Arg+append x (Arg xs) = Arg (show x:xs)++main :: IO ()+main = runCorecursiveApp (app parse unparse go)+  where+    parse :: [String] -> IO Arg+    parse = pure . Arg++    unparse :: Arg -> IO [String]+    unparse = pure  . getListOfThings++    go self arg+      | numberOfThings arg > 10 = print "done"+      | otherwise               = do+          pid <- getProcessID+          dat <- readCallback self (append pid arg)+          putStr $ show pid ++ show (getListOfThings arg) ++ " " ++ dat
+ corecursive-main.cabal view
@@ -0,0 +1,68 @@+-- This file has been generated from package.yaml by hpack version 0.28.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: 24db3f929122337c798281c9a462b828f1136db68cc113c224e0ae537470b42c++name:           corecursive-main+version:        0.1.0.0+synopsis:       Write your main like it can call itself back.+description:    Please see the README on GitHub at <https://github.com/githubuser/corecursive-main#readme>+category:       Control+homepage:       https://github.com/githubuser/corecursive-main#readme+bug-reports:    https://github.com/githubuser/corecursive-main/issues+author:         Author name here+maintainer:     example@example.com+copyright:      2018 Author name here+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10+extra-source-files:+    ChangeLog.md+    README.md++source-repository head+  type: git+  location: https://github.com/githubuser/corecursive-main++library+  exposed-modules:+      System.Process.Corecursive+      System.Process.Corecursive.Base+  other-modules:+      Paths_corecursive_main+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , process+  default-language: Haskell2010++executable corecursive-main-exe+  main-is: Main.hs+  other-modules:+      Paths_corecursive_main+  hs-source-dirs:+      app+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , corecursive-main+    , process+    , unix+  default-language: Haskell2010++test-suite corecursive-main-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_corecursive_main+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , corecursive-main+    , process+  default-language: Haskell2010
+ src/System/Process/Corecursive.hs view
@@ -0,0 +1,41 @@+-- | A module to facilitate and demonstrate a programming pattern where+-- application instances for a system is (co-)recursive.+module System.Process.Corecursive+    ( runCorecursiveApp+    , callback+    , readCallback+    -- * re-exported for convenience+    , App+    , Self+    , app+    ) where++import Control.Monad (void)+import Control.Monad.IO.Class (liftIO, MonadIO)+import System.Environment (getArgs, getExecutablePath)+import System.Process (callProcess, readProcess)++import System.Process.Corecursive.Base (runApp, app, App, Self(..))++-- | Run function for an App which arguments are read from the command line+-- using 'getArgs' and for which the binary is discovered from+-- 'getExecutablePath'.+runCorecursiveApp :: MonadIO m => App m [String] arg FilePath () -> m ()+runCorecursiveApp =+    runApp (liftIO getExecutablePath) (liftIO getArgs)++-- | Callback running the Self executable locally and waiting until completion+-- using 'callProcess'.+callback :: MonadIO m => Self m [String] arg FilePath -> arg -> m ()+callback self a = do+    exe <- executable self+    args <- unparse self $ a+    liftIO $ callProcess exe args++-- | Callback running the Self executable locally and waiting until completion+-- using 'readProcess'.+readCallback :: MonadIO m => Self m [String] arg FilePath -> arg -> m String+readCallback self a = do+    exe <- executable self+    args <- unparse self $ a+    liftIO $ readProcess exe args ""
+ src/System/Process/Corecursive/Base.hs view
@@ -0,0 +1,86 @@++-- | Types for building (co-)recursive system programs.+--+-- The tools in this module allow to define the call-site and the main entry+-- point of a (co-)recursive program.+--+-- However, this module does not define 'callback' usages so that implementers+-- are free to chose the mechanism they want to:+-- - start the recursive program+-- - retrieve a return-value (if the recursive program ever returns)+module System.Process.Corecursive.Base+    ( App (..)+    , app+    , runApp+    , Self (..)+    ) where++-- | A datatype representing an instance of the (co-)recursive program.+--+-- A reason why the records in this type are separate from 'App' is that a+-- calling program may need to execute a fair amount of progress+-- before knowing the right 'executable' (e.g., in the case of a remote+-- invocation). Also, sometimes one may want to adapt 'Self'.+data Self t msg arg inst = Self {+    executable :: t inst+  -- ^ An  action to retrieve an instance of the (co-)recursive program+  -- the 'inst' type is leaved as an argument to allow representing cases where+  -- the execution is actually remote.+  , unparse    :: arg -> t msg+  -- ^ An action to unparse arguments. Typically from 'unparseArgs'.+  }+instance Functor t => Functor (Self t msg arg) where+    fmap f (Self e u) = Self (fmap f e) u++-- | A datatype wrapping everything needed to make a (co-)recursive main+-- function.+--+-- Application authors may find this type useful because it has a Functor+-- instance, allowing to adapt the result of a computation.+--+-- See 'app'.+data App t msg arg inst ret = App {+    parseArgs          :: msg -> t arg+  -- ^ Function parsing argument from a serialized message.+  , unparseArgs        :: arg -> t msg+  -- ^ Function un-parsing an argument into a serialized message.+  , corecursiveProgram :: Self t msg arg inst -> arg -> t ret+  -- ^ Actual program to run from a specification.+  }++instance Functor t => Functor (App t msg arg inst) where+    fmap f (App p u cp) =+        App p u $ \self arg -> fmap f (cp self arg)++-- | Constructor for an App.+app+  :: (msg -> m arg)+  -- ^ Function parsing argument from a serialized message.+  -> (arg -> m msg)+  -- ^ Function un-parsing an argument into a serialized message.+  -> (Self m msg arg inst -> arg -> m ret)+  -- ^ Actual program to run from a specification.+  -> App m msg arg inst ret+app = App++-- | Typical function to run an 'App' with simplifying assumptions:+--+-- - locate the Self instance once for the rest of the program+-- - reads the arguments once+-- - starts the actual program+runApp+  :: Monad m+  => m inst+  -- ^ An action to locate the self instance of an applictaion.+  -- This action is run exactly once and the result is stored in 'Self'.+  -> m msg+  -- ^ An action to retrieve the message to start the application with.+  -- This action is run exactly once, the result is then parsed and discarded.+  -> App m msg arg inst ret+  -- ^ The 'App' to run+  -> m ret+runApp getInst getMsg (App parse unparse go) = do+    inst <- getInst+    let self = Self (pure inst) unparse+    arg <- parse =<< getMsg+    go self arg
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"