AlgST is an implementation of Parameterized Algebraic Protocols by Mordido et al. It includes a typechecker, interpreter and a simple module system. You can find the artifact in the pldi23-artifact branch.
Building is supported either in a Docker container or using the stack build tool.
-
Prerequisits: Docker, e.g. via Docker Desktop
-
Building:
; docker build -t proglang/algst -f utils/Dockerfile .
Building takes about 10 minutes on my machine.
-
Testing:
; docker run --rm -it proglang/algst stack test
This will run the test suite covering the parser, type checker and interpreter.
-
Running: To run
algstprefix the invocations given below with; docker run --rm -it proglang/algst # command follows here...
To verify that running
algstworks execute; docker run --rm -it proglang/algst algst --help
-
Prerequisits: stack, installation instructions are provided on the linked page.
-
Building:
; stack buildThis command will download and install the required version of GHC, build the dependencies and finally the
algstexecutable. -
Testing:
; stack test
The command will compile and execute the test suite covering the parser, type checker and interpreter.
-
Running: To run
algstprefix the invocations given below with; stack exec -- # command follows here...
To verify that running
algstworks execute; stack exec -- algst --help
A file can be checked by giving its path as an argument; to read input from the
terminal give - as the path.
When the flag --run is given the main symbol will be executed and the
result printed.
Querying the typechecker is possible with the flags
| Flag | Description |
|---|---|
--nf TYPE |
calculate the normal form for TYPE |
--kind,-K TYPE |
perform kind synthesis for TYPE |
--type,-T EXPR |
perform type synthesis for EXPR |
Full usage info is available with --help.
; algst - --run --type 'fork main' --nf 'dual ?-Int.End!' <<EOF
main : Int
main = 10 + 20
EOFoutputs
Success.
--type fork number
?Int.End?
--nf dual ?-Int.End!
?Int.End?
Result: Number 30
-
rec x : T = eEvaluates to the expression
ewithxnaming the recursive expression in its body. Giving a typeTis obligatory and must be a function type.eitself must be a lambda abstraction. -
let rec x : T = e1 in e2Is similar to the version above. Additionally the recursive function
e1is also visible toe2under the same namex.
-
new [T] : (T, dual T)The
newexpression creates a pair of connected channels. It must be used with a type application. -
send : forall (a:TU) (s:SL). a -> !a.s -> sSends an unrestricted message type over the provided channel.
sendexists as a convenience version ofsendLin. SeesendLinfor an explanation. -
sendLin : forall (a:TL) (s:SL). a -> !a.s -o sSends any message type over the provided channel.
If used with unrestricted data (e.g.
Int) the partially applied function (e.g.sendLin [Int] 1) is linear, i.e. must be called exactly once. By usingsendinstead the same partial applicationsend [Int] 1results in an unrestricted function which may be called any number of times. -
receive : forall (a:TL) (s:SL). ?a.s -> (a, s)Receives any message type from the provided channel.
-
fork : forall (a:TL). a -> ?a.End?fork eevaluates expressionein a new thread. In the parent context it evaluates to a single element channel from which the result can be read once it becomes available.A type application is not necessary.
-
fork_ : forall (a:TU). a -> ()fork_ eevaluates expressionein a new thread and discards the result value. In the parent contextfork_ eevaluates to the unit value().A type application is not necessary.
-
usleep : Int -> ()Delays execution for the given number of microseconds.
-
trace : forall (a:TL). a -> aPrints a description of the given value to the screen.
-
traceMsg : String -> ()Prints the given message to the screen.
For demonstration purposes consider a module Data.List with the following
contents:
data List a = Cons a (List a) | Nil
single : forall a. a -> List a
single [a] a = Cons [a] a (Nil [a])
By importing a module all its top level symbols and associated constructors become available to the importing module. An import list can be used to modify the set of imported symbols.
-
Import all symbols:
import Data.List -
Import only specific symbols:
import Data.List (type List, Nil, single)The above statement imports all symbols from
Data.Listexcept theConsconstructor.Type names have to be prefied with the
typekeyword. -
Import symbols under a new name:
import Data.List (type List as ConsList, single as singleton) import Data.List (*, type List as ConsList, single as singleton)The first statement imports the
Listtype under the nameConsListand thesinglefunction is made available under the namesingleton.The second statement (note the leading
*) applies the same renamings but additionally imports the remaining symbols under their given name.Type names have to be prefied with the
typekeyword. -
Import all symbols except the given identifiers:
import Data.List (*, type List as _, Cons as _, single as _)The above statement imports only the
Nilconstructor as all other symbols are hidden. Note the leading*in the import list.Hiding and renaming symbols can be combined freely.
Re-exporting symbols is currently not supported. Neither is it possible to control which top-level symbols can be imported by other modules.
A module name A.B.C maps to a file A/B/C.algst in the module search path.
Without any additional arguments algst will search for modules in the current
working directory. With -I,--search-dir DIR other directories can be
specified.
The directory utils/vim contains syntax files for highlighting in vim.