Dollar Scripting Language Manual

Introduction

What is Dollar?

Dollar is a scripting language for the JVM, it is intended to allow the rapid development of prototype applications or occasional use scripts; especially ones dealing with systems integration. Much in the same way that you would write BASH scripts for system programming.

In a Unix shell, like BASH, we largely script the execution of small programs and operate on files. In Dollar we largely script builtin functions and the transfer of data between URIs.

You should find Dollar familiar if you have worked with BASH and Java as it borrows idioms from both. The exception is the use of reactive programming which may be a little novel to the reader.

When should I use it?

Dollar is intended to allow the rapid development of prototype applications, small hacks and occasional use scripts; especially ones dealing with systems integration.

Although Dollar is intended to be a complete programming language it is not suited to large scale application development. For that, the author recommends sticking to Java or a similar strongly typed general purpose languages.

Executable Documentation

Everything in this documentation is executed as part of the build process, so all the examples are guaranteed to run with the latest master branch of Dollar.

Yep Dollar can actually run Markdown files, in fact the source file that this page was built from starts with:

#!/usr/bin/env dollar

So it can be executed directly from a Unix command line.

The source for this page (minus that header) is here

Getting Started

NOTE: At present only Mac OS X and 64 Bit Ubuntu Linux is supported, however since Dollar is entirely based in Java it’s trivial to port to other systems. Please add an issue on the GitHub project specifying the platform you’d like to help support.

First download the Dollar scripting runtime from distribution

Make sure dollar/bin is on your PATH.

Run dollar <filename> to execute a Dollar script.

Here is an example of what Dollar looks like


def testParams {$2 + " " + $1}

@@ testParams ("Hello", "World") 

.: testParams ("Hello", "World") == "World Hello"

Understanding the Basics

Dollar has it’s own peculiarities, mostly these exists to help with it’s major target: JVM based integration projects. So it’s important to understand the basic concepts before getting started.

Coding Conventions

I’ve put these at the beginning knowing that they won’t make sense until you’ve read the whole document, so skip this if you need; but having them at the beginning will be handy if you refer back to this document later.

  • Classes and Types in CamelCase
  • variables, functions and fields, in lowerCamelCase
  • CONSTANT_VALUES should be in UPPERCASE
  • BUILTIN functions are in UPPERCASE
  • keywords are in lower case

Use def for functions rather than const. Unless otherwise required define a function using a block not a list or map.

When choosing to use a keyword or symbol for an operator that supports both e.g. print/@@ choose the keyword if the visual complexity is too high and choose the symbol if brevity adds clarity. If you’re unsure err on the side of keywords.

The following however should usually be used in their operator forms for most scripts as they are widely used and therefore easy for a new developer to pick up: @@, .:, <->, <=>, #, <<, >>, :-.

An example of when the keyword forms are more useful is the writing of test scripts, in which case print, assert, is, always etc. are clearer than @@, .:, <->, <=> to the reader.

Reactive Programming

Dollar expressions are by default lazy, this is really important to understand otherwise you may get some surprises. This lazy evaluation is combined with a simple event system to make Dollar a reactive programming language by default.

The simplest way to understand reactive programming is to imagine you are using a spreadsheet. When you say a cell has the value SUM(A1:A4) that value will react to changes in any of the cells from A1 to A4. Dollar works the same way by default, however you can also fix values when you want to write procedural code.

Let’s see some of that behaviour in action:


var variableA = 1
const variableB := variableA
variableA = 2

.: variableB == 2

In the above example we are assigning the variableA to the value 1, we then declare (using the declarative operator :=) that variableB is the same as variableA. So when we change variableA to 2 we also change variableB to 2.

Before we go any further let’s clarify := vs =, I have chosen to follow the logic described here so that the := operator is a definition (and by it’s nature reactive) and = is an assignment ( not reactive and has an infinite fix depth, more on that later).

This means that a := b + 1 translates to a is defined as b + 1 so a is behaving reactively, changes to b cause a change in the value of a. It also means that a = b + 1 simply assigns b + 1 to the variable a, changes to b do not cause changes to a.

