DotArrow is the codename for "mobile code" in the context of a Pi-calculus to
Scala translator. It is implemented in a very simplistic fashion, for either
Scala or Haskell.
It is possible for Scala in PISC because
of three reasons:
-
input actions allow for the received value to be applied a function before returning the actual result - so that an entire
Scalasource file passed as aStringcan be updated throughscala.sys.process._facility to one modified as follows; -
the
IOmonad in the "mobile code" allows for a sequence offlatMaps to be chained and - if wanted - to beIO.canceledat any point between twoflatMaps; -
Scalametaallows forEnumerator.Generators to be manipulated such thatIO.canceledis inserted after eachflatMapwith a pattern variable with ascribed type.
Also, Serializable (case) classes allow for objects of type other than basic
types to be read and written using ObjectInputStream and ObjectOutputStream.
Alternatively, objects can be encoded to/decoded from JSON using the Scala library
circe.
The bin/pi.sh shell script has been added, respectively, two functions:
dotarrowCirceanddotarrowCirce2;dotarrowStreamanddotarrowStream2.
The first is used to insert _ <- IO.canceled cancellation points, while the second
is used to transform the Scalameta AST by dropping the first Enumerator.Generator
together with "its" IO.canceled.
The examples/dotarrow folder must have two sub-folders:
./examples/dotarrow/
src/
tmp/
The Scala source files go in the ./examples/dotarrow/ folder.
The (parent) root folder contains three files for "stream" serialization:
../dotarrow/stream/
app.scala.in
app2.scala.in
bin.in
The first two are used in the two shell functions, dotarrowStream, respectively,
dotarrowStream2, while the third is copied initially as the object serialization
(temporary) binary file.
And two files for "circe" JSON serialization:
../dotarrow/circe/
app.scala.in
app2.scala.in
These two are used in the two shell functions, dotarrowCirce, respectively,
dotarrowCirce2.
!!!Warning: do not delete them!!!
The examples folder contains a dotarrow_stream_ex0.pisc file. It contains embedded
Scala code that uses the scala.sys.process._ facility to execute Scala code
as it is reduced to _ <- IO { ... }.void; the final value 196.0 can be seen.
To "execute" the "mobile code", parse the .pisc file:
sbt:π-Calculus2Scala> run dotarrow_stream_ex0
Choose one example from the examples/dotarrow/ folder, e.g., ex3.scala;
then - in a shell -, cd to examples folder, and run:
./examples $ pio dotarrow_stream_ex0
./examples $ pi dotarrow_stream_ex0.scala -- ex3
Note how "ex3" was passed as an argument to the main method in dotarrow_stream_ex0.scala.
Try the same, but using circe:
sbt:π-Calculus2Scala> run dotarrow_circe_ex0
./examples $ pio dotarrow_circe_ex0
./examples $ pi dotarrow_circe_ex0.scala -- ex4
Or both:
sbt:π-Calculus2Scala> run dotarrow_stream_circe_ex_3_4
./examples $ pio dotarrow_stream_circe_ex_3_4
./examples $ pi dotarrow_stream_circe_ex_3_4.scala
The Scala source files must have a strict format:
-
the main object must be
object App extends IOApp.Simple; -
the main method must be
override def run: IO[Unit]; -
the body of the main method must be a
for-yieldcomprehension; -
variable-bound generators of the
for-yieldmust bind a single variable and be ascribed the type; -
definitions of the
for-yieldmust correspond tocase classes, bind a single variable for each parameter of thecase class, and the right hand side be a single variable that is bound - immediately preceding the definition - by a generator without an ascribed type (i.e., thecase class); -
names of variable-bound generators must not contain whitespace; their types may;
-
the scope of pattern variables in definitions and generators is limited until the first variable-bound with ascribed type generator;
-
"
if" guards are impossible as "withFilter" lacks from theIOmonad; -
the last
for-yieldstatement must be_ <- IO { ... }.void; -
must not output to
scala.Console.err; -
characters in the
Scalasource files are not supported; -
in addition, for
JSONserialization withcirce, the implicitEncoders/Decoders must be in the scope of the main method.
It is possible for Haskell in PISC because
of three reasons - two as for Scala and third:
TemplateHaskellallows forBindSs to be manipulated such thatexitFailureis inserted after eachdo-statement with a pattern variable with type signature.
Also, values can be encoded to/decoded from JSON using the Haskell library
aeson.
The bin/pi.sh shell script has been added two functions:
dotarrowAesonanddotarrowAeson2.
To "execute" the "mobile code", parse the .pisc file:
sbt:π-Calculus2Scala> run dotarrow_aeson_ex0
Choose one example from the examples/dotarrow/ folder, e.g., ex1/app/Main.hs;
then - in a shell -, cd to examples folder, and run:
./examples $ pio dotarrow_aeson_ex0
./examples $ pi dotarrow_aeson_ex0.scala -- ex1
Note how "ex1" was passed as an argument to the main method in dotarrow_aeson_ex0.scala,
while the implied (see below) "app/Main.hs" was omitted.
Haskell Tool Stack must be used and the
folders (e.g., "ex1") must have a .cabal build file; the command line launched
by PISC is, e.g.,
stack run -- /path/to/pisc/examples/dotarrow/tmp.../app/Main.hs
from either two dotarrow - with "stack-cabal" setup - sub-folders:
./dotarrow/aeson
./dotarrow/aeson2
copied to, respectively:
/path/to/pisc/examples/dotarrow/tmp.../tmp/aeson
/path/to/pisc/examples/dotarrow/tmp.../tmp/aeson2
folders.
The Haskell source files must have a strict format:
-
the executable source file must be
app/Main.hswith moduleMain; -
among
imports must be the following:import System.Exit (exitFailure) import System.IO (hPutStrLn, stderr) import Inp_gUgVwYdD8r import Out_gUgVwYdD8rwhere modules
Inp_gUgVwYdD8randOut_gUgVwYdD8rmust not be modified; -
the main method signature
main :: IO ()and declaration must be the last two declarations; -
the body of
mainmust be adonotation; -
dostatements that bind must pattern-match a single variable with type assertion; -
dostatements thatletmust correspond to a unique-constructordatatype, bind a single variable for each parameter of thedatatype, and the right hand side be a single variable that is bound - immediately preceding theletstatement - by a binding without an asserted type (i.e., thedatatype); -
the last
dostatement must bereturn (); -
must not output to
stderr.
This type of "mobile code" alternatively takes the form of either
Scala or Haskell programming language, but is very restricted.
The bin/pi.sh shell script has been added two functions:
dotarrowScalaToHaskellanddotarrowHaskellToScala.
To "execute" the "mobile code", parse the .pisc file:
sbt:π-Calculus2Scala> run dotarrow_circe_aeson_ex0
Choose one Scala example from the examples/dotarrow/ folder, e.g., ex5.scala
or ex6.scala, then - in a shell -, cd to examples folder, and run:
./examples $ pio dotarrow_circe_aeson_ex0
./examples $ pi dotarrow_circe_aeson_ex0.scala -- ex5
Note how "ex5" was passed as an argument to the main method
in dotarrow_circe_aeson_ex0.scala.
The Scala source files must have even a stricter format:
-
no
packagestatements; -
only simple arithmetic expressions (without division) of type
Intin theIO { }blocks; -
case classes must not share parameter names - as these are translated intoHaskellunique-constructordatatypes with identical field labels.
Note that both Scala Cli
and Ammonite must be installed, as well as
Stack. Also, the
Perlscript bin/Scalameta2Haskell.pl
must be available in the PATH.
Codecs is the codename for encoding "programs" as numeric expressions:
-
in
Scalavia providing animplicitobjectextending theNumerictrait; -
in
Haskellvia providing aninstanceof theNumclass.
There are currently two implementation of codecs:
-
lists of integers that can be added, negated or subtracted; the multiplication operator is used to concatenate lists, e.g.,
./examples $ pi dotarrow_codec_list_aeson_ex0.scala -- ex3 ./examples $ pi dotarrow_codec_list_circe_ex0.scala -- ex11 -
an expression
DSLthat mirrors the native numeric expressions, e.g.,./examples $ pi dotarrow_codec_idem_aeson_ex0.scala -- ex4 ./examples $ pi dotarrow_codec_idem_circe_ex0.scala -- ex12
In the latter DSL case, for example, the integers - either variables
of type Int that have been previously bound, or literals - are wrapped
within fromInt in an "invisible" step before the main program is run,
and the numeric operations are actually performed with DSL values.
Because the number of examples is growing rapidly, this is the list of the commands with which they were tested as arguments:
-
Scala./examples $ pi dotarrow_circe_ex0.scala -- ex1 ./examples $ pi dotarrow_circe_ex0.scala -- ex2 ./examples $ pi dotarrow_stream_ex0.scala -- ex3 ./examples $ pi dotarrow_circe_ex0.scala -- ex4 ./examples $ pi dotarrow_codec_list_circe_ex0.scala -- ex11 ./examples $ pi dotarrow_codec_idem_circe_ex0.scala -- ex12 ./examples $ pi dotarrow_codec_idem_circe_ex0.scala -- ex13 -
Haskell./examples $ pi dotarrow_aeson_ex0.scala -- ex1 ./examples $ pi dotarrow_aeson_ex0.scala -- ex2 ./examples $ pi dotarrow_codec_list_aeson_ex0.scala -- ex3 ./examples $ pi dotarrow_codec_idem_aeson_ex0.scala -- ex4 ./examples $ pi dotarrow_codec_idem_aeson_ex0.scala -- ex5 -
Scala&Haskell./examples $ pi dotarrow_circe_aeson_ex0.scala -- ex5 ./examples $ pi dotarrow_circe_aeson_ex0.scala -- ex6 ./examples $ pi dotarrow_circe_aeson_ex0.scala -- ex7 ./examples $ pi dotarrow_circe_aeson_ex0.scala -- ex8 ./examples $ pi dotarrow_circe_aeson_ex0.scala -- ex9 ./examples $ pi dotarrow_circe_aeson_ex0.scala -- ex10