andrevandelft / scala Goto Github PK
View Code? Open in Web Editor NEWThis project forked from scala/scala
The SubScript extension to the Scala programming language
Home Page: http://www.subscript-lang.org/
This project forked from scala/scala
The SubScript extension to the Scala programming language
Home Page: http://www.subscript-lang.org/
Tests are specified on lines such as
, [a;b] -> "a;b" -> "->a a->b ab"
The first string on such lines are superfluous; the information should be taken from the closure [a,b].
There is an ever increasing need for concurrent and reactive applications. Programming languages and framworks get therefore more and more support for this, but the result looks more like an explosion of engineering solutions rather than a coherent approach that applies available theory. In particular fundamental idioms for non-determinism and parallelism have been neglected. We aim to improve the situation using SubScript, an extension to Scala based on a mathematical theory named the Algebra of Communicating Processes.
Traditionally most programming langues have elementary support for sequential composition, often using syntax elements such as semicolons and line breaks. In comparison the support for choice and parallelism, which are conceptually similar, is apparently much on a low level. Existing formalisms that offer elementary support for choice and parallelism, have been neglected in most programming language research.
Since the early days of computing there have been formalisms with high level support for non-deterministic choice.
In the 1950s, Stephen Kleene researched the behaviour description of nerve nets. To describe their event handling he invented regular expressions. About the same time linguist Noam Chomsky started working on formal grammars. In 1960 Backus Naur Form (BNF) was born, a grammar notation for computer languages. In that year also the first compiler-compiler was created, later followed by many others such as YACC.
In the seventies the term Path Expression was coined for specifications with concise constructs such as sequential and concurrent compositions, to describe process synchronisation. This inspired Jan van den Bos around 1980 to build the Input Tool Model: an extension to Pascal and Modula-2 with a layer of regular expressions for the specification of input patterns in user machine interaction.
By that time three algebraic process specification formalisms emerged: CSP, CCS, ACP. Unlike most earlier formalisms these allowed for mixing of input and output actions; they also offered a more complete set of compositions: sequence, non-deterministic choice, parallelism, iterations and others. This largely widened the application area.
In the end of the 1980s the Input Tool Model branched off a sequence of ACP extensions to programming languages: Pascal, C, C++, Java and Scala. The latter extension is named SubScript.
ACP is a solid mathematical aproach. Processes are notated as expressions, with operations such as addition, meaning choice, and multiplication, meaning sequence. At the bottom level there is a 0 (“deadlock”, a blocking process), a 1 (“empty process”, which does not really do anything but which immediately terminates succesfully) and so-called atomic actions. Axioms may be as rewrite rules so that we can reason precisely about semantics. E.g.,
(x+1)y = xy + 1y = xy + y
In other words: adding 1 to a process makes it optional.
Other kinds of composition, such as parallelism and disruption, may be defined in ACP using additional axioms.
SubScript essentially extends Scala with concepts from ACP. It lives up to this quote of YACC creator Stephen Johnson:
" The ideas and techniques underlying YACC are fundamental and have application in many areas of computer science and engineering. One application I think is promising is using compiler-design techniques to design GUIs - I think GUI designers are still writing GUIs in the equivalent of assembly language, and interfaces have become too complicated for that to work any more."
SubScript adds a new element to Scala syntax, called script. A script resembles an ordinary method, but is defined by an ACP expression rather than ordinary Scala statements. For example, the following GUI script expresses that a user invokes “ok” by either clicking an “ok” button, or by pressing the Enter key:
script okCommand = clicked(okButton) + key(Enter)
By analogy with Scala’s implicit method feature we can make the called scripts clicked and key implicit, so that we can write even more concisely:
script okCommand = okButton + Enter
Instead of adding two processes, it seems we are now adding a button and a key code. Thus the expressions are not limited to processes, but they hold any kind of items for which implicit conversions exist.
ACP is good for specifying and verifying internal process behaviour and synchronous process communication, but less for named processes and queued asynchronous communication. The latter are are well supported by the Actor model. Apparently ACP and the actor model may complement one another well.
Internal actor behavior is relatively cumbersome to specify in Akka, the actor library for Scala. Support for finite state machines, futures and async
has been some improvement using futures and the async construct, but at times obfuscate the control flow. Using SubScript for internal actor behaviour, message receptions and subsequent responses are not any more done in receive
methods, but rather in nestable message handlers, enclosed in <<
and >>
brackets. There are much like the partial functions in receive
methods. The case
keyword may be omitted if there is only 1 variation. Moreover a long arrow ==>
may follow in a message handler, which is a special sequential composition: since it is still in the <<…>>
bracket pair, earlier declared parameters and local values and variables are available. Moreover the message sender is saved under the hood in a local value, so that it may safely be used later. For example, consider
script live = << s:String => var t=0; w1! s; w2! s
==> <<i:Int=>t+=i>> & <<s:String=>t+=s.length>>
{sender!t}
>>
This accepts a string as incoming message, and forwards it to 2 worker actors. These perform a computation; one returns a number and the other a string; the order of arrival is irrelevant thanks to the parallel composition operator &
. The sum of the received number and the length of the string is then returned to the sender of the original message: this is a scala code fragment, which must be enclosed within braces; it corresponds with an atomic action in the ACP sense. Then the live script ends so that the actor terminates.
SubScript is currently mature enough for GUI controllers and text parsing. SubScript actors are mature for experimentation, but probably not for production use. Currently its bare message handling has a throughput of several tens of thousands messages per second, which is in the order of 10 to 100 times slower than plain Akka.
For more see www.subscript-lang.org
{ } looks more tiny than {! !}, but up to now the former marks a "fat" atomic action, whereas the latter a so-called "tiny" code fragment. This should be the other way round; then all atomic actions would have at least one other symbol next to the braces, as in {? ?}, {* *}, {. .}, {... ...}.
Consider the following script:
def script live = (
val i: String = new String("Hello")
{println(i)}
)
On compilation, the compiler will fail with the following exception:
[ant:scalac] java.lang.OutOfMemoryError: Java heap space
[ant:scalac] at java.lang.Integer.valueOf(Integer.java:625)
[ant:scalac] at scala.runtime.BoxesRunTime.boxToInteger(BoxesRunTime.java:69)
[ant:scalac] at scala.tools.nsc.ast.parser.Parsers$UnitParser.syntaxError(Parsers.scala:233)
[ant:scalac] at scala.tools.nsc.ast.parser.Parsers$Parser.simpleScriptTerm(Parsers.scala:2600)
...
In general, it seems that every script variable/value declaration that involves the new
keyword on its right-hand side will cause such an exception.
A workaround is to enclose the right-hand side in the parentheses:
def script live = (
val i: String = (new String("Hello"))
{println(i)}
)
As noted on the Syntax documentation page:
There is still ambiguity with a.b and a(b).
If there is white space before . it is an optional break; else it is a path separator.
If there is white space before ( it starts a parenthesized expression; else it starts a parameter list
This needs to be implemented. For now, [a .] is not accepted by the parser.
So far, this transient dependency must be specified manually in whatever build system we use. Perhaps, this should be done once subscript-swing is also separated from the compiler, and when the new compiler is ready.
Currently script parameters may be enclosed in parentheses, but a notation with just comma's is also supported:
println("hello")
println, "hello"
the second notation would be useful in case of an implicit conversion with more than 1 parameter (to be implemented using an implicit conversion with a tuple), e.g.,
"(", nws
In a syntax definition could be implicit for parse("(", nws)
with nws a switch for "no white space" (before the symbol).
IMO the comma notation would only be good for such implicit script calls.
For other purposes, with an explicit name of the called script, it would be clearer to use a Smalltalk-like colon notation:
println: "hello"
If there are multiple parameters, these would be separated by comma's. E.g. a poisson process spawner would be in the old syntax
spawnEvery( 5*minute, [ aScript] )
In the new syntax:
every: 5*minute, spawn: [ aScript ]
or even with a closing tag:
every: 5*minute, spawn: [ aScript ] :end
Such a script could be defined as:
every:spawn:end[T](duration: double, process: Script[T]) = ...
Note that in the definition we would not write the extra colon for the trailing tag, and in case there is only one parameter we would not write a colon at all.
Could the colon notation also support variable argument lists? Probably, but then for the group of arguments the tag would not be repeated. E.g.,
myPrint: "a", "b", "c"
would call
myPrint( args:String* )
Currently there are several types of brace pairs that separate the process part in scripts from Scala code. Normal braces for normal atomic actions, and braces with immediately next to them one of these symbols: * ? ! . ...
.
I wanted to make it more difficult to specify normal atomic actions, for these are rather CPU expensive, so I thought these should be specified using {! ... !}
. These are now in use for tiny code fragments, and I thought those could use the plain braces instead.
Anyway, braces for tiny code fragments should also become less needed: method calls as tiny code may also use the plain script call syntax, and assignments could use the let
construct (work in progress).
BTW currently a method-with-script-call-syntax yields a normal atomic action; this should become a tiny code fragment.
But now I think tiny code fragments should be within {: ... :}
; every kind of code fragment would then have - no kind would be the default. The main reason for this proposal is that braces would have a use to mark Scala statements (resulting in a value) that would get an implicit conversion.
A use case is the data store proxy sample application. Its live
script would be:
live = << req: InformationRequest ==> {dataStore ? req}
~~(data:Data)~~> {dataStore ? DetailsRequest(data)}
~~(details:Details)~~> {! sender ! (data, details) !}
>>
{dataStore ? req}
etc. yield futures, which require implicit conversions to scripts.
BTW I had in mind to write a different kind of brace pair: {=dataStore ? req=}
but this does not really appeal to me.
Next to val and var declarations, we could have "let" constructs:
let a = b + 9
This would be equivalent to
{a = b + 9}
i.e. a tiny code fragment (proposed new syntax {}) doing the assignment.
This has 2 benefits: less parentheses-like symbols (i.c. braces), and good alignment with val and var declarations.
Consider the following script:
def script live = (
val i: Int = -1
{println(i)}
)
On compilation, it will fail the compiler with the following exception (first few lines):
[ant:scalac] java.lang.NullPointerException
[ant:scalac] at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$90.apply(Typers.scala:4443)
[ant:scalac] at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$90.apply(Typers.scala:4443)
[ant:scalac] at scala.collection.LinearSeqOptimized$class.forall(LinearSeqOptimized.scala:69)
[ant:scalac] at scala.collection.immutable.List.forall(List.scala:83)
[ant:scalac] at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$onError$2(Typers.scala:4443)
[ant:scalac] at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$tryTypedApply$1$2.apply(Typers.scala:4467)
[ant:scalac] at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$tryTypedApply$1$2.apply(Typers.scala:4467)
If we rewrite it without specifying the type, like this:
def script live = (
val i = -1
{println(i)}
)
then before the exception we'll get the following error:
[ant:scalac] /Users/anatolii/Projects/SSPolygon/src/main/scala/Main.scala:20: error: not found: value -
[ant:scalac] val i = -1
[ant:scalac] ^
Positive integers work correctly.
A workaround to the problem is to enclose the negative integer into parentheses:
def script live = (
val i = (-1)
{println(i)}
)
As I wrote in the PDF submitted to Onward!:
In the presented code the declaration of variable xResult implies a sequential composition, which is in general undesirable. (...)
Probably an alternative for this sequential composition would be appropriate, e.g., a
let ... in ...
construct as known from functional programming languages such as ML
The current VM processes about 10,000 actor messages per second, for a simple Sieve actor system. This may probably largely be improved by eliminating most creations and handlings of call graph messages:
Tasks now done by Activation, Deactivation, AAActivated, AAHandled may largely be implemented by recursive methods; for 1-ary and n-ary operators, and for then-else like constructs, there is a need for a continuation.
The Break messages may probably completely eliminated; a break or optional break could just set a few fields values in the closest by n-ary ancestor nodes.
However, the current inefficient implementation has also merits: it allows for nice animations in the debugger.
Therefore it should be possible to implement an optimizaton that leaves the old messaging model available.
For now a script or scripts section starts with "def script" whereas "script" would be much nicer. The reason is that "script" is still an identifier, rather than a keyword, and it would be quite hard (if I am right) to see in Scala expressions etc that this special identifiers starts a new script definition.
A solution would be to ensure that "script" is scanned as a keyword. However this should be done conditionally, i.e. after "import subscript.language" or something like that.
Changed the terminology: result values instead of return values
This is because otherwise the type is not well recognized; it must be used in generated code at several places. Having an explicit type simplifies the issue for the compiler, but not for the application programmer.
Consider following script:
live = @{}: (** {. .} **)
In AST view, it will look like follows:
<caseaccessor> def _live(): subscript.DSL.Script[scala.Unit] = subscript.DSL._script(this, scala.Symbol("live"))(subscript.DSL._at(((there: Any) => ()))(subscript.DSL._launch_anchor(subscript.DSL._eventhandling(((here: subscript.vm.N_code_eventhandling) => ())))))
Now instead of
subscript.DSL._at(((there: Any) => ()))
we should have
subscript.DSL._at(((there: N_launch_anchor) => ()))
In case of other nodes, the type of there
is set correctly, so the issue is only with N_launch_anchor and N_launch (which behaves identically to N_launch_anchor in this case).
LifeFrame contains:
speedKeyInput = times(10)
+ val c:Any=(pass_up1(here)+'0') key(chr(c)) setSpeed(char2Value(c))
This should become nicer, e.g.,
speedKeyInput = times(10)
+ val c=chr(pass_up1(here)+'0') c setSpeed(char2Value(c))
There is a bunch of Swing scripts that are supposed to wait for a mouse event to happen. They have signatures as follows:
mousePressed (comp: Component, ?p : java.awt.Point)
So they just provide an information about the point where the click happened. But the mouse events generally carry much more information: for example, whether this action triggers a popup menu.
I propose to change ?p: java.awt.Point
to correspondent events - ?e: MousePressed
in case of the former example. This, however, may break the existing Swing applications, so it requires some work.
Data flow arrows ~~>
and ~/~>
(for Throwables) are rather new in SubScript.
A recent use case revealed that the operator priority needs to change:
query.findInBackground
~~task:Task[List[ParseObject]]~~>
for (post <- task.getResult)
& ( post.put("published", true)
post.saveInBackground
)
With the current high priority the RHS of the arrow would be the for
-construct, but it should extend to the end of the code fragment. It seems that the arrow's operator priority should be very low; maybe the lowest possible priority, lower even than the newl line. At least it should be lower than the priority of ==
.
Another issue: currently there is one main dataflow operator: `x ~~> y /> z', where either the left or right arrow is optional. This is a bit ambiguous, as the following expressions are all different:
x ~~> y ~/~> z
x ~~> (y ~/~> z)
(x ~~> y) ~/~> z
Moreover we would like to be able to chain such expressions without worrying.
E.g.,
`x1 ~~> x2 ~~> x3 ~/~> z'
should be equivalent to
`((x1 ~~> x2) ~~> x3) ~/~> z'
i.e. a dataflow pipe with z
as fallback in case the pipe breaks down somewhere. Then we must have different syntax for the ternary use coming down to do x then y else z
.
Onother idea of specifying parameters inside the arrows has led to another idea: multiple alternatives, using extra arrows that start with +
, as in:
x ~~p1:P1~~> y1
+~~p2:P2~~> y2
Now we can use the same plus symbol for the ternary dataflow operator: `x ~~> y +/> z'.
In general the dataflow operator looks like:
x ~~a1:A1~~> y1
+~~a2:A2~~> y2
+~~a3:A3~~> y3
+~/~e1:E1~~> z1
+~/~e2:E2~~> z2
+~/~e3:E3~~> z3
Then this would translate to
x ~~> PartialFunction {
case a1:A1 ==> y1
case a2:A2 ==> y2
case a3:A3 ==> y3
case _ ==> {fail(new MatchException(a))}
}
+~/~> PartialFunction {
case e1:E1 ==> z1
case e2:E2 ==> z2
case e3:E3 ==> z3
case _ :Throwable ==> {fail(e)}
}
This transformation would be done by the compiler. It also translates the arrows into call scripts in DSL.scala by the names of _dataflow_then_else
, _dataflow_then
, _dataflow_else
.
The PartialFunction on the RHS is in fact a partial script; its treatment would be much like the current treatment of partial functions that handle incoming actor messages.
Note that the result of the partial script should be the result of the chosen branch. And other rules are:
x ~~> y +~/~> z
is the result of y
or z
, whenever either one of these two succeeds or fails (note that y
may be chosen multiple times, and z
at most once).x ~~> y
is set to the result of y
when that succeeds or fails, or to the result of x
when that fails.x ~/~> z
is set to the result of x
when this succeeds or of z
when that succeeds or fails.Most of this work seems relatively simple; I am not sure about the last parts (translating match expressions and setting the result value).
The gain would be big: dataflow operators would work almost completely (except for some compile time type checking), and lots of nice use cases will benefit, such as the Twitter Search example.
LookupFrame2, GraphicalDebugger and LifeFrame currently behave badly.
This is a regression from commit d49faa7 [d49faa7](8 April 2014).
OperatorsSuite reports no errors.
The output from SimpleScriptDebugger in both the HEAD and the d49faa7 commit on a "sensitive" application should highlight where the problem resides.
Then the problem should be reproduced in a new test in OperatorsSuite, and thereafter resolved.
If there's a script variable passed to an ordinary function call as an argument, it won't compile:
def script live = var x = 3 ; println(x)
This code will fail to compile with the following error:
error: not found: value _println
However, the following code works perfectly:
var x = 3;
def script live = println(x)
While testing subscript actors, I've came across the problem that akka 2.3.2 throwed exceptions that some core class is not found (like, class PartialFunction). Akka 2.3.0 worked OK. Then, after updating my repository with new commits, Akka 2.3.0 stopped working for very same cause, and akka 2.3.2 worked normally.
This means there may be compatibility issues with other libs too, which significantly decteases usability. Though it is not vital for the moment and doesn't harm our current tasks significantly, I think we should investigate on question when we have less workload.
Consider following program:
val script = [doSomething]
println(x)
In AST view, it will translate to:
val `script` = subscript.DSL._script(this, scala.Symbol("<lambda>"))(ScriptApply(<empty>, List(doSomething))).println(x);
Apparently, ".println(x)" at the end is not what we want.
There are 2 workarounds, both clumsy though:
val script = [doSomething];
println(x)
(note that both semicolon and extra line break are required, or it will not work)
Or:
val script = ([doSomething])
println(x)
As for now, "/ .." seems not to break activation. Observe the following script in the debugger:
def script..
live = process(1) / ..
process(x: Int) = {println(x)}
Phrases such as while,_
should be recognized as syntactic sugar for b => while,b
. This is much like the mechanism in plain Scala.
Then we will be able to write
exitCommand; confirmationDialog ==> while(!_)
"Step" button not always reacts to clicks; if it doesn't react once, then no button will react to the click after that time.
It seems that this bug depends on processor load. If, for example, you load one of the cores of the processor at 100% (say, by opening a new Scala console and typing "while (true) {}"), the chance for the "Step" to ignore clicks increases dramatically: in fact, it always fails to react on second or third click.
I don't know, however, whether the bug is only specific to my machine or does it affect other systems too.
To reproduce the bug:
Tolya mailed:
'.' operator functions not exactly the way it is supposed to. Basically, if you write: a & . & b & c, then 'c' will never be activated. That is, the process will go the following way:
- Activate '&'
- Activate 'a'
- Activate '.'
- Stop activation
- Start 'a'
- Activate 'b'
- Start 'b'
The way it should go:
...
6. Activate 'b'
7. Activate 'c'
8. Start 'b'
...
The build procedure for Scalac is vulnerable. I changed Parsers.scala
to a point that it could not parse subscript scripts properly any more.
Unfortunately I did also an ant all.clean
so that the 'locker' scalac had to be compiled; and this locker is used to compile Predef.scala
, subscript-swing
etc.
These did not compile any more, but they are needed to get Jars with which I could test the compiler, using the command gradle --debug publish-local -Ddocs.skip=1
.
I bypassed this by renaming Predef.scala
to something not ending in .scala
, and by commenting-out subscript-swing
etc in build.xml
. In a restructured Compiler we should avoid such problems.
Consider the following script:
live = (break ; println("Hello")) + println("World")
Here, the following happens:
break
makes ;
to have Success immediately on its activation+
receives Success message+
emits the Success message upwards the graph and activates its another operand, println("World")
This behaviour starts to cause a real trouble in the following script:
live = ((break ; println("Hello")) + println("World")) ...
Because +
emits Success
too early, the topmost sequential operator also receives it too early and activates the next operand, ...
while the first one, +
still has work to do (it waits for the println("World")
execution. Hence, causing an infinite activation loop without anything every outputted.
Right now, ScriptExecutor operates on several collections that can interact with threads other then the VM thread and, therefore, require heavy synchronization while accessing this collections. This issue is not critical, but may impact performance, as well as make the code less structured and more chaotic. Therefore, it's important to develop as lightweight synchronization strategy as possible (ideally - one synchronization per VM event loop cycle) and implement it.
Input:
def script..
key(??c: Char) = (-)
main(args: Array[String]) = val c: Char = ' '
key,?c {println(s"key => $c")}
key,'x' {println(s"key <= x=> $c")}
yielded
[quick.subscript] error: terms in comma expression should be path or literal
[quick.subscript] key,?c println(s"key => $c")
[quick.subscript] ^
It is not a problem for the 'x' parameter.
Putting the parameter ?c between classic parentheses makes the error message disappear. So actual ? and ?? parameters are currently not allowed in comma expressions, but they should be.
There is another problem, popping up when ?c is between parentheses:
[quick.subscript] error: not found: value _println
[quick.subscript] key(?c) println(s"key => $c")
[quick.subscript] ^
This goes away when println(...) is put between braces, but that should not be necessary.
Life: when starting;stopping;... at some point most of the buttons are disabled.
LookupFrame2: when typing in the input text field so that it becomes nonempty, occasionally the Search button is unexpectedly not enabled. After canceling a search, occasionally the same happens.
These problems are a little reproducible: they occur on the average after a few tries.
To analyse a log from the SimpleScriptDebugger would be needed.
Consider following program:
def main(args: Array[String]) {
val executor = ScriptExecutorFactory.createScriptExecutor(true)
future {executor run _live()}
executor.invokeFromET {executor.rootNode launch _doSomething()}
}
def script..
live = (* doSomething *)
doSomething = {println("Hello World!")}
Now, the dynamically launched process will be under node. When debugged with graphical debugger, it throws a NullPointerException when handling AAHappened and Success messages intended for root of dynamically launched subtree. Statically launched subtree behaves normally.
Regression:
def test = {
val i = -4
{println(i)}
}
yields
error: Int(-4) does not take parameters
{println(i)}
^
It appears that scanner correctly inserts a NEWLINE after the -4
, but method simpleExpr()
of the Parser parses this number and then continues with simpleExprRest(t, canApply = true)
; the latter starts with
if (canApply) newLineOptWhenFollowedBy(LBRACE)
It seems that the regular Scala compiler (2.11) has the same code; in a worksheet in Eclipse it gives the same problem.
Scala 2.8 online REPL, seems to behave better.
So it seems SubScript is well compatible with the current Scala version. But do we want that?
TemplateNode:
matching on T_n_ary_op (and T_1_ary_op) does not work;
therefore FTTB those classes have their own implementation of kindAsString
(see the comments in the file)
In it's current form, the output of the test case is hardly readable - because there's only one test case. It's hard to understand what's going wrong from the output like:
There was 1 failure:
1) testBehaviours(subscript.test.OperatorsSuite)
java.lang.AssertionError: expectedAtomsAtEndOfInput=b required=
I propose to create several test methods for several set of cases (say, one for usual operators, one for optional breaks etc), and also make sure that each entry of the specification list can manifest itself in case of failure so that we see at once what specification rule have failed.
Also, on failure, a long exception is thrown, java.lang.AssertionError. Test frameworks like jUnit usually have their own means of doing assertions, and if it fails they usually output this fact in a more elegant way then an exception. So I think we should use jUnit-specific assertions rather then scala native assertions.
According to the syntax the following is legal:
@{test}: println
However, because of a quick hack with annotations, the method call needs to be enclosed in braces, FTTB:
@{test}: {println}
The reason is that during the parser phase, for the annotation some code is generated of the form
DSL._at(_here: N_annotation[N_call[Any], T_call[Any]])
whereas it can only become known at compile time whether the RHS operand of @:
is a script call or a method call; in the latter case the parser should have had generated ...N_code_normal[Any]...
, but it could not know that then.
This bug may be repaired using a major design in the compiler: the parser should not do transformations; there should be proper ASTs for all subscript constructs, and only in the typer phase these should be transformed to regular Scala code.
In ACP atomic actions are really atomic. In SubScript these are much related to code fragments. However, some kinds of code fragments may take longer than just a conceptual moment:
To handle such cases a distinction has been made between the start and the end of atomic actions, even though this is self-contradictory. As a result we have now AAEnded messages sent around in the call graph that immediately follow AAStarted messages, namely for the normal code fragments.
As we are now in the process of defining the optional break behavior in the context of parallelism, it appears necessary to have a simple notion of an atomic action, and to stick close to ACP terminology. That is: an atomic action may happen, which means start and end at the same time. So we should replace the AAStarted and AAEnded messages by AAHappened messages.
For first 3 kinds of long lasting code fragments listed above, two of such AAHappened messages should be sent: one the start of the code fragment (say the opening brace) and one for the end of the fragment (the closing brace). A loopingEventHandlingCodeFragment should issue an AAHappened message each time when such an event is handled.
A few weeks or months ago I changed generated code from
here => ...
to
_node => implicit val here; ...
so that here
became implicitly available.
However, this was not really needed, since the following does the job:
implicit here => ...
as explained here on StackOverflow
So the generated code should now be simplified.
It should be possible to shorten
var param:AType = null; scriptCall(?param); {use param}
to
scriptCall(?param:AType); {use param}
So the second form is short hand for the first one.
Note that a var or val script expression should be the direct non-leftmost operand of a sequential operator; the identifier is then defined more to the right of the declaration.
^
Result CatchersScript calls and code fragments may be followed by result catchers: a caret(^
) optionally followed by a simple variable designator
There is no spacing allowed, to prevent ambiguity of the optional designator with a script call.
If such a variable is not specified explicitly after the caret, the script result value $result
is assumed.
An alternative would be to catch the result-exception value in a Try[]
; FTTB we stick to the result.
A variable name that starts with $
(except for $
, $result
and $failure
) is implicitly declared as local variable in the script, with an appropriate type:
the most specific common supertype of all the result types of the calls and code fragments that the variable appears next to as result catcher.
If a script body consists of just a code fragment or a script call without a result catcher, an implicit one is assumed for the script result value. (Maybe widen this condition to activation code containing a code fragment or calls)
If the calling script has no explicit result type declared, then the result type becomes the most specific common supertype of its script calls and code fragments that have result catchers for $result.
In this respect the script calls and code fragments body of a script lambda are normally supposed to belong to that lambda, rather than to the enclosing script.
Note that the operands of the data flow operator are lambdas. It makes sense that in a ~~> b
the result value from a goes to b. However, the entire dataflow construct should be seen as a lambda in itself; its result value will be fed by the RHS (b). This is something still to be worked out.
Probably a ~~> b^
should be interpreted as (a ~~> (b^))^
,
and a ~~> b ~/~> c ^
as (a ~~> (b^) ~/~> (c^))^
.
a ~~> b^ ~/~> c
would not be illegal, but it would make no sense.
The caret may be implemented in two ways: as a separate node or as a optional parameter to the DSL methods _call, _normal etc.
This parameter would then have type
(Symbol, N_Call[R] => Unit)
Given an actual result catcher ^$
of type R the parameter would be something like:
`$ -> ((here:N_Call[R])=> $ = here.getResult)
The symbol part is for debugging: the node from which the result value is grabbed gets in the graphical debugger an extra label with actual value information, such as ^$=10
.
Consider the following script:
def script live = (
val i = 4
val j = i
)
It will fail to compile, because "i cannot be found".
This is so, because the right-hand side of the assignment is supposed to be an ordinary Scala code that doesn't belong to any node, and, hence, doesn't have a here
variable in scope. Hence, i
can't be evaluated.
A workaround goes as follows:
def script live = (
val i = 4
val j: Int = 0
{j = i}
)
A good way to solve the problem would be to include the implicit here
variable to the right-hand side block of the assignment. The here
variable can evaluate to the n-ary operator the assignment belongs to - the sequential operator in the latter example.
Consider the following script:
def script live = (
if (false) then ({println("Hello")})
{println("World")}
)
An expected and intuitive behaviour would be an output "World". However, nothing is outputted, since println("World")
is also considered to be an operand of then
.
Same thing happens in case of else
:
def script live = (
if (false) then ({println("Hello")}) else ({println("Not hello")})
{println("World")}
)
Here, println("World")
is referred to else
.
One workaround is to place a semicolon after the if-then-else
statement. A semicolon has higher priority then spaces and does the right thing here:
def script live = (
if (false) then ({println("Hello")}) else ({println("Not hello")});
{println("World")}
)
Another workaround is to enclose the if-then-else
statement into parentheses, which will also do the proper prioritization:
def script live = (
(if (true) then ({println("Hello")}) else ({println("Not hello")}))
{println("World")}
)
I don't know whether it was intended to work so or maybe it is a bug. But it results in a very unintuitive behaviour and produces errors which are hard to trace.
An intuitive behaviour would be to execute just only one operand after then
and just one operand after else
, not all the operands left in this n-ary operator. In case if many operands need to be executed, one can enclose them in parentheses so that the compiler would treat them as a single operand. This is more intuitive - both for people and for the compiler.
Consider following program:
class SimpleActor extends SubScriptActor {
def script live = {println("I exist")}
}
def main(args: Array[String]) {
val as = ActorSystem()
for (_ <- 1 to 10) as actorOf Props[SimpleActor]
}
Now, if you run it without a debugger (with abt run), everything happens as expected. You get 10 "I exist" messages at the console in every run.
However, if you run it with a debugger (abt debug), strange things happen.
First, even before "Step" button is clicked, there's a random amount (up to 10) of "I exist" messages at the console (this means, SubScript VM starts some processing without being blocked by the debugger).
Second, not every actor is spawned. Some InvokeFromET messages (used to dynamically and sequentially activate subscript actors templates from the main graph) are observable in the message queue (also random amount of them), but it's often the case that debugger doesn't process them, simply saying "Waiting" in the current message window when it's time to process them.
I suppose that somehow it is related to the dynamic nature of actor templates activation.
For event handling code fragments add a method "execute", so that these will be easier to use, without explictly refer to a CodeExecutor.
I already used this new method in the paper submitted to the Scala Workshop 2014.
Output parameters (?p) and constrained parameters (??p) are not yet supported for script lambda's. A lambda expression such as
(?i:Int) => [expr]
should be parsed well and transformed into
(_i:FormalOutputParameter[Int]) => [expr (with i replaced by _i.value)]
etc., like scriptLocalDataTransformer does in https://github.com/AndreVanDelft/scala/blob/develop/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala (around line 1700).
A type
?Int => Script
should be parsed and rewritten into
FormalOutputParameter[Int] => Script
The hard part will be the parsing: accept the optional ? and ?? at the start of a parameter list, just after the '(' (or after "case" in a partial function), or maybe even harder: just before a type name.
It may be worthwile to support all these transformations as well for normal methods and for class parameters, rather than for scripts only. The Scala code in https://github.com/AndreVanDelft/scala/blob/develop/src/subscript/subscript/swing/Scripts.scala contains currently phrases such as
case class KeyTypedReactor[...](..., keyCode: FormalConstrainedParameter[Char]) ... {
...
if (keyCode.matches(char)) {
keyCode.value = char
...
}
...
}
Instead we should be able to write:
case class KeyTypedReactor[...](..., ??keyCode:Char) ... {
...
if (_keyCode.matches(char)) {
keyCode = char
...
}
...
}
To have less parentheses, there is currently syntax
print,i
During a discussion on Reddit it appeared that a colon would look better:
print: i
val executor = ScriptExecutorFactory.createScriptExecutor(true)
def node = {
val n = N_n_ary_op(null, false)
n.index = scala.util.Random.nextInt(100)
n
}
executor.callGraphMessages += Activation(node)
executor.callGraphMessages += Continuation(node)
executor.callGraphMessages += Activation(node)
executor.callGraphMessages += Continuation(node)
for (msg <- executor.callGraphMessages) println(msg)
If you run this code, you'll see that the messages go exactly in order they were added:
-1 Activation 29 null
-1 Continuation 20 null {} 20 null S=false nActivated=0 (=0S+0N)
-1 Activation 93 null
-1 Continuation 0 null {} 0 null S=false nActivated=0 (=0S+0N)
Expected result would be that Activation messages go prior to Continuation messages because they have higher priority. This is very strange, I have no idea why this may be the case - code in SubScript classes seems to be perfectly valid. This also may be exactly the cause of issue #22, because there the behaviour was exactly like messages were added in random order and remained in that order.
From SubScriptActor:
override def aroundReceive(receive: Actor.Receive, msg: Any) {
...
// If a message was handled, Akka will try to match it against a function that can handle any message
// otherwise it will try to match the message against function that can handle virtually nothing
// (except LocalObject, which is certainly local and can't be available from the outside)
case object LocalObject
super.aroundReceive(if (messageWasHandled) {case _: Any =>} else {case LocalObject =>}, msg)
}
Would it not be handier to support message handling by a regular receive method as fall back option? Then we would not need the LocalObject; the lines would be:
// If a message had been handled here, Akka should not cause it to be handled again;
// yet we must call now super.aroundReceive.
// We do so with, in place for the receive parameter,
// a partial function that can handle any message, and that does nothing.
// If the message was not handled, then we allow the actor to handle the message in its receive method.
// This is a fall back option with lightweight message handling
// that is supposed not to interfere with the handling in scripts
super.aroundReceive(if (messageWasHandled) {case _: Any =>} else receive, msg)
~~> depends on both "do-then-else" and script return values
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.