pact
pact copied to clipboard
Add support for progn-equivalent for sequencing
It would be nice to have some equivalent to Common Lisp’s progn
/ Scheme’s begin
/ Clojure’s do
to sequence side-effecting forms in an expression. Except not named progn
😄:
(if (< x 5)
(do
(begin-tx)
(print "in tx")
(commit-tx))
(print "not running tx"))
At the moment, the only option is to create a separate function wherein these side effecting expressions are sequenced:
(defun helper ()
"Runs tx"
(begin-tx)
(print "in tx")
(commit-tx))
(if (< x 5)
(helper)
(print "not running tx"))
As much as I resist introducing Clojure-isms into Pact, do
is by far the best one. begin
: well, where does it end? progn
: barf.
Brian do you want to take a crack at this sometime?
Proposal:
In order to cleanly support do
and to clean up support for a number of other natives/special forms, I recommend adding essentially varargs support to the function type system. (Support for users to write vararg functions completely optional.)
Specifically, I propose amending the Arg
type (part of FunType
, defined in Lang.hs) so that
an Arg object can be marked as matching any number of arguments of its type. Only one Arg so marked would be permitted in each FunType, so that there's no ambiguity in matching up actual function arguments to the specified function type. We could add this to data Arg o
:
_aVararg :: Bool
(Alternatively, we could add a TyMany
alternative to Type
, but that could unnecessarily pollute code not dealing with FunType
s.)
I suggest prettying/mangling this by adding " *" at the end of the arg name, space character included to ensure it's not a legal user symbol. Thus, do
would have this function type:
(stmts *:* stmt-last:<a> -> <a>)
This gives us a lot of flexibility to type things like the functions bench
and list
(shown here):
(values *:<a> -> [<a>])
And like with do
, we could give with-read
and others like it a more legit function type, rather than leaving out the body statements from the signature as is currently done, or pretending that only one body statement is permitted, as in let
and let*
. Plus we can get rid of related work-arounds and special cases, as in the handling of return values from forms like with-read
in the type checker (search "-- assoc binding with app return").
This could permit changing format
to be a vararg function rather than taking a list, but it's probably easier and slightly more flexible to keep it a list, even if it's somewhat less convenient.
This could also be used to extend +
, *
, and
, and or
to be n-ary, as in most Lisps.
I'm not a huge fan of varargs to begin with (except in Java where there are no decent list literals) ... especially so for user functions as I think it makes for a confusing API (it really means a list, so how do you put that in JSON?)
Pact 1.x had varargs -- format
was originally varargs, and we didn't have list literals but only list
. During the typechecker development we set out to ditch them, breaking format
with 2.0, and deprecating list
once literals were in. bench
should probably take a list but it's a REPL-only function so it's less critical.
Body forms on the other hand would be nice to support. We can simply introduce &body
or similar since they're always the same shape/type. To my mind the best home for that would be in FunType
itself, as Arg
gets used in more places than defun arg lists (e.g. also for bindings).
Also, like other elements of Pact, we would not allow &body
in user code; it would really just allow us to denote special forms properly to the typechecker and docs.