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.
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;
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!";
...
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 uselet _ = a (); let _ = b (); ...
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
.
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)
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!"
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;
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.