Assignment
Assignment is a statement, not an expression.
Operator | Function |
---|---|
= | Assign value (right to left) |
:= | Assign value (right to left) with inferred type |
The left operand of an assignment expression can not be a literal value.
Variable declaration and assignment.
a: U8 // declare typed var (default init)
a: U8 = 1 // declare typed and init
a:= 1 // declare inferred-type and init
a = 1 // assign - 'a' must already be declared and be mutable
Here 42 is assigned to the variable a
(inferred type).
a := 42
The receiving (left) operand can also be a path to a field.
MyStruct
field1: U8
s: Mut<MyStruct>
s.field1 = 42
z: MyStruct
field1 = 42 // inline assignment
Because both the
Equals
and theAssignment
operators use the ‘=
’ symbol, it is not possible to assign values inside a comparison expression.
if a := myFunc() // error! cannot assign inside a condition
if a = myFunc() // error! variable a is not declared
...
Boolean Assignment
Assignment clashes with comparison is-equal operator!
Use parenthesis around comparison.
[var] = (<bool expression>)
[var] = (<bool expression>)?
x := 42
// unclear syntax?
a: Bool = x = 42
// use () for comparison expression
a := (x = 42)
// a: Bool = true
// make it a rule for all bool expressions?
a := (x > 101)
// a: Bool = false
Assignment Chaining
The assignment can not be chained across multiple variables (left operand) that all get assigned the same value (right operand).
None of this works:
a := b := c := 42
b: Mut<U16>
c: Mut<U32>
b = c = 42
Structure Assignment
Assigning structures works the same as scalar values but for structures only the reference is copied.
s: MyStruct
fld1 = 42
fld2 = "42"
// reference passing
x := s
// this will make a new copy of Struct
// '<=' copies the instance
// '^' makes the result mutable
// ':' inferres the type for the var
x :<=^ s // mutable assignment
// only changes x, not s
x.fld1 = 101
b := (s.fld1 = 42)
// b = true
More on the <=
operator:
s: MyStruct
...
// copy same type
x :<= s // x: MyStruct
x : MyStruct <= s // or this
YourStruct
fld1: U8
fld2: Str
// map / transform (default by field names)
y: YourStruct <= s // y: YourStruct -mapped
See also Structure Transformation.
Conditional Assignment
Instead of an if
statement, use the ?=
operator for use with optional values.
The ?=
operator only assigns the value to the left operand if it does not already have a value.
a: Mut<U8>?
a ?= 42
// a = 42
b: Mut<U8>?
b = 42
b ?= 101
// b = 42
Only works with Opt<T>
values or dotnet null
reference types.
Atomic Assignment
Protect from interrupts / thread context switches.
A way to ensure an assignment operation is uninterrupted.
- unconditional (lock)
- conditional (exchange-if)
Atomic Type
A wrapper type that indicates the instance is accessed atomically.
Atom<T>
is implicitly mutableMut<T>
.
// assignment is easy because Atom
a: Atom<U8> = 42
a = 101
b: U8 // does not need to be Atom
// supply function on Atom
a.Exchange(b.Ptr())
a.ExchangeIf(a = 42, b.Ptr())
Atomic locking requires guaranteed unlocking… (defer keyword)
a: Atom<U8> = 42
a.Lock() // mandatory timeout?
defer a.Unlock() // defer keyword
// compact syntax?
a.Lock() -> defer .Unlock()
// atomic assignment
a = 101
// end of scope unlocks
Atom<T>
implements IDisposable and unlocks automatically on destruction when going out of scope.
Using Atom<T>
also allows to manage access to structs that are larger than a single primitive data type.
TBD: Syntax to set multiple fields under lock?
Struct
fld1: U8
fld2: Str
s: Atom<Struct>
// using object notation?
s = {fld1: 42, fld2: "42"} // Atom overrides = operator
// or a capture?
|s|
s.fld1 = 101
s.fld2 = "101"
Volatile
Volatile is used when the contents of a variable (memory location) can be changed from outside the program (memory mapped IO/hardware registers) or outside the compiler’s field of view (interrupt service routines).
// 'Volatile<T>' too long?
a: Volatile<U8> = 42 // write
x := a // read
TBD: Memory Fences! => default .NET
See also the Volatile
dotnet class.
Deconstruction
Deconstruction is copying the value into a variable or parameter.
Deconstructing an Array
// spread the array and the rest is a range
a, b, ..rest := ...(1, 2, 3, 4, 5)
// a: U8 = 1
// b: U8 = 2
// rest: Array<U8> = (3, 4, 5)
Deconstructing Function Parameters
arr := (1, 2, 3, 4, 5)
func5: (p1: U8, p2: U8, p3: U8, p4: U8, p5: U8)
...
func5(...arr) // with 5 params
// what if the param count does not match array item count?
// must at least be the number of parameters without default values.
// should also work with an anonymous type
params := { p1=1, p2=2, p3=3, p4=4, p5=5 }
func5(...params)
Deconstructing a Structure
MyStruct
Field1: U8
Field2: U8
Field3: U8
s = MyStruct
...
// partial by name (ignore case)
field1, field3 = ...s
// field1: U8 = <value of s.Field1>
// field3: U8 = <value of s.Field3>
// <value of s.Field2> is not used
a, b := ...s // error! field names must match (case insensitive)
// or when in-order - all fields must be specified (or ignored)
a, _, _ := ...s
// a: U8 = <value of s.Field1>
// <value of s.Field2 and s.Field3> are ignored
_, _, a := ...s
// a: U8 = <value of s.Field3>
// <value of s.Field1 and s.Field2> are ignored
Use the spread operator
...
on the right-hand-side value to indicate it is being deconstructed
Is there a need to override how deconstruction is done on a (custom) type? => Yes
TBD: deconstruct variable names with aliases? => No
o =: {x=42, y=101}
aliasX=x, aliasY=y := ...o
// here we need a different '=' operator for aliases!
// can we parse the '=' in these terms?
aliasX=.x, aliasY=.y := ...o // '=.' operator? (both alias and name are in scope)
aliasX=_x, aliasY=_y := ...o // '=_' operator? (only alias is in scope - see Identifiers/Aliases)
Swap Scalar Variables
(unlike structs)
x := 42
y := 101
// left = deconstruct
// right = anonymous struct '{}'
x, y = ...{ y, x }
// x = 101
// y = 42
// swap operator (still needed?)
x <=> y