Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Access

There are four access expressions:

  • namespace member access
  • receiver-qualified routine access
  • container member access
  • field member access

Receiver-Qualified Routine Access

One access form is the receiver-style routine call. In FOL this remains procedural syntax: the receiver value is the first routine input, and the dot form is only call-site sugar.

A receiver-qualified call consists of an expression (the receiver), followed by a single dot ., a routine name, and a parenthesized expression list:

"3.14".cast(float).pow(2);

Read:

value.method(arg1, arg2)

as:

method(value, arg1, arg2)

This spelling does not create classes, objects, inheritance, or runtime method ownership. It is just a shorter way to call a receiver-qualified routine.

Namespace Access

Accessing namespaces is done through the double-colon operator :::

use log: pkg = {"std/fmt/log"};
log::io::console::write_out.echo();

Container Access

Array, Vectors, Sequences, Sets

Containers can be indexed by writing a square-bracket-enclosed expression of type int[arch] after them.

var collection: int = { 5, 4, 8, 3, 9, 0, 1, 2, 7, 6 }

collection[5]
collection[-2]

Containers can be accessed with a specified range too, by using colon within a square-bracket-enclosed:

syntaxmeaning
:the whole container
elA:elBfrom element elA to element elB
:elAfrom beginning to element elA
elA:from element elA to end
collection[-0]                              // last item in the array
{ 6 }
collection[-1:]                             // last two items in the array
{ 7, 6 }
collection[:-2]                             // everything except the last two items
{ 5, 4, 8, 3, 9, 0, 1, 2 }

If we use double colon within a square-bracket-enclosed then the collection is inversed:

syntaxmeaning
::the whole container in reverse
elA::elBfrom element elA to element elB in reverse
::elAfrom beginning to element elA in reverse
elA::from element elA to end in reverse
collection[::]                              // all items in the array, reversed
{ 6, 7, 2, 1, 0, 9, 3, 8, 4, 5 }
collection[2::]                             // the first two items, reversed
{ 4, 5 }
collection[-2::]                            // the last two items, reversed
{ 6, 7 }
collection[::-3]                            // everything except the last three items, reversed
{ 2, 1, 0, 9, 3, 8, 4, 5 }

Matrices

Matrices are 2D+ arrays, so they use nested index access:

var aMat = mat[int, int] = { {1,2,3}, {4,5,6}, {7,8,9} };

nMat[[1][0]]

All other access forms behave like arrays.

Maps

Accessing maps is done by using the key inside square brackets:

var someMap: map[str, int] = { {"prolog", 1}, {"lisp", 2}, {"c", 3} }
someMap["lisp"]

Axioms

Accessing axioms is similar to accessing maps, but matching is broader and the result is always a vector of matches:

var parent: axi[str, str] = { {"albert","bob"}, {"alice","bob"}, {"bob","carl"}, {"bob","tom"} };

parent["albert",*]
parent["bob",*]

parent[*,_]

Matching can be with a vector too:

var parent: axi[str, str] = { {"albert","bob"}, {"alice","bob"}, {"bob","carl"}, {"bob","tom"}, {"maggie","bill"} };
var aVec: vec[str] = { "tom", "bob" };

parent[*,aVec]

A more complex matching example:

var class: axi;
class.add({"cs340","spring",{"tue","thur"},{12,13},"john","coor_5"})
class.add({"cs340","winter",{"tue","fri"},{12,13},"mike","coor_5"})
class.add({"cs340",winter,{"wed","fri"},{15,16},"bruce","coor_3"})
class.add({"cs101",winter,{"mon","wed"},{10,12},"james","coor_1"})
class.add({"cs101",spring,{"tue","tue"},{16,18},"tom","coor_1"})

var aClass = "cs340"
class[aClass,_,[_,"fri"],_,*,_]

Availability

To check whether an element exists, add : before []. The result is true when the element exists.

var val: vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

val:[5]
val:[15]


var likes: axi[str, str] = { {"bob","alice"} , {"alice","bob"}, {"dan","sally"} };

likes["bob","alice"]:
likes["sally","dan"]:

In-Place Assignment

Some access forms can bind a value while matching:

var val: vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var even: vec = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}
val[even => Y]
.echo(Y)

This is more useful with axioms:

var parent: axi[str, str] = { {"albert","bob"}, {"alice","bob"}, {"bob","carl"}, {"bob","tom"}, {"maggie","bill"} };

parent:[* => Y,"bob"]

Field Access

Field access expressions access stored data inside record-like values. Here is an example record user:

var user1: user = {
    email = "someone@example.com",
    username = "someusername123",
    active = true,
    sign_in_count = 1
};

fun (user)getName(): str = { result = self.username; };

There are two things you may access on such a value:

  • receiver-qualified routines
  • data fields

Receiver-Qualified Routines

The same dot spelling is used for receiver-qualified routines, but they remain ordinary routines rather than object-owned behavior:

user1.getName()

Data

There are multiple ways to access stored data. The simplest is the dot operator .:

user1.email

Another way is by using square bracket enclosed by name:

user1[email]

And lastly, by square bracket enclosed by index:

user1[0]