Capabilities
Capabilities can modify how a function is called, implemented or changes compile time informations about an object.
Capabilities:
-
Unused: Still implement semantic, even if unused
-
Inline: Function name is removed its own environment (prevents direct recursion). Tells compiler to eventually directly implement function instead of calling it. Classes are used as tuples, which cannot be casted to traits.
-
Constant: Function can be computed at compile time. Only primitive datatypes can be used.
-
Internal: Semantic is handled by the compiler
Requires:
-
Ensure that certain properties are valid. id in ensurecond can also be functions in the same class which only have the class as parameter, next to the attributes of the class.
-
Function cannot be called if one ensurecond is invalid
Ensures:
Ensurance is a way to express the state of an object during compile-time. This allows the programmer to detect invalid state at compile-time.
-
Ensures that after calling the function, certain properties are valid. The id result can be accessed to ensure that the result behaves in a certain way. This also can be conditional on the result, so if the user ensures the ensurecond0, ensurecond1 would automatically be valid.
-
Deductions are made based on the implication ensurecond0 => ensurecond1. If ensurecond0 is valid ensurecond1 is also valid. Then ensurecond1 is checked if it is ensurecond1 in an implication.
-
The idcall in ensurecond or ensurecond1 will be reset before ensurance is made. Meaning if
connected == False
was ensured and#!ensure connected == True
is called,connected == False
will no longer be valid. -
When the condition is a binary expression, the left side is the a compile-time object and the right side it's state. The compile-time object is bound to the runtime-object (object of class, trait, type). If it is unary, the expression will be the compile-time object.
-
Deductions:
- x == True => x != False
- x == False => x != True
- x => x == True
- !x => x == False
- x < y => x != y
- x > y => x != y
- x < y, y < z => x < z (This makes x > 0 valid for x > 512)
Example:
class Client(_addr : Address)
_nativeConn : &Option{NativeClientConnection}
#!requires connected == False
func _init
_nativeConn = Option{NativeClientConnection}.None()
;
#!ensures result == False => _nativeConn == Option.None
#!ensures result == True => _nativeConn == Option.Some
func connected(This) : bool
match _nativeConn
Some(_) => return True ;
_ => return False ;
;
;
#!requires connected == False
#!ensures result == True => connected == True
#!ensures result == False => connected == False
func connect(This) : bool
_nativeConn = createNativeClientConnection(_addr)
match _nativeConn
Some(_) => return True ;
_ => return False ;
;
;
#!requires connected == True
#!ensures connected == False
func disconnect
conn := _nativeConn :: Option.Some
conn.disconnect()
;
#!requires connected == True
func send(This, msg : Array{u8})
conn = _nativeConn :: Option.Some
conn.send(msg)
;
#!require connected == True
func receive(This, msg : Array{u8})
conn = _nativeConn :: Option.Some
conn.send(msg)
;
;
func main
client := Client(Address("127.0.0.1", 22))
ensure client.connect()
std.io.out << "Connected to server" << std.io.endl
client.send("help the world")
client.disconnect()
// client.send, receive no longer callable
else
// client.send, receive cannot be called here
// as ensure connected == True is invalid
std.io.out << "Connection failure" << std.io.endl
;
;