diff --git a/index.html b/index.html index 99fffa45e..fcb65b192 100644 --- a/index.html +++ b/index.html @@ -985,7 +985,7 @@

Cyber Docs

-
v0.4-dev 135-f3bda25
+
v0.4-dev 157-5826946
@@ -1601,7 +1603,7 @@

Lists. #

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 ArgOptions, returns a map of the options and a rest entry which contains the non-option arguments.

    +

    func parseArgs(options List) Table

    +

    Given expected ArgOptions, 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 #

    keysummary
    'name' -> ArrayThe name of the file or directory.
    'type' -> #file | #dir | #unknownThe type of the entry.
    ^topic -

    map DirWalkEntry #

    +

    Map DirWalkEntry #

    keysummary
    'name' -> ArrayThe name of the file or directory.
    'path' -> ArrayThe path of the file or directory relative to the walker's root directory.
    'type' -> #file | #dir | #unknownThe type of the entry.
    ^topic -

    map ArgOption #

    +

    Table ArgOption #

    keysummary
    'name' -> StringThe name of the option to match excluding the hyphen prefix. eg. -path
    'type' -> metatype(String | float | boolean)Parse as given value type.
    'default' -> anyOptional: 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. #

    ^top

    -

    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' ],