laborantin-hs 0.1.4.0 → 0.1.5.0
raw patch · 3 files changed
+168/−16 lines, 3 filesdep +asyncdep ~base
Dependencies added: async
Dependency ranges changed: base
Files
- Laborantin/CLI.hs +19/−1
- README.md +140/−6
- laborantin-hs.cabal +9/−9
Laborantin/CLI.hs view
@@ -7,6 +7,8 @@ import Control.Monad import Control.Applicative import Control.Monad.IO.Class+import Control.Concurrent+import Control.Concurrent.Async import Laborantin import Laborantin.Types import Laborantin.Implementation@@ -67,6 +69,7 @@ data Labor = Run { scenarii :: [String] , params :: [String] , matcher :: [String]+ , concurrency :: Int } | Continue { scenarii :: [String] , params :: [String]@@ -129,6 +132,12 @@ , Help "Restrict to a matching expression" , ArgHelp "MATCHER EXPRESSION" ]+ , concurrency %> [ Short "c"+ , Long ["concurrency"]+ , Help "Number of experiments to run at a same time"+ , ArgHelp "CONCURRENT RUNS"+ , Default (1::Int)+ ] ] instance RecordCommand Labor where@@ -182,7 +191,16 @@ filterDescriptions (ScenarioName []) xs = xs filterDescriptions (ScenarioName ns) xs = filter ((flip elem ns) . sName) xs +concurrentmapM_ :: Int -> (a -> IO b) -> [a] -> IO ()+concurrentmapM_ n f xs = do+ goChan <- newChan :: IO (Chan ())+ joinChan <- newChan :: IO (Chan ())+ let f' a = readChan goChan >> f a >> writeChan goChan () >> writeChan joinChan ()+ mapM_ (async . f') xs+ replicateM_ n (writeChan goChan ()) + mapM_ (\_ -> readChan joinChan) xs + runLabor :: [ScenarioDescription EnvIO] -> Labor -> IO () runLabor xs labor = do now <- getCurrentTime@@ -191,7 +209,7 @@ Find {} -> do execs <- runEnvIO (loadMatching now) mapM_ (T.putStrLn . describeExecution) execs Rm {} -> runSc (loadAndRemove now)- Run {} -> mapM_ runSc (targetExecs [])+ Run {} -> do concurrentmapM_ (concurrency labor) runSc (targetExecs []) Continue {} -> do execs <- runEnvIO (loadMatching now) mapM_ runSc (targetExecs execs) Analyze {} -> runSc (loadAndAnalyze now)
README.md view
@@ -1,11 +1,145 @@ laborantin-hs ============= -Initially, Laborantin is a Ruby framework for controlling and managing-experiments. Laborantin-Hs is the Haskell port of Laborantin.+Laborantin is a Haskell framework for running controlled experiments.+It is already quite stable and only few things should change in the near+future. Comments and pull requests are warmly welcome. -# Introduction+# Install +The easiest way to install Laborantin is to use the package published on+hackage.++```sh+ cabal update+ cabal install laborantin-hs+```++Alternatively you can clone this repository with git to get the latest+development version.++```sh+ git clone https://github.com/lucasdicioccio/laborantin-hs + cd laborantin-hs+ cabal update+ cabal sandbox init # only if you want a sandboxed install+ cabal install # you can also use cabal configure && cabal build to just build the repo+```++# Two-minutes tutorial++When using Laborantin the typical workflow is as follows:++1. Write one or multiple scenarios using the DSL, e.g. ```my-experiment.hs```.+2. Compile the application with ```ghc --make -O2 my-experiment.hs```. If you built Laboratin using a sandbox, you need to specify the local package database, e.g.++ ghc -no-user-package-db \+ -package-db /path/to/laboratin/.cabal-sandbox/x86_64-linux-ghc-7.6.3-packages.conf.d \+ --make -O2 my-experiment.hs++3. Run experiments with++ ./my-experiment run -m "@sc.param 'some-param' in [42, 'toto'] and @sc.param 'other-param' == 1234"+++Example, annotated, code is as follows. Inline comments start with "--". Please+note that the actual implementation of the `executePingCommand` is left as an+exercise.++```haskell+{-# LANGUAGE OverloadedStrings #-}++module Main where+-- import the world+import Data.Text (Text)+import Control.Monad.IO.Class (liftIO)+import Laborantin.DSL+import Laborantin.Types+import Laborantin.CLI+import Laborantin.Implementation++-- declare one scenario+ping :: ScenarioDescription EnvIO+ping = scenario "ping" $ do -- start defining a scenario called "ping"+ -- enters the description for this scenario+ describe "ping to a remote server"+ -- declares a first parameter, called "destination".+ -- this parameter has some description for documentation purposes+ -- we should explore two values (strings) by default for this parameter+ parameter "destination" $ do+ describe "a destination server (host or ip)"+ values [str "example.com", str "probecraft.net"]+ -- declares a second parameter, called "packet-size".+ -- we should also explore two values (rational numbers) by default for this+ -- parameter+ parameter "packet-size" $ do+ describe "packet size in bytes"+ values [num 50, num 1500] ++ -- now implement the "run hook", which is the actual code to run+ run $ do+ (StringParam srv) <- param "destination" -- lookup "destination" parameter, it should be a string+ (NumberParam ps) <- param "packet-size" -- lookup "packet-size" parameter, it should be a rational number+ liftIO (executePingCommand srv ps) >>= writeResult "raw-result" -- executes the ping action defined below, and dumps the result into a file called "raw-result"+ where executePingCommand :: Text -> Rational -> IO (Text)+ executePingCommand host packetSize = ...++-- list your scenarios in the defaultMain to get a command-line app+main :: IO ()+main = defaultMain [ping]+```++At first, it looks like Laborantin requires a lot of boilerplate if your+experiment is a one-liner. Nothing is free and this is the small cost you have+to pay. However you get a handy command-line tool for this cost.++For instance, you get some documentation command: `./my-experiment describe` will output:++```+# Scenario: ping+ ping to a remote server+ 4 parameter combinations by default+## Parameters:+### destination+(destination)+ a destination server (host or ip)+ 2 values:+ - StringParam "example.com"+ - StringParam "probecraft.net"++### packet-size+(packet-size)+ packet size in bytes+ 2 values:+ - NumberParam (50 % 1)+ - NumberParam (1500 % 1)+```++You can run experiments with: `./my-experiment run` (stripped output).++```+backend> execution finished++backend> "preparing ping"+backend> "resolving dependencies"+backend> scenario: "ping"+ rundir: results/ping/81e44c78-4fd8-4ab9-8f97-5494dac646a2+ json-params: {"packet-size":{"val":1500.0,"type":"num"},"destination":{"val":"probecraft.net","type":"string"}}++backend> execution finished+```++Then find where experiments results are located with: `./my-experiment find`.++```+results/ping/2ead949a-ed36-4523-9a9c-7c7e2c22a1b2 ping (Success) {"packet-size":{"val":1500.0,"type":"num"},"destination":{"val":"example.com","type":"string"}}+results/ping/81e44c78-4fd8-4ab9-8f97-5494dac646a2 ping (Success) {"packet-size":{"val":1500.0,"type":"num"},"destination":{"val":"probecraft.net","type":"string"}}+results/ping/866b63e8-d407-442c-bc33-f1fb4e96c2a8 ping (Success) {"packet-size":{"val":50.0,"type":"num"},"destination":{"val":"example.com","type":"string"}}+results/ping/a34826bc-5160-4e12-95cc-12fb5c02fc7b ping (Success) {"packet-size":{"val":50.0,"type":"num"},"destination":{"val":"probecraft.net","type":"string"}}+```++# Discussion+ Designing scientific experiments is hard. Scientific experiments often must test an hypothesis such as *does parameter X* influences the outcome of *process A* ? Experimenters must write code to run the *process A*, a task that may be daunting@@ -112,10 +246,10 @@ # Roadmap -For version 0.1.5.x+For version 0.1.6.x -* [improvement] Use system locale rather than default locale for time parsing- - actually might not be such a good idea, let everyone input %d/%d/%y+* [improvements] changes 'require' to support parametrization+ - needs to pass extra arguments * [feature] exports to propose exported files using "show-exports" command * [feature] labor-script binary for easy-integration of other scripts - will need to expand parameter spaces by extending undeclared parameters
laborantin-hs.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: laborantin-hs-version: 0.1.4.0+version: 0.1.5.0 synopsis: an experiment management framework description: Laborantin is a framework and DSL to run and manage results from scientific@@ -11,15 +11,15 @@ . Writing experiments with Laborantin has at least two advantages over rolling your own scripts. First, Laborantin standardizes the workflow of your- experimentations. There is one-way to describe what a project can do, what- experiments where already run, how to delete files corresponding to a specific- experiment etc. Second, Laborantin builds on years of experience running- experiments. Using Laborantin should alleviates common pain points such as- querying for experiments, managing dependencies between results+ experimentations. There is one way to describe what a project can do, what+ experiments were already run, how to delete files corresponding to a specific+ experiment, etc. Second, Laborantin builds on years of experience running+ experiments. Using Laborantin should alleviate common pain points such as+ querying for experiments, managing dependencies between results, etc. . Laborantin's DSL lets you express experiment parameters, setup, teardown, and recovery hooks in a systematic way.- In addition, this DSL let you express dependencies on your+ In addition, the DSL lets you express dependencies on your experiments so that you can run prior experiments or data analyses. . Laborantin comes with a default backend that stores@@ -62,7 +62,7 @@ exposed-modules: Laborantin, Laborantin.DSL, Laborantin.Implementation, Laborantin.Types, Laborantin.CLI, Laborantin.Query other-modules: Laborantin.Query.Parse, Laborantin.Query.Interpret other-extensions: FlexibleContexts, OverloadedStrings, TupleSections- build-depends: base >=4.6 && <4.7, transformers >=0.3 && <0.4, mtl >=2.1 && <2.2, containers >=0.5 && <0.6, text >=0.11 && <0.12, bytestring >=0.10 && <0.11, aeson >=0.6 && <0.7, uuid >=1.2 && <1.3, directory >=1.2 && <1.3, random >=1.0 && <1.1, hslogger >=1.2 && <1.3, cmdlib >= 0.3.5, split >= 0.2.2, time >= 1.4.0.1, parsec >= 3.1.0, old-locale >=1.0.0.5+ build-depends: base >=4.6 && <4.8, transformers >=0.3 && <0.4, mtl >=2.1 && <2.2, containers >=0.5 && <0.6, text >=0.11 && <0.12, bytestring >=0.10 && <0.11, aeson >=0.6 && <0.7, uuid >=1.2 && <1.3, directory >=1.2 && <1.3, random >=1.0 && <1.1, hslogger >=1.2 && <1.3, cmdlib >= 0.3.5, split >= 0.2.2, time >= 1.4.0.1, parsec >= 3.1.0, old-locale >=1.0.0.5, async >= 2.0.1.0 -- hs-source-dirs: default-language: Haskell2010 @@ -70,5 +70,5 @@ main-is: main.hs ghc-options: -Wall hs-source-dirs: examples- build-depends: base >=4.6 && <4.7, transformers >=0.3 && <0.4, mtl >=2.1 && <2.2, containers >=0.5 && <0.6, text >=0.11 && <0.12, bytestring >=0.10 && <0.11, aeson >=0.6 && <0.7, uuid >=1.2 && <1.3, directory >=1.2 && <1.3, random >=1.0 && <1.1, hslogger >=1.2 && <1.3, cmdlib >= 0.3.5, split >= 0.2.2, laborantin-hs >= 0.1.1.2+ build-depends: base >=4.6 && <4.8, transformers >=0.3 && <0.4, mtl >=2.1 && <2.2, containers >=0.5 && <0.6, text >=0.11 && <0.12, bytestring >=0.10 && <0.11, aeson >=0.6 && <0.7, uuid >=1.2 && <1.3, directory >=1.2 && <1.3, random >=1.0 && <1.1, hslogger >=1.2 && <1.3, cmdlib >= 0.3.5, split >= 0.2.2, laborantin-hs >= 0.1.1.2 default-language: Haskell2010