At this point it’s time to introduce a what is arguably a cleaner and easier to understand short hand for ‘const reactiveVar := {…}` the short hand is ‘def reactiveVar {…}’ such as:

    def myFunction { @@ "Hello World"}

The def keyword implies const and it also does not allow dynamic variable names (more on that later). A rule of thumb is if you’d like to have something act like a function use def.

TL;DR = behaves like it’s Java equivalent, := doesn’t and use def to create functions.

The assertion operator .: will throw an assertion error if the value following is either non boolean or not true.

Now let’s throw in the causes operator :


var a=1
a causes { @@ $1 }
a=2
a=3
a=4

2
3
4

That simple piece of code will simply output each change made to the variable a, but wait a minute what about …


var b=1
var a=1
a + b + 1 causes { @@ "a=" + a + ", b=" + b}
a=2
a=3
a=4
b=2
a=2, b=1
a=3, b=1
a=4, b=1
a=4, b=2

Yep, you can write reactive expressions based on collections or arbitrary expressions. When any component changes the right hand side is re-evaluated (the actual value that changed is passed in as $1).

But it’s even simpler than that, many of Dollars operators are reactive themselves. That means they understand changes to their values. Take @@ (or print) as an example:

var b=1
@@b
b=2

Outputs 1 then 2 because @@ heard the change to b and re output the new value. Often this is what you want, however if you don’t just add the fix operator & before the value. That will stop reactive behaviour.

var b=1
@@ &b
b=2

Functional Programming and the ‘pure’ operator

Support for functional programming is included in Dollar, this will be widened as the language is developed. For now it is provided by the pure operator. This signals that an expression or declaration is a pure expression or function.

In this example we’re declaring reverse to be an expression that reverses two values from a supplied array. Because we declare it as pure the expression supplied must also be pure. To understand what a pure function is please see http://en.wikipedia.org/wiki/Pure_function. Basically it prohibits the reading of external state or the setting of external state. We next swap [2,1] within a newly created pure expression, which is subsequently assigned to a. If reverse had not been declared pure it would not be allowed within the pure expression.

 pure def reverse [$1[1],$1[0]]

 a= pure {
     reverse([2,1])
 }

Note some builtin functions are not themselves pure and will trigger parser errors if you attempt to use them in a pure expression. Take DATE() for example which supplies an external state (the computers clock).

Assignment and Definition

Assignment


var variableA = 1
var variableB = variableA
variableA = 2

.: variableB == 1

So as you can see when we use the = assignment operator we assign the value of the right hand side to the variable.

The assignment operator = has an infinite ‘fix’ depth This means that any expression will be evaluated completely also it means the result is not reactive.

The always operator <=> will compare two values and throw an exception if they are not the same at any point at or after the expression, ` a <=> b is the same as .: a == b`.

The assert equals operator <-> will compare two values only at the point that the expression occurs. It is roughly the same as .equals() in Java and is the equivalent of .: &a == &b

Definition

There are two ways of using definitions in Dollar, they are semantically the same but syntactically different. Firstly we can just use the := definition operator. This is not an assignment in the sense that the variable being defined is in fact being assigned the expression on the right hand side. Not the value of the expression.


const lambdaVar :=  {$1 + 10}
lambdaVar(5) <=> 15

In the above example we have parametrized the expression lambdaVar with the value 5 and got the value 15. So we can clearly see that lambdaVar is an expression (or lambda) in this case, not a fixed value.

The above looks a lot like a function doesn’t it. So to add a little syntactic sugar you can also declare the exact same expression using the def syntax below.


def lambdaVar  {$1 + 10}
lambdaVar(5) <=> 15

Note that def implies const, def means define and therefore not variable.

Summary

It’s important to note that all values in Dollar are immutable - that means if you wish to change the value of a variable you must reassign a new value to the variable. For example v++ would return the value of v+1 it does not increment v. If however you want to assign a constant value, one that is both immutable and cannot be reassigned, just use the const modifier at the variable declaration.

const MEDIUM = 23
// MEDIUM= 4 would now produce an error

So := supports the full reactive behaviour of Dollar, i.e. it is a definition not a value assignment, and = is used to nail down a particular value or reduce the reactive behaviour. Later we’ll come across the fix operator & which instructs Dollar to fix a value completely.

Blocks

Line Block

Dollar supports several block types, the first is the ‘line block’ a line block lies between { and } and is separated by either newlines or ; characters.


var myBlock = {
    "Hello "
    "World"
}

myBlock <=> "World"

const myBlock2 = {1;2}

myBlock2 <=> 2

When a line block is evaluated the result is the value of the last entry. For advanced users note that all lines will be evaluated, the value is just ignored. A line block behaves a lot like a function in an imperative language.

List Block

Next we have the list block, the list block preserves all the values each part is separated by either a , or a newline but is delimited by [ and ].


var list = [
    "Hello "
    "World"
]

list <=> ["Hello ","World"]

const list2 = [1,2]

list2 <=> [1,2]

Map Block

Finally we have the map block, when an map block is evaluated the result is the aggregation of the parts from top to bottom into a map. The map block starts and finishes with the { } braces, however each part is separated by a , or newline not a ;. The default behaviour of a map block is virtually useless, it takes each value and makes it’s String value the key and the original value is the value paired with that key.


var mapBlock = {
    "Hello",
    "World"
}

mapBlock <=> {"Hello":"Hello", "World":"World"}

const mapBlock2 = { 1, 2}

mapBlock2 <=> {"1":1,"2":2}

Map blocks are combined with the pair : operator to become useful and create maps/JSON like this:


var mapBlock = {
    "first":"Hello ",
   "second":"World"
}

@@ mapBlock

mapBlock.second <=> "World"

A map block with one entry that is not a pair is assumed to be a Line Block.

The stdout operator @@ is used to send a value to stdout in it’s serialized (JSON) format, so the result of the above would be to output {"first":"Hello ","second":"World"} a JSON object created using JSON like syntax. Maps can also be created by joining pairs.


var pair1 = "first" : "Hello ";
var pair2 = "second" : "World";

.: pair1 + pair2 == {"first":"Hello ","second":"World"}

Dollar maps are also associative arrays (like JavaScript) allowing you to request members from them using the list subscript syntax

{"key1":1,"key2":2} ["key"+1] <=> 1
{"key1":1,"key2":2} [1] <=> {"key2":2}
{"key1":1,"key2":2} [1]["key2"] <=> 2

As you can see from the example you can request a key/value pair (or Tuple if you like) by it’s position using a numeric subscript. Or you can treat it as an associative array and request an entry by specifying the key name. Any expression can be used as a subscript, numerical values will be used as indexes, otherwise the string value will be used as a key.

Lists

Dollar’s lists are pretty similar to JavaScript arrays. They are defined using the [1,2,3] style syntax and accessed using the x[y] subscript syntax.

.: [1,2,3] + 4 == [1,2,3,4];
.: [1,2,3,4] - 4 == [1,2,3];
.: [] + 1 == [1] ;
.: [1] + [1] == [1,1];
.: [1] + 1 == [1,1];

[1,2,3][1] <=> 2

Note we’re using the assert equals or <=> operator here, this is a combination of .: and == that will cause an error if the two values are not equal.

You can count the size of the list using the size operator #.

#[1,2,3,4] <=> 4

Ranges

Dollar (at present) supports numerical and character ranges using Maven style syntax

In pseudo-code:

(a..b) = {x | a < x < b}
[a..b] = {x | a <= x <= b}
[a..b) = {x | a <= x < b}
(a..b] = {x | a < x <= b}
(a..) = {x | x > a}
[a..) = {x | x >= a}
(..b) = {x | x < b}
(..b] = {x | x <= b}
(..) = all values

Please see the Guava docs for more information on the range format used.


#("a".."c") <=> 1
#["a".."c"] <=> 3
[1..3][1] <=>2

Scopes & Closure

Dollar makes extensive use of code blocks with scope closure. Blocks, lists and maps all have scope closure - I suggest reading this article by Martin Fowler if you’re unfamiliar with closures. Let’s start with a simple example:

var outer=10;
def func {
    outer;
}
func() <=> 10;

In the above example func is a block collection which returns outer. It has access to outer because at the time of declaration outer is in it’s parent’s lexical scope.


def func {
    var inner=10;
    {$1+inner}
}

func()(10) <=> 20;

In the above example we now return an anonymous block collection from func which we then parametrize with the value 10. When func is executed it returns the parametrized block, which we then call with 10 and which adds the value inner to the parameter ($1) - naturally the result is 20.

So all of that looks fairly familiar if you’ve ever used JavaScript, but remember all of Dollar’s collections have scope closure so the following is valid:

var outer=10;

const scopedArray := [$1,outer,{var inner=20;inner}]

scopedArray(5)[0] <=> 5;
scopedArray(5)[1] <=> 10;
scopedArray(5)[2]() <=> 20;

In this example the list has lexical scope closure and when we parametrize it using (5) we can pass in the positional parameter ($1) for when it is evaluated.

Understanding Scopes A Little Deeper

Each parse time scope boundary (blocks, lists, maps, constraints, parameters etc.) is marked as such during the initial parse of the script. When executed each of these will create a runtime scope. Each runtime boundary will create a hierarchy of scopes with the previous being the parent.

Where an executable element with scope closure (such as lists, blocks and maps) is executed all current scopes are saved and attached to that element. So when the element is subsequently executed it retains it’s original lexical closure (as described here).

Please look at the SourceNodeOptions class for the three types of scoped nodes, they are NO_SCOPE which has no effect on the current scope, NEW_SCOPE which creates a new scope but does not have closure and SCOPE_WITH_CLOSURE which creates a new scope with lexical closure.

Error Handling

Error handling couldn’t be simpler. Define an error expression using the error keyword, the expression supplied will be evaluated on an error occurring within any sub scope of the scope in which it is defined. The special variables msg and type will be assigned values.

var errorHappened= false
error { @@ msg; errorHappened= true }
var a= << http://fake.com:99999
.: errorHappened

Logging

Logging is done by the print,debug and err keywords and the @@,!! and !? operators.

Keyword Operator
print @@
debug !!
err !?
@@ "I'm a stdout message"
!! "I'm a debug message"
!? "I'm an error message"

Type System

Intro

Although Dollar has a very loose type system, it does support basic runtime typing and a type prediction system. At present the inbuilt types includes: String, Integer, Decimal, List, Map, URI, Void, Range, Boolean. The value for a type can be checked using the type operator:

.: "Hello World" type String
.: ["Hello World"] type List

Date

Dollar supports a decimal date system where each day is 1.0. This means it’s possible to add and remove days from a date using simple arithmetic.

@@ DATE()
@@ DATE() + 1
@@ DATE() - 1

.: DATE() + "1.0" type String
.: DATE() / "1.0" type Decimal

Components of the date can be accessed using the subscript operators:

@@ DATE().DAY_OF_WEEK

@@ DATE()['DAY_OF_YEAR']=1

Valid values are those from java.time.temporal.ChronoField

NANO_OF_SECOND, NANO_OF_DAY, MICRO_OF_SECOND, MICRO_OF_DAY, MILLI_OF_SECOND, MILLI_OF_DAY, SECOND_OF_MINUTE, SECOND_OF_DAY, MINUTE_OF_HOUR, MINUTE_OF_DAY, HOUR_OF_AMPM, CLOCK_HOUR_OF_AMPM, HOUR_OF_DAY, CLOCK_HOUR_OF_DAY, AMPM_OF_DAY, DAY_OF_WEEK, ALIGNED_DAY_OF_WEEK_IN_MONTH, ALIGNED_DAY_OF_WEEK_IN_YEAR, DAY_OF_MONTH, DAY_OF_YEAR, EPOCH_DAY, ALIGNED_WEEK_OF_MONTH, ALIGNED_WEEK_OF_YEAR, MONTH_OF_YEAR, PROLEPTIC_MONTH, YEAR_OF_ERA, YEAR, ERA, INSTANT_SECONDS, OFFSET_SECONDS

As you can see we can do date arithmetic, but thanks to another Dollar feature anything that can be specified as xxx(i) can also be written i xxx (where i is an integer or decimal and xxx is an identifier). So we can add days hours and seconds to the date.

@@ DATE() + 1 DAY
@@ DATE() + 1 HOUR
@@ DATE() + 1 SEC

Those values are built in, but we can easily define them ourselves.

def fortnight ($1 * 14)

@@ DATE() + 1 fortnight

STRING

INTEGER

DECIMAL

LIST

MAP

URI

VOID

NULL

RANGE

BOOLEAN

Constraints

Although there are limited compile time type constraints (using the predictive type system) in Dollar a runtime type system can be built using constraints. Constraints are declared at the time of variable assignment or declaration. A constraint once declared on a variable cannot be changed. The constraint is placed before the variable name at the time of declaration in parenthesis.

var (it < 100) a = 50
var (previous type Void|| it > previous) b = 5
b=6
b=7
var ( it type String) s="String value"

The special variables it - the current value and previous - the previous value, will be available for the constraint.

To build a simple runtime type system simply declare (using :=) your type as a boolean expression.


//define a pseudo-type
def colorEnum ( it in ["red","green","blue"] )


//Use it as a constraint
var (colorEnum) myColor= "green"

error { @@ msg }

//This fails
var myColor="apple"

Of course since the use of (it type XXXX) is very common Dollar provides a specific runtime type constraint that can be added in conjunction with other constraints. Simply prefix the assignment or declaration with <XXXX> where XXXX is the runtime type.

var <String> (#it > 5) s="String value"

It is intended that the predictive type system combined with runtime types will help to spot a few more bugs at compile time.

Type Coercion

Dollar also supports type coercion, this is done using the as operator followed by the type to coerce to.

var <String> s= 1 as String
s <=> "1"

A few more examples follow.

1 as String <=> "1"
1 as Boolean <=> true
1 as List <=> [1]
1 as Map <=> {"value":1}
1 as Void <=> void
1 as Integer <=> 1

"1" as Integer <=> 1
"http://google.com" as URI
"1" as Void <=> void
"true" as Boolean <=> true
"1" as Boolean <=> false
"1" as List <=> ["1"]
"1" as Map <=> {"value":"1"}
"1" as String <=> "1"

true as String <=> "true"
true as Integer <=> 1
true as List <=> [true]
true as Map <=> {"value":true}
true as Boolean <=> true
true as Void <=> void


[1,2,3] as String <=> "[ 1, 2, 3 ]"
[1,2,3] as List <=> [1,2,3]
[1,2,3] as Boolean <=> true
[1,2,3] as Map <=> {"value":[1,2,3]}

{"a":1,"b":2} as String <=> '{"a":1,"b":2}'
{"a":1,"b":2} as List <=> ["a":1,"b":2]
{"a":1,"b":2} as Boolean <=> true
{"a":1,"b":2} as Void <=> void

Imperative Control Flow

With imperative control flow, the control flow operations are only triggered when the block they are contained within is evaluated. I.e. they behave like control flow in imperative languages. So start with these if you’re just learning Dollar.

If

Dollar supports the usual imperative control flow but, unlike some languages, everything is an operator. This is the general rule of Dollar, everything has a value. Dollar does not have the concept of statements and expressions, just expressions. This means that you can use control flow in an expression.


var a=1
var b= if a==1 2 else 3
b <=> 2

So let’s start with the if operator. The if operator is separate from the else operator, it simply evaluates the condition supplied as the first argument. If that value is boolean and true it evaluates the second argument and returns it’s value; otherwise it returns boolean false.

The else operator is a binary operator which evaluates the left-hand-side (usually the result of an if statement), if that has a value of false then the right-hand-side is evaluated and it’s result returned, otherwise it returns the left-hand-side.

The combined effect of these two operators is to provide the usual if/else/else if/ control flow


var a=5
//Parenthesis added for clarity, not required.
var b= if (a == 1) "one" else if (a == 2) "two" else "more than two"
.: b == "more than two"

For


for i in [1..10] {
    @@ i
}

While

var a= 1
while a < 10 {
 a= a+1
}
a <=> 10

Reactive Control Flow

Causes

Dollar is a reactive programming language, that means that changes to one part of your program can automatically affect another. Consider this a ‘push’ model instead of the usual ‘pull’ model.

Let’s start with the simplest reactive control flow operator, the ‘=>’ or ‘causes’ operator.

var a=1; var b=1

a => (b= a)

a <-> 1 ; b <-> 1

a=2 ; a <-> 2 ; b <-> 2

Okay so reactive programming can melt your head a little. So let’s go through the example step by step.

Firstly we assign fixed values to a and b, we then say that when a changes the action we should take is to assign it’s value to b. Okay now we check to see if the current value of a is equal to 1 (using the imperative assert equals or is operator <->).

We then do the same with b to see if it is 1.

Next we assign a new value of 2 to a. This will immediately (within the same thread) trigger the reactive => operator which is triggered by changes to a. The trigger assigns the value of a to b, so b is now the same as a. The assertions at the end confirm this.

When

Next we have the ‘when’ operator which can be specified as a statement, usually for longer pieces of code. Or as the ? operator, for concise code.


var c=1
var d=1

//When c is greater than 3 assign it's value to d
c > 3 ? (d= c)

c <-> 1; d <-> 1
c= 2; c <-> 2; d <-> 1
c= 5 ; c <-> 5 ; d <-> 5

This is similar to the previous example except that we have to set a value greater than 3 for the action to be taken.

//Note alternative syntax is when <condition> <expression>
var c=1
when c > 3 { @@ c}

Collect

The collect operator listens for changes in the supplied expression adding all the values to a list until the until clause is triggered. It then evaluates the second expression with the values it for the current value, count for the number of messages received since last emission and collected for the collected values. The whole operator itself emits void unless the collection operation is triggered in which case it emits the collection itself. Values can be skipped with an unless clause. Skipped messages increase the count value, so use #collected if you want the number of collected values.


var e=void

//Length is greater than or equal to 4 unless void
var (#it >= 4 || it type Void) collectedValues=void

//count starts at 0 so this means five to collect (except if it contains the value 10)
collect e until count == 4 unless it == 10 {
    print count
    print collected
    collectedValues= collected
}

e=1; e=2; e=3; e=4; e=5; e=6
collectedValues <-> [1,2,3,4,5]
e=7; e=8; e=9; e=10
collectedValues <-> [6,7,8,9]
e=11; e=12; e=13; e=14; e=15; e=16
collectedValues <-> [11,12,13,14,15]

Parameters & Functions

In most programming languages you have the concept of functions and parameters, i.e. you can parametrize blocks of code. In Dollar you can parametrize anything. For example, let’s just take a simple expression that adds two strings together, in reverse order, and pass in two parameters.

($2 + " " + $1)("Hello", "World") <=> "World Hello"

The naming of positional parameters is the same as in shell scripts.

Now if we take this further we can use the declaration operator := to say that a variable is equal to the expression we wish to parametrise, like so:


const testParams := ($2 + " " + $1)
testParams ("Hello", "World") <=> "World Hello"

Yep we built a function just by naming an expression. You can name anything and parametrize it - including maps, lists, blocks and plain old expressions.

What about named parameters, that would be nice.

const testParams := (last + " " + first)
testParams(first="Hello", last="World") <=> "World Hello"

Yep you can use named parameters, then refer to the values by the names passed in.

Resources & URIs

URIs are first class citizen’s in Dollar. They refer to a an arbitrary resource, that can be accessed using the specified protocol and location. Static URIs can be referred to directly without quotation marks, dynamic URIs can be built by casting to a uri using the as operator.

var posts = << https://jsonplaceholder.typicode.com/posts 
var titles = posts each { $1.title }
@@ titles

In this example we’ve requested a single value (using <<) from a uri and assigned the value to posts then we simply iterate over the results using each and each value (passed in to the scope as $1) we extract the title. The each operator returns a list of the results and that is what is passed to standard out.

Using Other Languages

Hopefully you’ll find Dollar a useful and productive language, but there will be many times when you just want to quickly nip out to a bit of another language. To do so, just surround the code in back-ticks and prefix with the languages name. Currently only java is supported but more will be added soon.


var variableA="Hello World"

var java = java `out=in.get(0);` (variableA)

java <=> "Hello World"

Java

A whole bunch of imports are done for you automatically (see below) but you will have to fully qualify any thirdparty libs.

imports dollar.lang.*dollar.internal.runtime.script.api.* com.sillelien.dollar.api.* java.io.* java.math.* java.net.* java.nio.file.* java.util.* java.util.concurrent.* java.util.function.* java.util.prefs.* java.util.regex.* java.util.stream.*

static imports DollarStatic.* dollar.internal.runtime.script.java.JavaScriptingStaticImports.*

The return type will be of type var and is stored in the variable out. The Java snippet also has access to the scope (Scope) object on which you can get and set Dollar variables.

Reactive behaviour is supported on the Scope object with the listen and notify methods on variables. You’ll need to then built your reactivity around those variables or on the out object directly (that’s a pretty advanced topic).

Operators

Iterative Operators

Comparison Operators

Numerical Operators

Dollar support the basic numerical operators +,-,/,*,%,++,– as well as #

Remember ++ and – do not change a variable’s value they are a shorthand for a+1 and a-1 not a=a+1 or a=a-1


 1 + 1 <=> 2
 3 -2 <=> 1
 2 * 2 <=> 4
 5 / 4 <=> 1
 5 % 4 <=> 1
 5.0 /4 <=> 1.25
 # [1,2,3] <=> 3
 # 10 <=> 1
 10++ <=> 11
 10-- <=> 9

And similar to Java, Dollar coerces types as required:

.: (1 - 1.0) type Decimal
.: (1.0 - 1.0) type Decimal
.: (1.0 - 1) type Decimal
.: (1 - 1) type Integer

.: (1 + 1.0) type Decimal
.: (1.0 + 1.0) type Decimal
.: (1.0 + 1) type Decimal
.: (1 + 1) type Integer

.: 1 / 1 type Integer
.: 1 / 1.0 type Decimal
.: 2.0 / 1 type Decimal
.: 2.0 / 1.0 type Decimal

.: 1 * 1 type Integer
.: 1 * 1.0 type Decimal
.: 2.0 * 1 type Decimal
.: 2.0 * 1.0 type Decimal


.: 1 % 1 type Integer
.: 1 % 1.0 type Decimal
.: 2.0 % 1 type Decimal
.: 2.0 % 1.0 type Decimal
.: ABS(1) type Integer
.: ABS(1.0) type Decimal

Logical Operators

Dollar supports the basic logical operators &&,   ,! as well as the truthy operator ~ and the default operator :-.

Truthy

The truthy operator ~ converts any value to a boolean by applying the rule that: void is false, 0 is false, “” is false, empty list is false, empty map is false - all else is true.


.: ~ [1,2,3]
.: ! ~ []
.: ~ "anything"
.: ! ~ ""
.: ~ 1
.: ! ~ 0
.: ! ~ {void}
.:  ~ {"a" : 1}
.: ! ~ void

void :- "Hello" <=> "Hello"
1 :- "Hello" <=> 1

Boolean Operators

The shortcut operators || and && work the same as in Java. As do the comparison operators >,< etc. They also have keyword alternatives such as and or or.

Keyword Operator Java Equivalent
and && &&
or ǀǀ ǀǀ
equal == .equals()
  != ! .equals()
  < lhs.compareTo(rhs) < 0
  > lhs.compareTo(rhs) > 0
  <= lhs.compareTo(rhs) <= 0
  >= lhs.compareTo(rhs) >= 0

Examples:

true && true <=> true
true && false <=> false
false && true <=> false
false && false <=> false

true and true always true

true || true <=> true
true || false <=> true
false || true <=> true
false || false <=> false

false or false always false

.: 1 < 2
.: 3 > 2
.: 1 <= 1
.: 1 <= 2
.: 1 > 0
.: 1 >= 1
.: 2 >= 1
.: 1 == 1
.: "Hello" == "Hello"
.: "abc" < "abd"

Default Operator

The default operator ‘:-‘ (keyword default) returns the left hand side if it is not VOID otherwise it returns the right hand side.

void :- "Hello" <=> "Hello"
1 :- "Hello" <=> 1
void default 2 <=> 2

Pipe Operators

Remaining Operators

Imports & Modules

Import

Modules

Modules can be imported using the module keyword and a string representing in URI format the location of the module. At present the standard format is the Github locator so we’re going to look at that first.

const chat:= module "github:neilellis:dollar-example-module::chat.ds" (channel="test")
var sub= chat.server()
chat.stop_()

Let’s start by breaking down the module URI. Okay our first part says it’s the GitHub scheme and so the GitHub module locator will be used. Next we say the repository belongs to neilellis and the repository is called dollar-example-module. The next part is the optional branch indicator - here we are using the master, so we just leave that empty. Finally we can optionally supply the name of a single script we want to import. If we don’t provide that then the main script from the modules ‘module.json’ file will be used.

The source for the module can be found here: https://github.com/neilellis/dollar-example-module

You will need to have the git command on your path and to have access to the repository using git clone.

The GitHub resolver will checkout the specified repository and store it under ~/.dollar/runtime/modules/github/<username>/<repo-name>/<branch> all further interaction with the module will then be done from the checked out version. If you already have a version checked out a git pull will be done to update the branch.

const hello := module "github:neilellis:dollar-example-module:0.1.0:branch.ds"
@@ hello

Module Locators

Writing Modules

Modules consist of a file called module.json with the name of the main script for the module and an optional array of Maven style java dependencies. And then one or more Dollar files.

//TODO: change module.json to module.ds

{
"main":"chat.ds",
"dependencies":["org.twitter4j:twitter4j-core:4.0.2"]
}

The Dollar files should use the export modifier on assignments that it wishes to make available to client applications and it can refer to variables that don’t exist, in which case values for those variables will need to be passed as parameters to the module declaration in the client application.

var redis= ("redis://localhost:6379/" + ${channel :- "test"}) as URI
var www= (("http:get://127.0.0.1:8111/" + ${channel :- "test"}) as URI)

export def server  {
           www subscribe {
            $1.params >> redis
            { body :  all redis }
        }
    };

export def stop_ {STOP(www);STOP(redis); @@ [STATE(www),STATE(redis)]}

export def state_ [STATE(www),STATE(redis)]

Builtin Functions

Concurrency & Threads

Notes:

All types are immutable, including collections. You cannot reassign a variable from a different thread unless it is declared as volatile.

Parallel & Serial Lists

The parallel operator |:| or parallel causes a list to be evaluated in parallel, otherwise it is executed in serial even if the current expression is being evaluated in parallel.


const a = [ TIME(), {SLEEP(1 SEC); TIME();}, TIME() ];
const b = |:| [ TIME(), {SLEEP(1 SEC); TIME();}, TIME() ];
//Test different execution orders
.: a[2] >= a[1]
.: b[2] < b[1]

Fork

The fork operator -< or fork will cause an expression to be evaluated in the background and any reference to the forked expression will block until a value is ready.

const sleepTime := {@@ "Background Sleeping";SLEEP(4 SECS); @@ "Background Finished Sleeping";TIME()}
//Any future reference to c will block until c has completed evaluation
var c= fork sleepTime
SLEEP(1 SEC)
@@ "Main thread sleeping ..."
SLEEP(2 SECS)
@@ "Main thread finished sleeping ..."
var d= TIME()
.: c > d

In the example the value of c is greater than d because the value of c is evaluated in the background. Note that as soon as you make use of the value of c you block until the value is ready. This is exactly the same as Java’s Futures.

Advanced Topics

TODO


T E S T S ——————————————————- Running dollar.internal.runtime.script.ParserMainTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.203 sec - in dollar.internal.runtime.script.ParserMainTest

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

Appendix A - Operators

all or <@

reactive impure No Scope Inherited Execution

('<@'|'all') <expression>

Returns a non-destructive read of all the values of a collection or URI addressed resource.

var posts = <@ https://jsonplaceholder.typicode.com/posts

always or <=>

reactive pure No Scope Inherited Execution

<expression> ('<=>'|'always') <expression>

Asserts that the left-hand-side is always equal to the right-hand-side.

def lamdaVar  {$1 + 10}
lamdaVar(5) <=> 15
lamdaVar(5) always 15

and or &&

reactive pure No Scope Inherited Execution

<expression> ('&&'|'and') <expression>

Returns the logical ‘and’ of two expressions, e.g. a && b. Just like in Java it will shortcut, so that if the left-hand-side is false the right-hand-side is never evaluated.

true && true <=> true
true && false <=> false
false && true <=> false
false && false <=> false

true and true <=> true
false and true always false

assert or .:

reactive pure No Scope Inherited Execution

('.:'|'assert') <expression>

The assertion opeartor is used to assert that an expression holds true. It is a reactive operator such that it is evaluated when the right-hand-side expression changes. so .: a > 10 is asserting that a is always greater than 10. To avoid reactive behaviour use the fix operator such as .: &a > 10 which means that when this statement is evaluated the value of a is compared with 10 - if at this point it is not greater than 10 then the assertion will fail.

.: 1 < 2
.: 3 > 2
.: 1 <= 1
.: 1 <= 2

assert 1 < 2

= (assign)

reactive pure No Scope Inherited Execution

Values can be assigned to a variable using the standard = assignment operator. A variable is declared using either ‘var’, ‘const’ or ‘volatile’.

var is used to mark a simple declaration of a mutable variable. E.g. var a = 1; a= 2.

const is used to declare a readonly variable, since all values in Dollar are immutable this makes the variable both readonly and immutable.

volatile is used to declare the variable as mutable from multiple threads.

Although there are no compile type constraints in Dollar a runtime type system can be built using constraints. Constraints are declared at the time of variable assignment or declaration. A constraint once declared on a variable cannot be changed. The constraint is placed before the variable name at the time of declaration in parenthesis.

Of course since the use of (it is XXXX) is very common, so Dollar provides a specific runtime type constraint that can be added in conjunction with other constraints. Simply prefix the assignment or decleration with <XXXX> where XXXX is the runtime type.

Values can be also be marked for export as in Javascript.

var (it < 100) a = 50
var (previous type Void || it > previous ) b = 5
b=6
b=7
var ( it type String) s1="String value"
var <String> (#it > 5) s2="String value"
const immutableValue= "Hello World"

avg or [%]

reactive pure No Scope Inherited Execution

( 'avg' <list-expression> ) | ( <list-expression> '[%]' )

The average operation avg or [%] calculates the average value of a list.

[1,2,3,4,5][%] <=> 3
avg [1,2,3,4,5] <=> 3

block

reactive impure New Scope Inherited Execution

'{' ( <expression> ';' ) * [ <expression> ] '}'

A line block lies between { and } and is separated by either newlines or ; characters.


var myBlock = {
    "Hello "
    "World"
}

myBlock <=> "World"

const myBlock2 = {1;2}

myBlock2 <=> 2

When a line block is evaluated the result is the value of the last entry. For advanced users note that all lines will be evaluated, the value is just ignored. A line block behaves a lot like a function in an imperative language.

const aBlock := {
        @@ "The first command"
        @@ "The second command"
}

builtin

reactive impure No Scope Inherited Execution

<name> (<parameter>)*

Dollar has many built-in functions.

//TODO: document them

var now= DATE();

cast

reactive pure No Scope Inherited Execution

<expression> 'as' <type>

Dollar supports type coercion using the as operator followed by the type to coerce to.

1 as String <=> "1"
1 as Boolean <=> true
1 as List <=> [1]
1 as Map <=> {"value":1}
1 as Void <=> void
1 as Integer <=> 1

"1" as Integer <=> 1
"http://google.com" as URI
"1" as Void <=> void
"true" as Boolean <=> true
"1" as Boolean <=> false
"1" as List <=> ["1"]
"1" as Map <=> {"value":"1"}
"1" as String <=> "1"

true as String <=> "true"
true as Integer <=> 1
true as List <=> [true]
true as Map <=> {"value":true}
true as Boolean <=> true
true as Void <=> void


[1,2,3] as String <=> "[ 1, 2, 3 ]"
[1,2,3] as List <=> [1,2,3]
[1,2,3] as Boolean <=> true
[1,2,3] as Map <=> {"value":[1,2,3]}

{"a":1,"b":2} as String <=> '{"a":1,"b":2}'
{"a":1,"b":2} as List <=> ["a":1,"b":2]
{"a":1,"b":2} as Boolean <=> true
{"a":1,"b":2} as Void <=> void

causes or =>

reactive pure No Scope Inherited Execution

<expression> ('=>'|'causes') <expression>

The causes operator is used to link a reactive expression to an imperative action. The left-hand-side is any expression and the right hand-side is any expression that will be evaluated when the left-hand-side is updated such as a+b => {@@ a; @@ b}.

var a=1; var b=1

a => (b= a)

&a <=> 1 ; &b <=> 1

a=2 ; &a <=> 2 ; &b <=> 2

choose or ?*

reactive pure No Scope Inherited Execution

<expression> ('?*'|'choose') <expression>

The choose operator is a simple reversal of the subscript operator used for code clarity.

var value= "red";
value choose {"red": "roses", "green": "tomatoes"} <-> "roses"

class

reactive impure No Scope Inherited Execution

'class' <identifier> <expression>

class MyClass {
    <String> name=$1;
    <Integer> age=$2;
    def updateAge {
          this.age=$1
    }
}

<MyClass> clazz= new MyClass("Neil",47);
clazz.name <=> "Neil"
clazz.age <=> 47

//Objects are immutable, just like all types in Dollar
//When you perform a mutation operation you get a new instance back with
//the change made.
var newClazz= clazz.updateAge(20)

//So this hasn't changed
clazz.age <=> 47
newClazz.age <=> 20


collect

non-reactive pure New Scope Inherited Execution

collect <expression> [ 'until' <expression> ] [ 'unless' <expression> ] <expression>

The collect operator listens for changes in the supplied expression adding all the values to a list until the until clause is triggered. It then evaluates the second expression with the values it for the current value, count for the number of messages received since last emission and collected for the collected values. The whole operator itself emits void unless the collection operation is triggered in which case it emits the collection itself. Values can be skipped with an unless clause. Skipped messages increase the count value, so use #collected if you want the number of collected values.

var e=void

//Length is greater than or equal to 4 unless void
var (#it >= 4 || it type Void) collectedValues=void

//count starts at 0 so this means five to collect (except if it contains the value 10)
collect e until count == 4 unless it == 10{
    print count
    print collected
    collectedValues= collected
}

e=1; e=2; e=3; e=4; e=5; e=6
collectedValues is [1,2,3,4,5]
e=7; e=8; e=9; e=10
collectedValues is [6,7,8,9]
e=11; e=12; e=13; e=14; e=15; e=16
collectedValues is [11,12,13,14,15]

create or |||>

reactive impure No Scope Inherited Execution

('|||>'|'create') <expression>

Creates a service described typically by a URI.


debug or !!

reactive impure No Scope Inherited Execution

('!!'|'debug') <expression>

Sends the result of the right-hand-side to the debug log.

!! "I'm a debug message"

-- (decrement)

reactive pure No Scope Inherited Execution

'--' <expression>

Returns the right-hand-side decremented. Note the right-hand-side is not changed so --a does not not decrement a, it returns a decremented

10++ <=> 11
var unchanged= 1;
unchanged++;
unchanged <-> 1;

default or :-

reactive pure No Scope Inherited Execution

<expression> (':-'|'default') <expression>

If the left-hand-side is VOID this returns the right-hand-side, otherwise returns the left-hand-side.

void :- "Hello" <=> "Hello"
1 :- "Hello" <=> 1
1 default "Hello" <=> 1

:= (definition)

reactive pure No Scope Inherited Execution

( [export] [const] <variable-name> ':=' <expression>) | ( def <variable-name> <expression )

Declares a variable to have a value, this is declarative and reactive such that saying const a := b + 1 means that a always equals b+1 no matter the value of b. The shorthand def is the same as const <variable-name> := so def a {b+1} is the same as const a := b + 1 but is syntactically better when declaring function like variables.

Declarations can also be marked as pure so that they can be used in pure scopes, this is done by prefixing the declaration with pure.

var variableA = 1
const variableB := variableA
variableA = 2

.: variableB == 2

definition-constraint

reactive pure New Scope Inherited Execution


destroy or <|||

reactive impure No Scope Inherited Execution

('<|||'|'destroy') <expression>

TODO:


/ (divide)

reactive pure No Scope Inherited Execution

<expression> '/' <expression>

Divides one value by another.

 .: DATE() / "1.0" type Decimal
 5 / 4 <=> 1
 5.0 /4 <=> 1.25
 .: 1 / 1 type Integer
 .: 1 / 1.0 type Decimal
 .: 2.0 / 1 type Decimal
 .: 2.0 / 1.0 type Decimal

drain or <-<

reactive impure No Scope Inherited Execution

('<-<'|'drain') <expression>

Drain an expression, using a URI of all it’s data. This is a complete destructive read.


each or =>>

reactive pure No Scope Inherited Execution

<expression> ('=>>'|'each') <expression>

Eache will iterate over a collection and pass each value (passed in as $1) to the second argument.

var posts = << https://jsonplaceholder.typicode.com/posts
var titles = posts each { $1.title }
@@ titles

else

reactive pure No Scope Inherited Execution

<expression> 'else' <expression>

The else operator is a binary operator which evaluates the left-hand-side (usually the result of an if statement), if that has a value of false then the right-hand-side is evaluated and it’s result returned, otherwise it returns the left-hand-side.

var a=5
//Parenthesis added for clarity, not required.
var b= if (a == 1) "one" else if (a == 2) "two" else "more than two"
.: b == "more than two"

emit or ...

reactive pure No Scope Inherited Execution

The emit operator ... takes a list and converts it to a set of events, typically this is piped to a function to process each event as it occurs.

var e=0;

def updateE {e=$1}

var collectedValues=[]

collect e until it == 4 unless it == 3{
    print count
    print collected
    collectedValues= collected
}

([1,2,3,4] ...) | updateE

collectedValues <=> [ 1, 2, 4 ]

== (equal)

reactive pure No Scope Inherited Execution

<expression> '==' <expression>

Compares two values to see if they are equal. Works with all types and maps to the Java .equals() method.

.: 1 == 1
.: "Hello" == "Hello"

err or !?

reactive impure No Scope Inherited Execution

('!?'|'err') <expression>

Sends the result of the right-hand-side to stderr.

!? "What happened"

error or ?->

reactive pure No Scope Inherited Execution

('?->'|'error') <expression>

The right-hand-side is executed if an error occurs in the current scope.

var errorHappened= false
error { err msg; errorHappened= true }
def redis ("redisx://localhost:999999/test" as URI)
write ("Hello World " + DATE()) to redis
.: errorHappened

fix or &

non-reactive pure No Scope Inherited Execution

('&'|'fix') <expression>

Converts a reactive expression into a fixed value. It fixes the value at the point the fix operator is executed. No reactive events will be passed from the right-hand-side expression.

var reactiveValue= 1
// The following line would fail if we used .: reactiveValue == 1
// when the 'reactiveValue= 2' statement executes
.: &reactiveValue == 1
reactiveValue= 2

for

reactive pure New Scope Inherited Execution

for <variable-name> <iterable-expression> <expression>

The for operator, this will iterate over a set of values and assign the specified variable to the current value when evaluating the expression.

for i in [1..10] {
    @@ i
}

fork or -<

reactive impure No Scope Inherited Execution

('-<'|'fork') <expression>

Executes the right-hand-side in a seperate thread returning a ‘future’. Any attempt to make use of the returned value from this operator will block until that thread finishes.

def sleepTime {@@ "Background Sleeping "+TIME();SLEEP(4 S); @@ "Background Finished Sleeping "+TIME();fix (TIME())}
//Any future reference to c will block until c has completed evaluation
var forkId= fork sleepTime
@@ "Main thread sleeping ..."
SLEEP(2 S)
@@ "Main thread finished sleeping ..."
var d= TIME()
var forkResult= $(forkId);
@@ forkResult
@@ d
.: forkResult type Integer
.: forkResult > d

> (greater-than)

reactive pure No Scope Inherited Execution

<expression> '>' <expression>

The standard > operator, it uses Comparable#compareTo and will work with any Dollar data type, including strings, ranges, lists etc.

.: 3 > 2

>= (greater-than-equal)

reactive pure No Scope Inherited Execution

<expression> '>=' <expression>

The standard >= operator, it uses Comparable#compareTo and will work with any Dollar data type, including strings, ranges, lists etc.

.: 2 >= 2
.: 2 >= 1
.: ! (2>=3)

if

reactive pure No Scope Inherited Execution

<expression> 'if' <expression>

Dollar does not have the concept of statements and expressions, just expressions. This means that you can use control flow in an expression such as:


var a=1
var b= if a==1 2 else 3
b <=> 2

The if operator is separate from the else operator, it simply evaluates the condition supplied as the first argument. If that value is boolean and true it evaluates the second argument and returns it’s value; otherwise it returns boolean false.

The else operator is a binary operator which evaluates the left-hand-side (usually the result of an if statement), if that has a value of false then the right-hand-side is evaluated and it’s result returned, otherwise it returns the left-hand-side.

The combined effect of these two operators is to provide the usual if/else/else if/ control flow

var a=5
//Parenthesis added for clarity, not required.
var b= if (a == 1) "one" else if (a == 2) "two" else "more than two"
.: b == "more than two"

in or

reactive pure No Scope Inherited Execution

<expression> ('€'|'in') <expression>

Returns true if the left-hand-side expression is contained in the right-hand-side expression.

.: "red" in ["red","blue","green"]
.: ! (1 in ["red","blue","green"] )

++ (increment)

reactive pure No Scope Inherited Execution

'++' <expression>

Returns the right-hand-side incremented. Note the right-hand-side is not changed so --a does not not decrement a, it returns a incremented

10-- <=> 9
var unchanged= 1;
unchanged--;
unchanged <-> 1;

is or <->

non-reactive pure No Scope Inherited Execution

<expression> ('<->'|'is') <expression>

Asserts that at the point of execution that the left-hand-side is equal to the right-hand-side.

 1 + 1 <-> 2
 1 + 1 is 2

< (less-than)

reactive pure No Scope Inherited Execution

<expression> '<' <expression>

The standard < operator, it uses Comparable#compareTo and will work with any Dollar data type, including strings, ranges, lists etc.

.: 2 < 3
.: "a" < "b"

<= (less-than-equal)

reactive pure No Scope Inherited Execution

<expression> '<=' <expression>

The standard <= operator, it uses Comparable#compareTo and will work with any Dollar data type, including strings, ranges, lists etc.

.: 2 <= 3
.: 2 <= 2
.: "a" < "b"
.: "a" <= "a"

list

reactive impure New Scope Inherited Execution

'[' ( <expression> ',' ) * [ <expression> ] ']'

Dollar’s lists are pretty similar to JavaScript arrays. They are defined using the [1,2,3] style syntax and accessed using the x[y] subscript syntax. You can count the size of the list using the size operator #. Dollar maps are also associative arrays (like JavaScript) allowing you to request members from them using the list subscript syntax x[y] or the member syntax ..

[1,2,3][1] <=> 2
#[1,2,3,4] <=> 4

.: [1,2,3] + 4 == [1,2,3,4];
.: [1,2,3,4] - 4 == [1,2,3];
.: [] + 1 == [1] ;
.: [1] + [1] == [1,1];
.: [1] + 1 == [1,1];

[1..3][-1] <=> 3
[1..3]<- <=> [3..1] //reverse
[1..3][/] <=> [1,2,3] //split
[1,2,3][<] <=> 1 //min
[1,2,3][>] <=> 3 //max
[1,2,3][+] <=> 6 //sum
[1,2,3][%] <=> 2 //avg
[1,2,3][*] <=> 6 //product
->[3,1,2] <=> [1,2,3] //sort
->[3,1,2]<- <=> [3,2,1] //sort then reverse


map

reactive impure New Scope Inherited Execution

'{' ( <expression> ',' ) * [ <expression> ] '}'


max or [>]

reactive pure No Scope Inherited Execution

<expression> ('[>]'|'max')


. (member)

reactive pure New Scope Inherited Execution

<expression> '.' <expression>

The membership or . operator accesses the member of a map by it’s key.

{"key1":1,"key2":2} ["key"+1] <=> 1
{"key1":1,"key2":2}.key2 <=> 2
{"key1":1,"key2":2}[1].key2 <=> 2

min or [<]

reactive pure No Scope Inherited Execution

<expression> ('[<]'|'min')


- (minus)

reactive pure No Scope Inherited Execution

<expression> '-' <expression>

Deducts a value from another value

2 - 1 <=> 1

module

reactive impure New Scope Inherited Execution

module <name> (<parameter>)*


% (modulus)

reactive pure No Scope Inherited Execution

<expression> '%' <expression>

Returns the remainder (modulus) of the division of the left-hand-side by the right-hand-side.

5 % 4 <=> 1
.: 1 % 1  type Integer
.: 1 % 1.0  type Decimal
.: 2.0 % 1  type Decimal
.: 2.0 % 1.0  type Decimal

* (multiply)

reactive pure No Scope Inherited Execution

<expression> '*' <expression>

Returns the product of two values. If the left-hand-side is scalar (non collection) then a straightforward multiplication will take place. If the left-hand-side is a collection and it is multiplied by n, e.g. {a=a+1} * 3 it will be added (+) to itself n times i.e. {a=a+1} + {a=a+1} + {a=a+1}.

2 * 5 <=> 10

.: 1 * 1 type Integer
.: 1 * 1.0 type Decimal
.: 2.0 * 1 type Decimal
.: 2.0 * 1.0 type Decimal
.: DATE() * 10 type Decimal


- (negate)

reactive pure No Scope Inherited Execution

'-' <expression>

Negates a value.

 .: -1 < 0

new

reactive impure New Scope Inherited Execution

'new' <identifier> (<parameters>)


not or !

reactive pure No Scope Inherited Execution

('!'|'not') <expression>

Returns the logical negation of the right-hand-side expression.

not( true ) <=> false
!true <=> false

!= (not-equal)

reactive pure No Scope Inherited Execution

<expression> '!=' <expression>

Returns true if the two expression are not equal.

.: 1 != 2

or or ||

reactive pure No Scope Inherited Execution

<expression> ('||'|'or') <expression>

Returns the logical ‘or’ of two expressions, e.g. a || b. Just like in Java it will shortcut, so that if the left-hand-side is true the right-hand-side is never evaluated.

true or false <=> true
true || false <=> true
false || false <=> false

: (pair)

reactive pure No Scope Inherited Execution

<expression> ':' <expression>

Creates a pair (or tuple of 2 values) of values with a key and a value.

var pair1 = "first" : "Hello ";
var pair2 = "second" : "World";

.: pair1 + pair2 == {"first":"Hello ","second":"World"}

parallel or |:|

non-reactive pure No Scope Inherited Execution

('|:|'|'parallel') <expression>

Causes the right-hand-side expression to be evaluated in parallel, most useful in conjunction with list blocks.

var s=     [ TIME(), {SLEEP(4 S); TIME();}(),  TIME() ];
var p= |:| [ TIME(), {SLEEP(4 S); TIME();}(),  TIME() ];
@@ s
@@ p
//Test different execution orders
.: s[0] type Integer
.: s[1] >= s[0]
.: s[2] >= s[1]
.: p[0] < p[1]
.: p[2] <= p[1]
.: p[1] - p[2] > 1000

parameter

reactive pure New Scope Inherited Execution

( <expression> | <builtin-name> | <function-name> ) '(' ( <expression> | <name> '=' <expression> )* ')'

In most programming languages you have the concept of functions and parameters, i.e. you can parametrized blocks of code. In Dollar you can parameterize anything. For example let’s just take a simple expression that adds two strings together, in reverse order, and pass in two parameters.

($2 + " " + $1)("Hello", "World") <=> "World Hello"

The naming of positional parameters is the same as in shell scripts.

By combining with the def decleration we create functions, such as:


def testParams($2 + " " + $1)
testParams ("Hello", "World") <=> "World Hello"

Yep we built a function just by naming an expression. You can name anything and parametrise it - including maps, lists, blocks and plain old expressions.

Named parameters are also supported.

($2 + " " + $1)("Hello", "World") <=> "World Hello"

var outer=10;
const scopedArray := [$1,outer,{var inner=20;inner}]

@@ scopedArray(5)[0]
scopedArray(5)[0] <=> 5;
@@ scopedArray(5)[1]
scopedArray(5)[1] <=> 10;
scopedArray(5)[2]() <=> 20;

const testParams := (last + " " + first)
testParams(first="Hello", last="World") <=> "World Hello"

def func($2 + " " + $1)
func ("Hello", "World") <=> "World Hello"

pause or ||>

reactive impure No Scope Inherited Execution

('||>'|'pause') <expression>

Pauses a service described typically by a URI.


| (pipe)

reactive pure New Scope Inherited Execution

<expression> '|' <expression>

The pipe operator pipes a value and all it’s changes to the function on the right-hand-side. Pipes can be chained.

The pipe operator is ideal for using with the emit (...) operator for reactive stream processing.

var inputValue= void;
<Integer> collectable=0;

def update {
    collectable= $1;
}

<List> result=[]

collect collectable until it == 6 unless it == 5 {
    print count
    print collected
    result= collected
}


inputValue + 2 | update

inputValue= 1; inputValue= 2; inputValue= 3; inputValue= 4;

result <=> [ 3, 4, 6]

+ (plus)

reactive pure No Scope Inherited Execution

<expression> '+' <expression>

Appends or adds two values.

var pair1 = "first" : "Hello ";
var pair2 = "second" : "World";
.: pair1 + pair2 == {"first":"Hello ","second":"World"}

.: [1,2,3] + 4 == [1,2,3,4];
.: [1,2,3,4] - 4 == [1,2,3];
.: [] + 1 == [1] ;
.: [1] + [1] == [1,1];
.: [1] + 1 == [1,1];

.: (1 + 1.0) type Decimal
.: (1.0 + 1.0) type Decimal
.: (1.0 + 1) type Decimal
.: (1 + 1) type Integer

print or @@

reactive impure No Scope Inherited Execution

('@@'|'print') <expression>

Sends the right-hand-side expression to stdout.

@@ "Hello"
print "World"

product or [*]

reactive pure No Scope Inherited Execution

<expression> ('[*]'|'product')


publish or *>

reactive impure No Scope Inherited Execution

<expression> ('*>'|'publish') <expression>

publish


pure

reactive pure No Scope Inherited Execution

'pure' <expression>

Support for some aspects of functional programming are included in Dollar. Primarily the idea of pure expressions using the pure operator. This signals that an expression or declaration is a pure expression or declaration.

In the example we’re declaring reverse to be an expression that reverses two values from a supplied array. Because we declare it as pure the expression supplied must also be pure. To understand what a pure function is please see http://en.wikipedia.org/wiki/Pure_function. Basically it prohibits the reading of external state or the setting of external state. We next swap [2,1] within a newly created pure expression, which is subsequently assigned to a. If reverse had not been declared pure it would not be allowed within the pure expression.

Note some builtin functions are not themselves pure and will trigger parser errors if you attempt to use them in a pure expression. Take DATE() for example which supplies an external state (the computers clock).

pure def reverse [$1[1],$1[0]]

var a= pure {
 reverse([2,1])
}

.. (range)

reactive pure No Scope Inherited Execution

<expression> '..' <expression>

Creates a RANGE between the two values specified using the standard Maven range syntax

In pseudocode:

(a..b) = {x | a < x < b}
[a..b] = {x | a <= x <= b}
[a..b) = {x | a <= x < b}
(a..b] = {x | a < x <= b}
(a..) = {x | x > a}
[a..) = {x | x >= a}
(..b) = {x | x < b}
(..b] = {x | x <= b}
(..) = all values

Please see https://github.com/google/guava/wiki/RangesExplained for more information on the range format used.

//Types
.: (1..5) type Range
.: [1..5) type Range
.: [1..5] type Range
.: (1..5] type Range
.: (..5] type Range //  less than or equal to 5
.: (5..] type Range //  greater than 5
.: (..) type Range //   all numbers



//Bounds
.: ! (1 in (1..5))
.:  (1 in [1..5))
.: 3 in (1..5)
.: !(5 in (1..5))
.: 5 in (1..5]
.: !(0 in (1..5))
.: !(6 in (1..5))

//count
# (1..1] <=> void
# [1..1] <=> 1
# [1..1) <=> void



read

reactive impure No Scope Inherited Execution

'read' ['block'] ['mutate'] ['from'] <expression>

The read operator is used to read data from an expression typically a URI. It has options to read-and-block (block) as well as read-and-destroy (mutate). Note that the from keyword is just syntax sugar and can be omitted.


<< (read-simple)

reactive impure No Scope Inherited Execution

'<<' <expression>

Performs a simple read from another data item, typically this is used with a URI.


reduce or >>=

reactive pure No Scope Inherited Execution

<expression> ('>>='|'reduce') <expression>

Performs a reduction on the elements of the left-hand-side expression, using an associative accumulation function, and returns the reduced value, if any. This is equivalent to Java code of

 boolean foundAny = false;
 T result = null;
 for (T element : this stream) {
     if (!foundAny) {
         foundAny = true;
         result = element;
     }
     else
         result = accumulator.apply(result, element);
 }
 return foundAny ? Optional.of(result) : Optional.empty();

but is not constrained to execute sequentially.

The rhs function/expression must be an associative function.

[1,2,3,4,5] reduce ($1 + $2)

reversed or <-

reactive pure No Scope Inherited Execution

<expression> ('<-'|'reversed')


script

non-reactive impure No Scope Inherited Execution

<language-name> <script-code>

Hopefully you’ll find Dollar a useful and productive language, but there will be many times when you just want to quickly nip out to a bit of another language. To do so, just surround the code in backticks and prefix with the languages name. Currently only java is supported but more will be added soon.

const variableA="Hello World"

//Now we parametrize the script. The parameters are available in a list of 'var' objects (see the dollar-core docs)

const javaWithParam = java`
out=in.get(0).$multiply(in.get(1)).$multiply(in.get(2));
` (10,20,30)

javaWithParam <=> 10*20*30

# (size)

reactive pure No Scope Inherited Execution

'#' <expression>

Returns the size of non-scalar types or the length of a string.


sort or ->

reactive pure No Scope Inherited Execution

('->'|'sort') <expression>

->[3,1,2] <=> [1,2,3]
sort [3,1,2] <=> [1,2,3]

split or [/]

reactive pure No Scope Inherited Execution

<expression> ('[/]'|'split')

The list split operator [/] will convert any non list type to a list with division as the preferred method. The split operator will split a Map or an Object into pairs for example.

sort ({"first":1,"second":2,"third":3}[/]) <=> [{"first":1},{"second":2},{"third":3}]

class TestClass {
    var first=1;
    var second=2;
    var third=3;
}
new TestClass() [/] <=> [{"first":1},{"second":2},{"third":3}]

// Single value types become single value lists.
1[/] <=> [1]

start or |>

reactive pure No Scope Inherited Execution

('|>'|'start') <expression>

Starts a service described typically by a URI.


state or <|>

reactive impure No Scope Inherited Execution

('<|>'|'state') <expression>

Returns the state of a service described typically by a URI.


stop or <|

reactive impure No Scope Inherited Execution

('<|'|'stop') <expression>

Stops a service described typically by a URI.


subscribe or <*

reactive impure No Scope Inherited Execution

<expression> ('<*'|'subscribe') <expression>

subscribe


*= (subscribe-assign)

reactive pure No Scope Inherited Execution

subscribe-assign


subscript

reactive pure No Scope Inherited Execution

( <expression> '[' <index-expression>|<key-expression> ']' ) | ( <expression> '.' (<index-expression>|<key-expression>) )

subscript operator

{"key1":1,"key2":2} ["key"+1] <=> 1
{"key1":1,"key2":2}.key2 <=> 2
{"key1":1,"key2":2}[1].key2 <=> 2

sum or [+]

reactive pure No Scope Inherited Execution

<expression> ('[+]'|'sum')

The sum operator reduces a list using the + operation.

[1,2,3,4,5][+] <=> 15
["a","b","c"][+] <=> "abc"

~ (truthy)

reactive pure No Scope Inherited Execution

'~' <expression>

The truthy operator ~ converts any value to a boolean by applying the rule that: void is false, 0 is false, “” is false, empty list is false, empty map is false - all else is true.

.: ~ [1,2,3]
.: ! ~ []
.: ~ "anything"
.: ! ~ ""
.: ~ 1
.: ! ~ 0
.: ! ~ {void}
.:  ~ { "a" : 1}
.: ! ~ void

type

reactive pure No Scope Inherited Execution

<expression> 'type' <expression>

A boolean operator that returns true if the left-hand-side variable is one of the types listed on the right-hand-side. Analogous to Java’s instanceof.

.: 1 type Integer,Decimal
.: "Hello" type String
.: 1.0 type Decimal,String

unique or [!]

reactive pure No Scope Inherited Execution

<expression> ('[!]'|'unique')

[1,2,2,3][!] <=> [1,2,3]
["aaa","a","aaa","b"][!] <=> ["aaa","a","b"]

unit

reactive impure New Scope Inherited Execution

<numeric> <unit-name>


unpause or <||

reactive impure No Scope Inherited Execution

('<||'|'unpause') <expression>

Un-pauses a service described typically by a URI.


when or ?

reactive pure New Scope Inherited Execution

<expression> ('?'|'when') <expression>

var c=1
var d=1

//When c is greater than 3 assign it's value to d
c > 3 ? (d= c)

&c <=> 1; &d <=> 1
c=2; &c <=> 2; &d <=> 1
c=5 ; &c <=> 5 ; &d <=> 5

//Note alternative syntax is when <condition> <expression>
when c > 3 { @@ c}

when-assign

reactive pure No Scope Inherited Execution

('var'|'volatile') [<type-assertion>] <variable-name> '?' <condition-expression> '='<assignment-expression>

The ‘when assign’ operator ? .. = updates a variable to the assignment expression whenever the ‘condition expression’ changes and is true.

var h=1
var i ? (h < 3) = (h + 2)
h=4
.: i type Void
h=2
i <=> 4

while

reactive pure New Scope Inherited Execution

while <condition> <expression>

while operator

var a= 1;
while a < 10 {
    print a
    a=a+1;
}

window

non-reactive pure New Scope Inherited Execution

window <expression> 'over' <duration-expression> [ 'period' <duration-expression> ] [ 'unless' <expression> ] [ 'until' <expression> ] <window-expression>

The window operator provides a time window based view of a changing value.

The first part of the window operator is the expression this will be listened to for changes and it’s value passed into the windowing process.

The next part is the over clause which determines the duration over which changes are captured. Any change that is older than this duration is discarded.

Following this is the optional period clause which determines how often the the window is calculated and the window-expression evaluated. If it is not supplied it defaults to the value in the over clause.

Next is the optional unless clause which specifies which changes to ignore, if this clause is true then the change will be ignored.

Then the optional until clause which specifies a condition for when the windowing should stop completely.

Finally the window-expression is the expression which is evaluated with the following variables available:

  • count a value that increments on every window-expression evaluation
  • collected a list of values that have been windowed across the duration of the over clause and which have not been excluded by the unless clause.
var changeable= 1;
volatile collectedValues= void;

window (changeable) over (10 S) period (5 S) unless (it == 5)  until (it == 29) {
        @@collected
        collectedValues= collected;
}

for i in [1..32] {
    SLEEP (1 S)
    changeable=changeable+1
}


.: #collectedValues > 0
collectedValues <-> [21,22,23,24,25,26,27,28,29]
@@ collectedValues

write

reactive impure No Scope Inherited Execution

'write' ['block'] ['mutate'] ['to'] <expression>

The write operator is used to write data to an expression typically a URI. It has options to write-and-block (block) as well as write-and-destroy (mutate). Note that the to keyword is just syntax sugar and can be omitted.


>> (write-simple)

reactive impure No Scope Inherited Execution

<expression> '>>' <expression>

Performs a simple write to another data item, mostly used to write to a URI.


Appendix B - Keywords

as

block

const

Mark a variable definition as a constant, i.e. readonly.

def

every

export

Export a variable at the point of definition.

false

Boolean false.

for

from

infinity

mutate

no

Boolean false.

null

A NULL value of ANY type.

over

period

pure

The start of a pure expression.

to

true

Boolean true.

unless

until

var

Marks a variable as variable i.e. not readonly.

void

A VOID value.

volatile

Marks a variable as volatile, i.e. it can be accessed by multiple threads.

with

yes

Boolean true.

Appendix C - Reserved Keywords, Operators and Symbols

Keywords

The following keywords are reserved:

abstract, await, break, case, catch, closure, continue, dispatch, do, dump, enum, extends, fail, filter, final, finally, float, goto, implements, import, impure, include, instanceof, interface, join, lambda, load, measure, native, package, pluripotent, private, protected, public, readonly, return, save, scope, send, short, static, super, switch, synchronized, this, throw, throws, trace, transient, try, unit, variant, varies, vary, wait

Operators

The following operator keywords are reserved:

serial

The following operator symbols are reserved:

&=, &>, +>, ->, -_-, ::, <$, <&, <+, <++, <-, <=<, <?, >&, >->, ?$?, ?..?, ?:, ?>, @, @>, |*, |..|

Symbols

The following general symbols are reserved:

Appendix D - Operator Precedence

All operators by precedence, highest precedence (associativity) first.

Name Keyword Operator Type
fix fix & prefix
sort sort -> prefix
range   .. binary
default default :- binary
member   . binary
parameter     postfix
subscript     postfix
cast     postfix
decrement   -- prefix
for for   control
in in binary
increment   ++ prefix
not not ! prefix
reversed reversed <- postfix
size   # prefix
truthy   ~ prefix
while while   control
divide   / binary
each each =>> binary
modulus   % binary
multiply   * binary
reduce reduce >>= binary
avg avg [%] postfix
max max [>] postfix
min min [<] postfix
minus   - binary
negate   - prefix
plus   + binary
product product [*] postfix
split split [/] postfix
sum sum [+] postfix
unique unique [!] postfix
emit emit ... reserved
greater-than   > binary
less-than   < binary
pipe   | binary
equal   == binary
greater-than-equal   >= binary
less-than-equal   <= binary
not-equal   != binary
and and && binary
or or || binary
all all <@ prefix
causes causes => binary
choose choose ?* binary
drain drain <-< prefix
publish publish *> binary
read read   prefix
read-simple   << prefix
subscribe subscribe <* binary
when when ? binary
write write   control
write-simple   >> binary
pair   : binary
create create |||> prefix
destroy destroy <||| prefix
else else   binary
fork fork -< prefix
if if   binary
parallel parallel |:| prefix
pause pause ||> prefix
start start |> prefix
state state <|> prefix
stop stop <| prefix
type type   binary
unpause unpause <|| prefix
assign   = assignment
definition   := assignment
definition-constraint     assignment
subscribe-assign   *= assignment
when-assign     assignment
always always <=> binary
assert assert .: prefix
debug debug !! prefix
err err !? prefix
error error ?-> prefix
is is <-> binary
print print @@ prefix
block     collection
builtin     other
class class   other
collect collect   control
list     collection
map     collection
module module   other
new new   prefix
pure pure   prefix
script     other
unit     postfix
window window   control