diff --git a/index.html b/index.html index 99fffa45e..fcb65b192 100644 --- a/index.html +++ b/index.html @@ -985,7 +985,7 @@
The .
prefix is used to reference the current module's namespace.
Unlike local variables, namespace variables do not currently infer the type from the right hand side so a specific type must be specified or it will default to the any
type:
var .my_map Map = [:]
+var .my_map Map = Map{}
Since namespace variables are initialized outside of a fiber's execution flow, they can not reference any local variables:
var .b = a --> Compile error, initializer can not reference a local variable.
@@ -1210,13 +1210,14 @@ Keywords. #
- Control Flow:
if
else
switch
case
while
for
break
continue
pass
- Operators:
or
and
not
-- Variables:
var
my
+- Variables:
var
- Functions:
func
return
- Coroutines:
coinit
coyield
coresume
- Types:
type
as
- Metaprogramming:
template
- Error Handling:
try
catch
throw
- Modules:
import
+- Dynamic Typing:
let
^topic
Contextual keywords. #
@@ -1257,11 +1258,11 @@ Comparison Operators. Operator overloading. Zero values. #
Uninitialized type fields currently default to their zero values. However, this implicit behavior will be removed in the future in favor of a default value clause. Zero values must then be expressed using the reserved zero
literal.
The following shows the zero values of builtin or created types:
-Type Zero value boolean
false
int
0
float
0.0
String
''
Array
Array('')
List
[]
Map
[:]
type S
[S:]
#host type S
S.$zero()
dynamic
int(0)
any
int(0)
?S
Option(S).none
^topic
+Type Zero value boolean
false
int
0
float
0.0
String
''
Array
Array('')
List
[]
Map
Map{}
type S
S{}
#host type S
S.$zero()
dynamic
int(0)
any
int(0)
?S
Option(S).none
^topic
Type casting. #
The as
keyword can be used to cast a value to a specific type. Casting lets the compiler know what the expected type is and does not perform any conversions.
If the compiler knows the cast will always fail at runtime, a compile error is returned instead.
@@ -1349,7 +1350,7 @@ Comments. #
^topic
CYON. #
CYON or the Cyber object notation is similar to JSON. The format uses the same literal value semantics as Cyber.
-[
+{
name: 'John Doe',
'age': 25,
-- This is a comment
@@ -1358,7 +1359,7 @@ CYON. #
'San Francisco',
'Tokyo',
],
-]
+}
^topic
Basic Types. #
@@ -1391,19 +1392,20 @@ Basic Types. #
- Arrays.
- Lists.
- Tuples.
+- Tables.
+
- Maps.
- Symbols.
-any
.
-
+any
.
dynamic
.
var list = [234]
-- Append a value.
-list.append 123
+list.append(123)
-- Inserting a value at an index.
list.insert(1, 345)
@@ -1623,35 +1625,55 @@ Lists. #
Tuples. #
Incomplete: Tuples can only be created from #host funcs at the moment.
^topic
-Maps. #
-Maps are a builtin type that store key value pairs in dictionaries. See type Map
.
+Tables. #
+A Table
is a versatile object that can have an arbitrary set of fields.
+By default, the record literal initializes a Table
:
+var o = {}
+
+o = {a: 123}
+print o.a --> 123
+
+A Table
can be initialized explicitly using its type name:
+var o = Table{a: 123}
+
+Any field can be assigned a value.
+However, accessing a field before it's initialized results in a panic:
+o.my_field = 234
+print o.my_field --> 234
+
+print o.foo --> panic. The field `foo` was not initialized.
+
^topic
-Create map. #
-Create a map using key value pairs inside a collection literal:
-var map = [ a: 123, b: () => 5 ]
-
-Maps entries can be listed in multiple lines:
-var map = [
- foo: 1,
- bar: 2,
-]
+Table indexing. #
+Indexing can be used to access a field with a dynamic name. The key must be a String
:
+var o = { name: 'Nova' }
+var field = 'name'
+print o[field] --> Nova
+If the object is intended to be used like a hash map with varying key types, consider using Map
instead.
^topic
-Empty map. #
-The empty map is initialized using [:]
:
-var empty = [:]
+Check field existence. #
+Planned Feature
+
^topic
+Prototypes. #
+Planned Feature
+
^topic
+Maps. #
+Maps are a builtin type that store key value pairs in dictionaries. See type Map
.
+Maps are initialized with the Map
type and a record literal:
+var map = Map{ a: 123, b: () => 5 }
+
+The empty record literal creates an empty map:
+var empty = Map{}
^topic
Map indexing. #
Get a value from the map using the index operator:
print map['a']
-Maps can be accessed with the .
dot operator as well:
-print map.a
-
^topic
Map operations. #
-var map = [:]
+var map = Map{}
-- Set a key value pair.
map[123] = 234
@@ -1660,7 +1682,7 @@ Map operations. #
print map.size()
-- Remove an entry by key.
-map.remove 123
+map.remove(123)
-- Iterating a map.
for map -> [val, key]:
@@ -1671,8 +1693,8 @@ Map block. #
Entries can also follow a collection literal block.
This gives structure to the entries and has
the added benefit of allowing multi-line lambdas.
-Planned Feature
-var colors = []:
+Most likely this will not be implemented in favor of a builder syntax
+var colors = {}:
red: 0xFF0000
green: 0x00FF00
blue: 0x0000FF
@@ -1682,7 +1704,7 @@ Map block. #
print c.blue
-- Nested map.
- darker []:
+ darker {}:
red: 0xAA0000
green: 0x00AA00
blue: 0x0000AA
@@ -1696,7 +1718,8 @@ Symbols. #
^topic
any
. #
-A variable with the any
type can hold any value, but copying it to narrowed type destination will result in a compile error:
+Unlike dynamic
, any
is statically typed and performs type checks at compile-time.
+any
type can hold any value, but copying it to narrowed type destination will result in a compile error:
func square(i int):
return i * i
@@ -1710,21 +1733,8 @@ any
. #
print square(a as int) --> 100
^topic
-Invoking any
values. #
-Since any
is a static type, invoking an any
value must be explicitly casted to the appropriate function type.
-Planned Feature: Casting to a function type is not currently supported.
-
func add(a int, b int) int:
- return a + b
-
-var op any = add
-print op(1, 2) -- CompileError. Expected `func (int, int) any`
-
-var opFunc = op as (func (int, int) int)
-print opFunc(1, 2) -- Prints "3".
-
-^topic
dynamic
. #
-The dynamic type defers type checking to runtime. See Dynamic Typing.
+The dynamic type defers type checking to runtime. However, it also tracks its own recent type in order to surface errors at compile-time. See Dynamic Typing.
^topic
Custom Types. #
@@ -1768,7 +1778,7 @@ Custom Types. #
Type aliases.
-Type copies.
+Distinct types.
Traits.
Union types.
Generic types.
@@ -1798,17 +1808,17 @@ Fields. #
^topic
Instantiate. #
New object instances are created using a record literal with a leading type name:
-
var node = [Node value: 123, next: none]
+var node = Node{value: 123, next: none}
print node.value -- Prints "123"
A record literal can also initialize to the inferred object type:
-var node Node = [value: 234, next: none]
+var node Node = {value: 234, next: none}
print node.value -- Prints "234"
^topic
Default field values. #
When a field is omitted in the record literal, it gets initialized to its zero value:
-var node Node = [value: 234]
+var node Node = {value: 234}
print node.next -- Prints "Option.none"
type Student:
@@ -1816,7 +1826,7 @@ Default field values. Default field values. ^topic
Circular references. #
Circular type references are allowed if the object can be initialized:
-Planned Feature: Optional types are not currently supported.
-
type Node:
+type Node:
val any
next ?Node
-var n = [Node:] -- Initializes.
+var n = Node{} -- Initializes.
In this example, next
has an optional ?Node
type so it can be initialized to none
when creating a new Node
object.
The following example will fail because this version of Node
can not be initialized:
@@ -1837,7 +1846,7 @@ Circular references.
^topic
@@ -1849,10 +1858,10 @@ Unnamed object. #
b float
next ?Node
-var node = [Node
- value: [a: 123, b: 100.0],
+var node = Node{
+ value: {a: 123, b: 100.0},
next: none,
-]
+}
^topic
Methods. #
@@ -1868,7 +1877,7 @@ Methods. #
self.inc(321)
print value
-var n = [Node value: 123, next: none]
+var n = Node{value: 123, next: none}
n.incAndPrint() -- Prints "444"
Methods can be declared outside of the type declaration. When using the flat declaration style, self
must be the first parameter to distinguish it from a type function:
@@ -1895,7 +1904,7 @@ Type functions. #
-- Declare namespace function inside `Node`.
func Node.new():
- return [Node value: 123, next: none]
+ return Node{value: 123, next: none}
var n = Node.new()
@@ -1918,12 +1927,12 @@ Declare struct. #
x float
y float
-var v = [Vec2 x: 30, y: 40]
+var v = Vec2{x: 30, y: 40}
^topic
Copy structs. #
Since structs are copied by value, assigning a struct to another variable creates a new struct:
-var v = [Vec2 x: 30, y: 40]
+var v = Vec2{x: 30, y: 40}
var w = v
v.x = 100
print w.x -- Prints '30'
@@ -1969,16 +1978,16 @@ Choices. #
^topic
Initialize choice. #
If the payload is an object type, the choice can be initialized with a simplified record literal:
-var s = [Shape.rectangle width: 10, height: 20]
+var s = Shape.rectangle{width: 10, height: 20}
The general way to initialize a choice is to pass the payload as an argument:
-var rect = [Rectangle width: 10, height: 20]
-var s = [Shape rectangle: rect]
+var rect = Rectangle{width: 10, height: 20}
+var s = Shape{rectangle: rect}
-s = [Shape line: 20]
+s = Shape{line: 20}
A choice without a payload is initialized with an empty record literal:
-var s = [Shape.point:]
+var s = Shape.point{}
^topic
Choice switch
. #
@@ -2000,7 +2009,7 @@ Choice switch
. #^topic
Access choice. #
A choice can be accessed by specifying the access operator .!
before the tagged member name. This will either return the payload or panic at runtime: Planned Feature
-var s = [Shape line: 20]
+var s = Shape{line: 20}
print s.!line -- Prints '20'
^topic
@@ -2080,11 +2089,11 @@ Type aliases. #
type Pos2 = Vec2
-var pos = [Pos2 x: 3, y: 4]
+var pos = Pos2{x: 3, y: 4}
^topic
-Type copies. #
-A type copy creates a new type using the same type and memory layout of a target type.
+Distinct types. #
+A distinct type creates a new type by copying a target type.
It's declared with type
name declaration followed by the target type specifier:
type Vec2:
x float
@@ -2092,7 +2101,7 @@ Type copies. #
type Pos2 Vec2
-var pos = [Pos2 x: 3, y: 4]
+var pos = Pos2{x: 3, y: 4}
Functions can be declared under the new type's namespace:
import math
@@ -2103,15 +2112,15 @@ Type copies. #
var dy = math.abs(o.y - y)
return dx + dy
-var pos = [Pos2 x: 3, y: 4]
-var dst = [Pos2 x: 4, y: 5]
+var pos = Pos2{x: 3, y: 4}
+var dst = Pos2{x: 4, y: 5}
print pos.blockDist(dst) --> 2
Note that functions declared from the target type do not carry over to the new type.
Unlike a type alias, the new type and the target type can not be used interchangeably since they are different types. However, instances of the new type can be casted to the target type, and vice versa: Planned Feature
type Pos2 Vec2
-var a = [Pos2 x: 3, y: 4]
+var a = Pos2{x: 3, y: 4}
var b Vec2 = a as Vec2
@@ -2136,7 +2145,7 @@ Generic types. #
Expand type template. #
When the template is invoked with compile-time argument(s), a specialized version of the type is generated.
In this example, String
can be used as an argument since it satisfies the type
parameter constraint:
-var a MyContainer(String) = [id: 123, value: 'abc']
+var a MyContainer(String) = {id: 123, value: 'abc'}
print a.get() -- Prints 'abc'
Note that invoking the template again with the same argument(s) returns the same generated type. In other words, the generated type is always memoized from the input parameters.
@@ -2253,7 +2262,7 @@ for
each. #
print n
Maps can be iterated. next()
returns a key and value tuple:
-var map = [ a: 123, b: 234 ]
+var map = Map{ a: 123, b: 234 }
for map -> entry:
print entry[0]
@@ -2509,7 +2518,8 @@ Modules. #
type float
type List
type ListIterator
-type tuple
+type Tuple
+type Table
type Map
type MapIterator
type String
@@ -2532,9 +2542,9 @@ Modules. #
type FFI
type CArray
type CDimArray
-map DirEntry
-map DirWalkEntry
-map ArgOption
+Map DirEntry
+Map DirWalkEntry
+Table ArgOption
test.
@@ -2691,40 +2701,40 @@ type error
#
type int
#
func $prefix~() int
func $prefix-() int
-
func $infix<(o any) bool
-
func $infix<=(o any) bool
-
func $infix>(o any) bool
-
func $infix>=(o any) bool
-
func $infix+(o any) int
-
func $infix-(o any) int
-
func $infix*(o any) int
-
func $infix/(o any) int
-
func $infix%(o any) int
-
func $infix^(o any) int
-
func $infix&(o any) int
-
func $infix|(o any) int
-
func $infix||(o any) int
-
func $infix<<(o any) int
-
func $infix>>(o any) int
+
func $infix<(o int) bool
+
func $infix<=(o int) bool
+
func $infix>(o int) bool
+
func $infix>=(o int) bool
+
func $infix+(o int) int
+
func $infix-(o int) int
+
func $infix*(o int) int
+
func $infix/(o int) int
+
func $infix%(o int) int
+
func $infix^(o int) int
+
func $infix&(o int) int
+
func $infix|(o int) int
+
func $infix||(o int) int
+
func $infix<<(o int) int
+
func $infix>>(o int) int
func fmt(kind symbol) String
Formats the integer using a kind specifier which can be binary .b
, octal .o
, decimal .d
, hexadecimal .x
, ASCII .c
.
-
func fmt(kind symbol, opts Map) String
-opts.pad
provides the ASCII rune that is used for padding with a string length of opts.width
.
+
func fmt(kind symbol, config Table) String
+opts.pad
provides the ASCII rune that is used for padding with a string length of config.width
.
func int.'$call'(val any) int
Converts a value to an 48-bit integer.
^topic
type float
#
func $prefix-() float
-
func $infix<(o any) bool
-
func $infix<=(o any) bool
-
func $infix>(o any) bool
-
func $infix>=(o any) bool
-
func $infix+(o any) float
-
func $infix-(o any) float
-
func $infix*(o any) float
-
func $infix/(o any) float
-
func $infix%(o any) float
-
func $infix^(o any) float
+
func $infix<(o float) bool
+
func $infix<=(o float) bool
+
func $infix>(o float) bool
+
func $infix>=(o float) bool
+
func $infix+(o float) float
+
func $infix-(o float) float
+
func $infix*(o float) float
+
func $infix/(o float) float
+
func $infix%(o float) float
+
func $infix^(o float) float
func float.'$call'(val any) float
Converts the value to a float
. Panics if type conversion fails.
^topic
@@ -2741,7 +2751,7 @@ type any
#
type type
#
^topic
type List
#
-func $index(idx int) any
+func $index(idx int) dynamic
func $index(range Range) List
func $setIndex(idx int, val any)
func append(list List)
@@ -2760,7 +2770,6 @@ type List
#
Removes an element at index idx
.
func resize(size int)
Resizes the list to len
elements. If the new size is bigger, none
values are appended to the list. If the new size is smaller, elements at the end of the list are removed.
-
func slice(start any, end any) List
func sort(lessFn any)
Sorts the list with the given less
function. If element a
should be ordered before b
, the function should return true
otherwise false
.
func List.fill(val any, n int) List
@@ -2772,8 +2781,16 @@ type ListIterator
type Tuple
#
func $index(idx int) any
^topic
+type Table
#
+func $initPair(key String, value any)
+
func $get(name String) dynamic
+
func $set(name String, value any)
+
func $index(key String) dynamic
+
func $setIndex(key String, value any)
+
^topic
type Map
#
-func $index(key any) any
+func $initPair(key any, value any)
+
func $index(key any) any
func $setIndex(key any, val any)
func contains(key any) bool
Returns whether there is a value mapped to key
.
@@ -3117,8 +3134,8 @@ os. #
Opens a directory at the given path
. iterable
indicates that the directory's entries can be iterated.
func openFile(path String, mode symbol) File
Opens a file at the given path
with the .read
, .write
, or .readWrite
mode.
-
func parseArgs(options List) Map
-Given expected ArgOption
s, returns a map of the options and a rest
entry which contains the non-option arguments.
+
func parseArgs(options List) Table
+Given expected ArgOption
s, returns a Table
of the options and a rest
entry which contains the non-option arguments.
func readAll() String
Reads stdin to the EOF as a UTF-8 string. To return the bytes instead, use stdin.readAll()
.
func readFile(path String) String
@@ -3180,7 +3197,7 @@ type FFI
#
Creates an ExternFunc
that contains a C function pointer with the given signature. The extern function is a wrapper that calls the provided user function. Once created, the extern function is retained and managed by the FFI context.
func bindLib(path ?String) any
Calls bindLib(path, [:])
.
-
func bindLib(path ?String, config Map) any
+
func bindLib(path ?String, config Table) any
Creates a handle to a dynamic library and functions declared from cfunc
. By default, an anonymous object is returned with the C-functions binded as the object's methods. If config
contains genMap: true
, a Map
is returned instead with C-functions binded as function values.
func bindObjPtr(obj any) pointer
Returns a Cyber object's pointer. Operations on the pointer is unsafe, but it can be useful when passing it to C as an opaque pointer. The object is also retained and managed by the FFI context.
@@ -3198,11 +3215,11 @@ type CArray
#
type CDimArray
#
^topic
-map DirEntry
#
+Map DirEntry
#
key summary 'name' -> Array
The name of the file or directory. 'type' -> #file | #dir | #unknown
The type of the entry.
^topic
-map DirWalkEntry
#
+Map DirWalkEntry
#
key summary 'name' -> Array
The name of the file or directory. 'path' -> Array
The path of the file or directory relative to the walker's root directory. 'type' -> #file | #dir | #unknown
The type of the entry.
^topic
-map ArgOption
#
+Table ArgOption
#
key summary 'name' -> String
The name of the option to match excluding the hyphen prefix. eg. -path
'type' -> metatype(String | float | boolean)
Parse as given value type. 'default' -> any
Optional: Default value if option is missing. none
is used if this is not provided.
^topic
test. #
The test
module contains utilities for testing.
@@ -3265,10 +3282,10 @@ Declare functions. #
^topic
Bind library. #
bindLib
accepts the path to the library and returns a object which can be used to invoke the functions declared from cfunc
:
-my lib = ffi.bindLib('./mylib.so')
+let lib = ffi.bindLib('./mylib.so')
lib.add(123, 321)
-Note that my
is used to allow lib
to be used dynamically since the type is unknown at compile-time.
+Note that let
is used to allow lib
to be used dynamically since the type is unknown at compile-time.
^topic
Search path. #
If the path argument to bindLib
is just a filename, the search steps for the library is specific to the operating system. Provide an absolute (eg. '/foo/mylib.so') or relative (eg. './mylib.so') path to load from a direct location instead. When the path argument is none
, it loads the currently running executable as a library allowing you to bind exported functions from the Cyber CLI or your own application/runtime.
@@ -3299,9 +3316,9 @@ Bind to Cyber type. #<
ffi.cbind(MyObject, [.float, .voidPtr, .bool])
ffi.cfunc('foo', [MyObject], MyObject)
-my lib = ffi.bindLib('./mylib.so')
+let lib = ffi.bindLib('./mylib.so')
-var res = lib.foo([MyObject a: 123.0, b: os.cstr('foo'), c: true])
+var res = lib.foo(MyObject{a: 123.0, b: os.cstr('foo'), c: true})
The example above maps to these C declarations in mylib.so
:
typedef struct MyObject {
@@ -3316,9 +3333,9 @@ Bind to Cyber type. #<
cbind
also generates ptrTo[Type]
as a helper function to dereference an opaque ptr to a new Cyber object:
ffi.cfunc('foo', [MyObject], .voidPtr)
-my lib = ffi.bindLib('./mylib.so')
+let lib = ffi.bindLib('./mylib.so')
-var ptr = lib.foo([MyObject a: 123, b: os.cstr('foo'), c: true])
+var ptr = lib.foo(MyObject{a: 123, b: os.cstr('foo'), c: true})
var res = lib.ptrToMyObject(ptr)
^topic
@@ -3407,8 +3424,7 @@ Throwing errors. #
throw error.Oops -- Throws an error with the symbol `#Oops`
func fail2():
- throw 123 -- Panic. Can only throw an error
- -- that implement the `Error` trait.
+ throw 123 -- Panic. Can only throw an `error` value.
throw
can also be used as an expression.
func fail():
@@ -3680,92 +3696,98 @@ Multi-thread. #
^topic
Dynamic Typing. #
-my
variables.
-- Invoking dynamic values.
-my
functions.
-dynamic
vs any
.
-- Recent type inference.
-- Assign to
var
.
+- Dynamic variables.
+- Runtime type checking.
+- Dynamic functions.
+- Dynamic objects.
+- Custom objects.
-Cyber supports dynamic typing with a less restrictive syntax. This can reduce the amount of friction when writing code, but it can also result in more runtime errors.
-my
variables. #
-Variables declared with my
are implicitly given the dynamic
type:
-my a = 123
+Dynamic typing is supported with a less restrictive syntax. This can reduce the amount of friction when writing code, but it can also result in more runtime errors.
+In Cyber, the let
keyword is used exclusively for dynamic declarations.
+Dynamic variables. #
+Variables declared with let
are implicitly given the dynamic
type:
+let a = 123
-^topic
-Invoking dynamic
values. #
-When a dynamic
value is invoked, checks on whether the callee is a function is deferred to runtime.
-my op = 123
-print op(1, 2, 3) --> RuntimeError. Expected a function.
+Typically a dynamic variable defers type checking to runtime, but if the compiler determines that an operation will always fail at runtime, a compile error is reported instead:
+let a = '100'
+
+print a / 2
+--> CompileError: Can not find the symbol `$infix/` in `String`
-^topic
-my
functions. #
-Functions declared with my
do not allow typed parameters or a return specifier.
-All parameters are implicitly given the dynamic
type.
-The return specifier is also implicitly dynamic
which indicates that the function can throw an error. This is only relevant when typed code calls a my
function:
-my foo(a, b, c):
- return a + b() + a[c]
+When a
is assigned a different type of value, its recent type is updated so the compiler can continue to surface errors ahead of time:
+a = [1, 2, 3]
+print a / 2
+--> CompileError: Can not find the symbol `$infix/` in `List`
^topic
-dynamic
vs any
#
-dynamic
values can be freely used and copied without any compile errors (if there is a chance it can succeed at runtime, see Recent type inference):
-my a = 123
-
-func getFirstRune(s String):
- return s[0]
+Runtime type checking. #
+If the type of a dynamic variable can not be determined at compile-time, type checking is deferred to runtime.
+In this example, the type for a
is unknown after assigning the return of a dynamic call to erase
.
+Any operation on a
would defer type checking to runtime:
+let a = erase(123)
-getFirstRune(a) -- RuntimeError. Expected `String`.
+print a(1, 2, 3)
+--> panic: Expected a function.
-Since a
is dynamic, passing it to a typed function parameter is allowed at compile-time, but will fail when the function is invoked at runtime.
-The any
type on the otherhand is a static type and must be explicitly declared using var
:
-var a any = 123
+If a dynamic variable's recent type differs between two branches of execution, the type is considered unknown after the branches are merged. Any operations on the variable afterwards will defer type checking to runtime:
+let a = 123
+if a > 20:
+ a = 'hello'
-func getFirstRune(s String):
- return s[0]
+-- Branches are merged. `a` has an unknown type.
-getFirstRune(a) -- CompileError. Expected `String`.
+print a(1, 2, 3)
+--> panic: Expected a function.
-This same setup will now fail at compile-time because any
does not satisfy the destination's String
type constraint.
-The use of the dynamic
type effectively defers type checking to runtime while any
is a static type and must adhere to type constraints at compile-time.
-A dynamic
value can be used in any operation. It can be invoked as the callee, invoked as the receiver of a method call, or used with operators.
^topic
-Recent type inference. #
-Although a dynamic
variable has the most flexibility, in some situations it is advantageous to know what type it could be.
-The compiler keeps a running record of a dynamic
variable's most recent type to gain additional compile-time features without sacrificing flexibility. It can prevent inevitable runtime errors and avoid unnecessary type casts.
-When a dynamic
variable is first initialized, it has a recent type inferred from its initializer. In the following, a
has the recent type of int
at compile-time because numeric literals default to the int
type:
-my a = 123
-
-The recent type can change at compile-time from another assignment.
-If a
is then assigned to a string literal, a
from that point on has the recent type of String
at compile-time:
-my a = 123
-foo(a) -- Valid call expression.
-a = 'hello'
-foo(a) -- CompileError. Expected `int` argument, got `String`.
-
-func foo(n int):
- pass
+Dynamic functions. #
+Functions declared with let
do not allow typed parameters or a return specifier.
+All parameters are implicitly given the dynamic
type.
+The return specifier is also implicitly !dynamic
which indicates that the function can throw an error. This is only relevant when typed code calls a dynamic function:
+let foo(a, b, c):
+ return a + b() + a[c]
-Even though a
is dynamic
and is usually allowed to defer type checking to runtime, the compiler knows that doing so in this context would always result in a runtime error, so it provides a compile error instead. This provides a quicker feedback to fix the problem.
-The recent type of a
can also change in branches. However, after the branch block, a
will have a recent type after merging the types assigned to a
from the two branched code paths. Currently, the any
type is used if the types from the two branches differ. At the end of the following if
block, a
has the recent type of any
type after merging the int
and String
types: Planned Feature
-my a = 123
-if a > 20:
- a = 'hello'
- foo(a) -- Valid call expression. `foo` can be called without type casting.
+The function foo
is a namespace function. It can't be reassigned like a variable after its declaration.
+However, function values (lambdas) assigned to variables allow reassignment. Lambdas can also capture variables from the parent scope:
+let count = 123
+let f = (a, b):
+ return a + b(10) + count
-foo(a) -- CompileError. Expected `String` argument, got `any`.
-
-func foo(s String):
- pass
+-- Reassign `f`.
+f = (a, b):
+ return count * 2 - b(a)
+
+Lambdas that simply return an expression can be written as:
+f = (a, b) => a + b(10)
^topic
-Assign to var
. #
-var
declarations are strictly for static typing. If the assigned value's type is dynamic
, the variable's type becomes any
.
-my getValue():
- return ['a', 'list']
-
--- Initialized as an `any` variable.
-var a = getValue()
+Dynamic objects. #
+The builtin Table
type is used to create dynamic objects.
+Tables are initialized with the record literal:
+let a = {}
+a.name = 'Nova'
+print a.name --> Nova
+
+Read more about how to use tables Tables.
+^topic
+Custom objects. #
+Custom objects allow declaring fields and methods. Planned Feature
+let Counter{ count }:
+ let inc():
+ count += 1
+
+let c = Counter{ count: 0 }
+c.inc()
+print c.count --> 1
+
+Fields are declared inside the braces and separated with commas.
+Unlike typed declarations, the fields declared only serve as a subset constraint.
+Additional fields can still be initialized and used.
+Objects can be declared without any methods: Planned Feature
+let Vec3{x, y, z}
+
+let v = Vec3{1, 2, 3}
^topic
Metaprogramming. #
@@ -3779,7 +3801,10 @@ Metaprogramming. #
Magic functions.
@@ -3804,8 +3829,7 @@ Metaprogramming. #
Operator overloading. #
All operators are implemented as object methods.
Incomplete: Not all operators have transitioned to the method paradigm.
-
Normally this would impact performance, but Cyber generates specialized bytecode for builtin types like int
and float
. The VM performs inline caching at runtime to eliminate the overhead of evaluating on dynamic operands.
-To overload an operator for an object type, declare $prefix
, $infix
, $postfix
methods. See the available builtin operators. Since operator names aren't allowed as standard identifiers, they are contained in a string literal.
+
To overload an operator for an object type, declare $prefix
, $infix
, $postfix
methods. See the available builtin operators. Since operator names aren't allowed as standard identifiers, they are contained in a string literal.
type Vec2:
x float
y float
@@ -3858,9 +3882,43 @@ Call module. #
var v = Vec2(1, 2)
^topic
-Getter/Setter. #
+$initRecord
method. #
Planned Feature
^topic
+$initPair
method. #
+The $initPair
method overrides the record initializer.
+After an instance of the type is created from its default record initializer, this method is invoked for each key-value pair in the record literal:
+type MyMap:
+ func '$initPair'(key any, value any) void:
+ print "$(key): $(value)"
+
+var m = MyMap{ a: 123, b: 234 }
+--> a: 123
+--> b: 234
+
+$initPair
is only allowed if the type has a default record initializer or $initRecord
is declared.
+^topic
+$get
method. #
+The $get
method allows overriding field accesses for undeclared fields:
+type Foo:
+ func '$get'(name String):
+ return name.len()
+
+var f = Foo{}
+print f.abc --> 3
+print f.hello --> 5
+
+^topic
+$set
method. #
+The $set
method allows overriding field assignments for undeclared fields:
+type Foo:
+ func '$set'(name String, value any):
+ print "setting $(name) $(value)"
+
+var f = Foo{}
+f.abc = 123 --> setting abc 123
+
+^topic
Missing method. #
Declare a $missing
method as a fallback when a method was not found in an instance.
Planned Feature
@@ -4615,7 +4673,7 @@ AOT #
keywords: {
keyword: [
'template', 'func', 'import', 'for', 'coinit', 'coresume', 'coyield',
- 'return', 'if', 'else', 'as', 'while', 'var', 'my', 'object', 'struct', 'with', 'caught',
+ 'return', 'if', 'else', 'as', 'while', 'var', 'let', 'dynobject', 'object', 'struct', 'with', 'caught',
'break', 'continue', 'switch', 'pass', 'or', 'and', 'not', 'is', 'error', 'throws',
'true', 'false', 'none', 'throw', 'try', 'catch', 'recover', 'enum', 'type', 'case'
],