This document mostly contains a basic overview showing some possible syntax for Brisk, this is a work in progress and more meant to give an idea on what the language will look like and is by no means complete, the first week of the course will focus on solidifying and writing out a proper grammar.
For reference or to get a feel for the language the grain docs are a good reference as the syntax is very similar, brisk mostly tries to fix semantic problems with grain and take a different approach to type checking that allows for more clear programs, along with a completely ground up compiler architecture.
Module
Grain Inspired: https://grain-lang.org/docs/guide/modules A module is a top level structure that contains code, you could almost compare it to a namespace in the classic c world, they can be used to organize code into logical units each file contains a module and modules can contain submodules.
module Main
provide let x = 1 // exposes Main.x
let y = 1 // not exposed
provide module SubModule { // Submodule Main.Submodule.
provide let x = 1 // Main.Submodule.x
}
From Syntax
Grain Inspired A module can be included from a file into another file with the following syntax:
from "<filepath>" include ModuleName // We state the name here so it's easy to anchor too
Statements
Control Flow
If
Standard c style if statements
if (<expr>) { // Can also just be an expr
<statementList>
} else { // optional else
<statementList>
}
Loops
For
Brisk will implement loops with a standard c style syntax.
for (<expr>; <expr>; <expr>) {
<statementList>
}
While
Brisk will also implement standard while loops with a c style syntax though for v1 at least do-while
loops will not be implemented.
while (<expr>) {
<statementList>
}
Generic
Provide
provide <statement> // This exposes a statement from a module
Let
Grain Inspired: https://grain-lang.org/docs/guide/mutation
let x = 1 // x is now 1 and its immutable
let x = 2 // we just shadowed x,
let mut y = 1 // we can change y now.
let rec z = [1, ...z] // z is recursively dependent on itself creating an infinite list
let rec f = (a) => a < 10 ? 1 + f(a) : 1 // recursive function
Expressions
DataTypes
Numbers
// Numbers are generic I think I am going to use subtyping to handle differences between floats and ints but investigation needs to be done
1
0x1A
String
// Strings are similar to c
"str"
f"str ${<expr>}"
"""
Multiline String
"""
Functions
Functions are first class and can be passed as if they were data, they can be recursive which is identifier by a rec
keyword on the let
binding.
() => <expr>
x => <expr>
(x, y) => <expr>
(x=, y) => <expr> // x is a named argument
(x=None, y) => <expr> // x is named but has a default
curry (x, y) => <expr> // can be applied partially
(x: Number): Number => x // x must be a number, and the func must return a number
Lists
Lists are just syntax for linked lists, internally they are reprsented using adts
[1, 2, 3] // a list with three numeric items
[1, ...lst] // puts the contents of lst into the new list, can only be done at the end for perf reasons
Arrays
[> 1, 2, 3] // Similar to list syntax but with `>` to mark them
[> 1, 2, ...arr] // array spread TBD on implementation details
Records
TBD
ADTS
TBD
General
Pattern Matching
Grain Inspired: https://grain-lang.org/docs/guide/pattern_matching I don’t know if this will hit v1 but compared to switch statements Brisk will have pattern matching which allows for rich pattern based destructuring
match (x) { // tries to keep things exhaustive
// if the input is Option<_>
// then this matches the Some(value) case binding the value to x
Some(x) => <expr>,
_ => <expr> // default case
}
Ternary
Similar to c style languages this is just an if statement for expressions:
<condition_expr> ? <true_expr> : <false_expr>
Function Application
In brisk function’s can be curried as in you can partially apply a function take the below code:
module Progarm
// a List submodule
module List {
// List.map, takes a list and function and
// calls the function with the list
// curry keyword says we can apply this partially
provide let rec map = curry (callback, data) => {
// recursively using pattern matching to go through the list
match (data) {
[value, ...rest] => [callback(value), ...map(rest)],
[] => []
}
}
}
// type for visual example
type addLastIter = (List<string>) => List<string>
// we only applied the callback so we now have a mapping function
let addLastNameIter: addLastIter = List.map((data) => data ++ " Last")
// we now apply the data and call the function because all params are filled
assert addLastNameIter(["Jake", "Brian"]) == ["Jake Last", "Brian Last"]