Forms
It's important to note that in idk-lang, every syntactically valid construct is known as a form. There are two types of forms, there are statements and there are expressions. Statements do not return a value. Expressions return a value.
Assignment & Blocks
Assignment comes in two flavors: a single-line assignment statement and a multiline assignment statement. Single-line assignment statements are (unsurprisingly) assignment occuring entirely on a single line. Multiline assignment statements must be started by a newline, and indented some amount of spaces to form a block. The last line of the block must be an expression and that value is what will be used for the assignment. Here are two valid assignment statements
Assignment is a statement, and thus cannot be used as a value. For
example, 1 + (x: 1)
would be a syntax error because
x: 1
does not return a value.
Also, in an attempt to have unified syntax, wherever a colon :
is syntactically valid, it can be followed by a single-line form or a
multiline list of forms. A multiline list of forms is known as a block
and must be indented with some amount of tabs or spaces (but not both!).
Expression Forms
The following forms (if
, when
, and
match
) are all expressions. Therefore, they can be used on
the right-hand side of a single-line assignment and as the expression in
the last line of the block of an assignment.
If Expression
The general syntax for if
expressions look like
this, (very similar to Python's syntax)
Remember, since if
is an expression, it returns a value. The
last value in the if
or else
block is what is
returned. For example,
This expression (rather verbosely) evaluates to the absolute value of
x
.
When Expression
when
expressions are a generalized version of if
expressions. They can take a list of boolean expressions and code to
execute. It's easier to explain with an example,
This mostly reads like english: When x and y are equal: return "equal". When the absolute value of their difference is less than one: return "close". Otherwise: return "far".
Notice, the first true boolean that is ecountered is the one whose branch will be executed. For example,
will return "which"
.
Parameterized When Expression
when
expressions are more than just multiple if expressions,
they can also pass paremeters to functions for a more expressive piece
of code. For example,
Here, x
and y
are passed to the functions in each
branch, until one evaluates to True
. If a when
expression is missing an else
branch, the compiler will
complain if it can't figure out that the when
expression is
exhaustive. That is, if the compiler can't tell that at least one of the
branches of the when
expression will return True
,
it will raise a compilation error.
Parameterized when
expressions can take any number of
parameters. Here's one with one parameter using operator sections that
evaluates to the sign of x
Match Expressions
The last expression form is a match
expression. It's similar
to the pattern matching facilities in other languages, but maybe slightly
more advanced. A match
expression is made up of a value
and several patterns to test the value against. Here's an example match
statement that computes the boolean expression x or y
,
Here, the match
expression tests the tuple x, y
against possible patterns. The underscore _
, or any other
variable name for that matter, matches against any object. Thus, the pattern
(_, True)
matches against all 2-tuples that have their second
element as True
.
match
expressions also have disjunctive patterns
meaning that multiple patterns can be matched in one branch. For example,
this expression returns True
is one of the elements is
0
,
match
expressions also have if
and
when
guards. These are useful for finer grained logic against
matched values. For example, let's say we have a tuple person
with type (String, Int)
representing the name and age of an
individual. Then we can have the following match
expression.
As you can hopefully see, if
guards are pretty useful. However,
as you can also hopefully see, multiple if guards begin to get pretty
verbose, even if they are doing conditionals on the same portion of the
pattern. There has to be a better way! In the same way that you can use
when
expressions as multiple if
expressions,
you can use when
guards as multiple if
guards! Here's a cleaner version of the code above,
Note: the when
guards need not be exhaustive. If
a when
guard fails to find a branch that evaluates to
True
, the entire pattern matching branch fails and moves
onto the next branch. Because of this, the else
clause in the
example above could be on either the when
guard block or as
the final clause in the match
expression.
The last feature of match
expressions is being
able to have multiple instances of the same variable in one pattern.
Here's an example of a match
expression that
evaluates to True
if the 2-tuple
tup
contains two elements that are equal to
eachother,
This is exactly euivalent to
Thus the additional constraints on tup
having two elements that
are of the same type and that inherit from Equatable
will apply.
The next section will be on loops and lambdas.