Learn X in Y minutes

Where X=MakrellPy

MakrellPy is the Python-hosted member of the Makrell language family. It compiles to Python AST and runs on CPython, so every Python library is available. The syntax is compact and expression-oriented, built on a shared structural foundation called MBF that also powers the family's data and markup formats.

####################################################
## 1. Primitives
####################################################

# Numbers
42              # => 42
3.14            # => 3.14
-5.7e-3         # => -0.0057

# Number suffixes — scale or convert inline
2.5M            # => 2500000        multiply by 10^6
100k            # => 100000         multiply by 10^3
2pi             # => 6.283185...    multiply by pi
90deg           # => 1.570796...    degrees to radians
3i              # => 3j             Python complex

# Booleans and null
true            # => True
false           # => False
null            # => None

# Strings with optional suffixes
"hello"                 # => "hello"
"2026-04-01"dt          # => datetime(2026, 4, 1)
"ff"hex                 # => 255
"[a-z]+"regex           # => re.compile("[a-z]+")

# E-strings interpolate {expr} segments
name = "world"
"Hello {name}!"e        # => "Hello world!"

# Collections
[1 2 3]         # list  => [1, 2, 3]
(1 2 3)         # tuple => (1, 2, 3)
[]              # => []
()              # => ()


####################################################
## 2. Operators
####################################################

# Arithmetic
2 + 3           # => 5
10 / 3          # => 3.333...
2 ** 8          # => 256

# Comparison and logic
3 == 3          # => True
3 != 4          # => True
true && false   # => False
true || false   # => True

# + coerces to string when either side is a string
"age: " + 25    # => "age: 25"

# Assignment (right-associative, lowest precedence)
x = y = 10

# @ indexes into collections (high precedence)
items = [10 20 30]
items @ 0       # => 10
items @ 2       # => 30

# . accesses members (highest precedence)
"hello".upper   # => <method>

# .. creates a range/slice
1 .. 5          # => slice(1, 5)


####################################################
## 3. Calling Functions
####################################################

# Curly brackets call functions: {f arg1 arg2}
{print "hello"}               # prints: hello
{len [1 2 3]}                 # => 3
{max 10 20}                   # => 20

# Nested calls work as you'd expect
{print {len "Makrell"}}       # prints: 7


####################################################
## 4. Defining Functions
####################################################

# Named function
{fun greet [who]
    "Hello, " + who + "!"}

{greet "Makrell"}   # => "Hello, Makrell!"

# Anonymous function
double = {fun [x] x * 2}
{double 5}          # => 10

# Lambda with the arrow operator ->
triple = x -> x * 3
{triple 4}          # => 12

# Multi-arg lambda
hyp = [x y] -> {Math.sqrt x ** 2 + y ** 2}

# No-arg lambda
dice = [] -> {random.randint 1 6}

# Partial application: _ becomes a parameter
add = {fun [a b] a + b}
add5 = {add 5 _}        # => a function that adds 5
{add5 10}                # => 15

inc = {add _ 1}
{inc 99}                 # => 100


####################################################
## 5. Pipes and Composition
####################################################

# | threads a value left-to-right:  a | f  =>  {f a}
5 | double               # => 10
5 | double | triple      # => 30

# Combine with partial application for expressive chains
[1 2 3 4 5]
    | {filter {< _ 4}}       # keep items < 4 => [1, 2, 3]
    | {map {* _ 2}}           # double them   => [2, 4, 6]
    | sum                     # sum           => 12

# |* maps a function over each element
[1 2 3] |* double        # => [2, 4, 6]

# \ is the reverse pipe:  f \ a  =>  {f a}
double \ 5              # => 10

# -> binds tighter than |, so lambdas compose naturally
[1 2 3] | {map x -> x + 10}   # => [11, 12, 13]


####################################################
## 6. Control Flow
####################################################

# if is an expression — it returns a value
x = 7
{if x > 0 "positive" "non-positive"}   # => "positive"

# Chained conditions
{if x > 100 "high"
    x > 50  "medium"
            "low"}                       # => "low"

# when — like if with null as the implicit else
{when x > 0 {print "positive"}}

# Loops
{while i < 3
    {print i}
    i = i + 1}

{for item [10 20 30]
    {print item}}

# do block — introduces a scope, returns the last expression
result = {do
    a = 10
    b = 20
    a + b}
# result => 30


####################################################
## 7. Pattern Matching
####################################################

# Match tests a value against pattern-result pairs
{match 42
    0     "zero"
    42    "the answer"
    _     "other"}
# => "the answer"

