Language Guide

1.0 Installing

I recommend downloading the APK file from the bin/ directory in the github repo.

The editor also works on linux. Clone the repo. Run main.py in python3.10 (or higher version) and install any libraries python complains about.

Note: You might have to copy the library code/example code manually if you want/need it. I do not know why buildozer sometimes leaves it out.

You can also build the project yourself using the buildozer tool.

1.1 Data Types

PocketML uses Sum types like most other statically typed functional languages:

data Maybe a
    | Just a
    | Nothing

Type aliases can be used to abbreviate the names of other data types:

type Mb a = Maybe a;

1.2 Branching

Pattern matching is based on the case keyword and can match basic data types:

case 1
    | 1 -> "One"
    | _ -> "Something else"

And sum types (except for numpy arrays):

case Just 1
    | Just x -> print x
    | Nothing -> ()

Lists are also sum types:

case [1,2,3]
    | Cons x _ -> print x
    | Nil -> print "empty..."

Use if-then-else for branching:

let f x = if x then "True!" else "false :(";
f True

When programming with side effects, an else branch may not be needed. The if-then expression must always return Unit.

let f : Bool -> Unit;
let f b = if b then print "True!";
...

1.3 Do-Syntax

When many functions need to be executed one after another, for example to cause side effects, the do-syntax can be used.

let f _ = do
    print "1"
    print "2"
    print "3"
    launch_missiles ()
;

Note: Do not confuse this do syntax with monadic do-notation in Haskell! This is more of a C-like block (e.g. { ...; ...; }). The do-syntax is not perfect and might fail to parse in some situations. When in doubt use let _ = a (); let _ = b (); ...

1.4 Lists

Lists can be created like in the following example:

import std;

print [1, 2, 3]
# => (Cons 1 (Cons 2 (Cons 3 Nil)))

Numpy arrays can be created using the @(x1, x2, x3, ...) syntax:

print (@(1, 2) + @(3, 4)) # => [4. 6.]

They have type Vec.

1.5 Tuples and Records

PocketML supports both tuples and records. It is best to use records and tuples sparingly, as custom data types carry more information and are more strongly typed.

type Point = (Number, Number);

type Person =
    { name: String
    , age: Number
    , location: Point };

Note that PocketML does not support tuple and record pattern matching yet!

Records also support a sort of weak row-polymorphism.

let getX : { x : a } -> a;
let getX r = r.x;

print $ getX {x=10, y=20}

Generally a record {x : a, y : b} and a record {y : b} unify. That also means that the following example only fails at runtime.

let getX : { x : a, y : b } -> a;
let getX r = r.x;

print $ getX {y=20}

Note: The section Python Interop has an example of how this can be used to implement named default arguments.

The standard library lib.std has functions for updating records:

import lib.std;

let myrec = {x=1,y=2,z=3};

do
    print (with { x = 22 } myrec)
    print (recordMap (\r -> with {x=r.x+1} r) myrec)

1.6 Functions, recursion and let

Variables are generally introduced using the let keyword. Let declarations can be used to introduce a variables type before defining it:

let pi : Number;
let pi = 3;

Functions can be introduced using \ and are anonymous. To create recursive functions, the rec keyword or an explicit type annotation is required:

let rec sum = \x -> case x
    | Nil -> 0
    | Cons x xs -> add x (sum xs);

print (sum [1,2,3,4])

Or alternatively:

let sum : (List Number) -> Number
let sum = \case
    | Nil -> 0
    | Cons x xs -> add x (sum xs);

print (sum [1,2,3,4])

Note: The above example uses the \case notation which is equivalent to \x -> case x ....

Functions can also be introduced using let:

let greet x = print2 "Hello," x;
greet "there!"

1.7 Modules

PocketML projects are organized into modules. A module exports variables, types and type aliases. The explicit module declaration can limit what is exported:

let greet x = print2 "Hi," x;
let pi = 4;
module (greet, pi)

Modules can also use (*) to export all types and variables.

let greet x = print2 "Hi," x;
let pi = 4;
module (*)

Modules inside a directory can be addressed using .:

import directory.mymodule;

1.8 Doc comments

The editor has a builtin search panel for looking up types or searching for a function with a certain type. Any comments before the semicolon in a declaration will be included as a doc comment:

data List a
	= Cons a (List a)
	| Nil
	# The default list type generated
	# by `[...]`
;

let mkDict : a -> Dict b
    # WARNING: `a` should always be a record.
;

type Color = Vec
	# Just a vector...
;

Most of the standard library is written in a self-documenting way to save on excessive comment clutter when searching in the doc panel.