In its quest to provide a familiar and easy to use scripting language for .NET #Script added some enhancements to JavaScript expressions like being able to use SQL-Like Boolean Expressions like =
, and
and or
inside a boolean expression, e.g:
[0,1,2,3,4,5] |> where => (it = 2 or it = 3) and it.isOdd()
In modern JS (ES6+) this would be written as:
[0,1,2,3,4,5].filter(it => (it == 2 || it == 3) && isOdd(it));
Breaking Change
By default the use of the single =
is now treated as an Assignment operator as it is in JS so you will need to update any of your boolean expressions that use this to use the standard ==
equality operator instead, e.g:
[0,1,2,3,4,5] |> where => (it == 2 or it == 3) and it.isOdd()
We recommend updating your scripts now so it will be unaffected when updating to future versions.
Or if youâre not ready to update your scripts you will be able to revert to the previous behavior and have =
treated as equality with:
ScriptConfig.AllowAssignmentExpressions = false;
JS familiarity over enhanced human-friendly syntax
At the time we valued human-friendly syntax over JS compatibility however over the years itâs become clearer that language familiarity is more important than enhanced human-friendly syntax as it makes it much easier for devs who knows JS (i.e. most) to use #Script
. Keeping compatibility with JS syntax also helps in other areas like code portability, JS tooling and syntax highlighters and linters, etc.
JS compatibility was the primary motivation for transitioning to the Pipe Forward operator whose future incorporation into JS will have a lot less friction then continuing to use the unix pipe |
operator.
The Pipe Forward operator makes it more obvious that the âoutput of the left expressionâ is passed as the âinput of the right targetâ which needs to be either a script method or a filter transformer however it could be forgiven to confuse it as an assignment expression where the output of the left expression was assigned to the sliders
variable:
{{ dirFiles('img/sliders') |> sliders }}
The mistake here was that it needs to be piped to the to
(or toGlobal
) script method which assigns it to the sliders
local scope argument:
{{ dirFiles('img/sliders') |> to => sliders }}
Local Variables
As assignment is likely the most common expression thatâs foreign to JS devs Iâve decided to just go ahead and add it to #Script, where in the latest v5.8.1 on MyGet you can now use JS syntax to assign a variable, e.g:
var sliders = dirFiles('img/sliders')
Like JS you can use either var
, let
or const
but they all behave like let
and assign a locally scoped variable at the time the expression is executed.
Also like JS the semicolon is optional and you can assign multiple variables in a single expression:
let a = 1 + 2, b = 3 * 4, c, d = 'D';
One semantic difference is that variable declarations are still an âExpressionâ in #Script, albeit one that returns a discarded result whilst theyâre a âStatementâ in JS, although this distinction doesnât mean much in practice.
Global Variables
Global Variables in #Script
are maintained in the PageResult.Args
dictionary which you could previously assign to using the toGlobal
script method (i.e. instead of to
) which will make it accessible to all scripts rendered within that PageResult
.
Weâre mimicking JSâs behavior to allow assignment expressions to assign global variables where âAssignment Expressionsâ on undeclared variables (i.e. where no locally scoped variable exists) will assign a global variable:
a = 1
A more descriptive syntax available to declare a variable is to assign it to the global
object (inspired by nodeâs global) which is an alias to the PageResult.Args
dictionary:
global.a = 1
Note: Like most languages assignment expressions in
#Script
return the assigned value
Assignment Expressions
In addition to declaring and assigning variables, weâve also added support for being able to use assignment expressions to assign and mutate Collections and Type Properties using either Member Expression or Index expression syntax, e.g:
intList[1] = 10
stringArray[1] = "foo"
stringMap["foo"] = "bar"
person.Age = 27
objectMap.Person.Name = "kurt"
objectMap['Per' + 'son'].Name = "kurt"
intList[1.isOdd() ? 2 : 3] = 30
To recap, the =
operator is now used for variable declarations and assignment expressions by default, but can be reverted to the previous equality behavior with:
ScriptConfig.AllowAssignmentExpressions = false;
Now available on MyGet and sharpscript.net
This feature is now available from v5.8.1 thatâs now available on MyGet, https://sharpscript.net has also been updated to use the latest version if you want to try it out in its interactive code boxes.