# _ is the wildcard — matches anything

# Alternatives with |
{match 5
    2 | 3 | 5   "small prime"
    _            "other"}
# => "small prime"

# List patterns
{match [1 2 3]
    [1 _ _]   "starts with 1"
    [_ _ _]   "three items"
    _         "other"}

# Binding with =
{match [10 20]
    [a=_ b=_]   a + b
    _            0}
# => 30

# Type patterns
{match 42
    _:str   "string"
    _:int   "integer"
    _       "other"}
# => "integer"

# Constructor / dataclass patterns
{dataclass Point
    x:float
    y:float}

{match {Point 3.0 4.0}
    {$type Point [0.0 0.0]}   "origin"
    {$type Point [x y]}       "({x}, {y})"e}
# => "(3.0, 4.0)"

# Regular sequence patterns — regex-like matching on lists
{match [1 2 2 2 3]
    [$r 1 (some 2) 3]   "one, twos, then three"
    _                    "other"}
# => "one, twos, then three"
# Quantifiers: maybe (0-1), some (1+), any (0+), 3* (exact), (2..5)* (range)

# Boolean pattern operators
[1 2 3] ~= [_ _ _]     # => True   (matches?)
[1 2] !~= [_ _ _]      # => True   (doesn't match?)


####################################################
## 8. Classes
####################################################

{class Animal
    {fun __init__ [self name sound]
        self.name = name
        self.sound = sound}
    {fun speak [self]
        self.name + " says " + self.sound}}

a = {Animal "Cat" "meow"}
{a.speak}       # => "Cat says meow"

# Inheritance
{class Dog [Animal]
    {fun __init__ [self name]
        {{super}.__init__ name "woof"}}
    {fun fetch [self item]
        self.name + " fetches " + item}}

d = {Dog "Rex"}
{d.speak}        # => "Rex says woof"
{d.fetch "ball"} # => "Rex fetches ball"

# Dataclasses — auto-generate __init__, __repr__, etc.
{dataclass User
    name:str
    age:int
    email:str}

u = {User "Alice" 30 "[email protected]"}
u.name          # => "Alice"


####################################################
## 9. Exception Handling
####################################################

{try
    x = 1 / 0
    {catch e:ZeroDivisionError
        {print "caught:" e}}
    {finally
        {print "done"}}}
# prints: caught: division by zero
#         done

# {else} runs when no exception was raised
{try
    result = {int "42"}
    {catch ValueError {print "bad"}}
    {else {print "ok:" result}}}
# prints: ok: 42


####################################################
## 10. Python Interop
####################################################

# Import any Python module — interop is seamless because
# Makrell compiles to Python AST directly
{import json}
{json.loads "[1, 2, 3]"}    # => [1, 2, 3]

{import os.path}
{os.path.exists "/tmp"}     # => True or False

# Every pip package works.
# {import pandas}
# df = {pandas.read_csv "data.csv"}


####################################################
## 11. Async/Await
####################################################

{async fun fetch_data [url]
    response = {await {aiohttp.get url}}
    {await {response.text}}}

{async for item async_iter
    {await {process item}}}

{async with {aiohttp.ClientSession} session
    {await {session.get url}}}


####################################################
## 12. Metaprogramming
####################################################

# Quote captures syntax as data (AST nodes, not values)
q = {quote [a b c]}         # => a SquareBrackets node

# Unquote splices values into quoted forms
x = 42
q = {quote {unquote x}}     # splice x's value into the form

# Meta blocks run at compile time
{meta greeting = "hello from compile time"}

# Macros transform syntax before compilation
{def macro twice [ns]
    ns = {regular ns}
    expr = ns@0
    {quote [{unquote expr} {unquote expr}]}}

{twice 5}       # => [5, 5]

# User-defined suffixes
{def strsuffix upper [s] {s.upper}}
"hello"upper    # => "HELLO"

{def floatsuffix pct [s] {float s} / 100}
50pct           # => 0.5

# User-defined operators
{def operator <=> 50 left}


####################################################
## 13. A Complete Example
####################################################

{import collections}

{fun word_freq [text n]
    text
    | {str.lower _}
    | {str.split _}
    | {collections.Counter _}
    | {_.most_common n}}

{word_freq "one fish two fish red fish blue fish" 3}
# => [('fish', 4), ('one', 1), ('two', 1)]

Want to know more?


Got a suggestion? A correction, perhaps? Open an Issue on the GitHub Repo, or make a pull request yourself!

Originally contributed by Hans-Christian Holm, and updated by 1 contributor.