Mobile INput Development Language

by Garrett Jones

Mindl is a programming language designed for ergonomically typing code on mobile devices.

Design Philosophy

Mobile-first typing

Graceful defaults. Missing data returns undefined instead of throwing errors.

Quick Start

Your First Directive

Directives are expressions wrapped in square brackets. They execute when evaluated by the host application.

[date]

This displays today's date.

Right-to-Left Nesting

Space-separated tokens nest from right to left:

# Single token - just call the function
[date]                     # Returns today's date

# Two tokens - pass second as argument to first
[neg 5]                    # Returns -5

# Three tokens - nest right-to-left
[neg add(2, 3)]            # add(2,3)=5, then neg(5)=-5

Variables and Assignment

Use the colon operator to define variables:

[x: 5; y: 10; add(x, y)]   # Returns 15

Context References

The dot . refers to the current context object. For example, in a note-taking app:

[.path]                    # Get this item's path
[.name]                    # Get this item's name
[.path: "journal/today"]   # Set this item's path
[.up.name]                 # Get parent item's name
[.root.path]               # Get root ancestor's path

Data Types

Type Example Description
Number 42, 3.14 Integers and decimals
String "hello" Text in double quotes
Date date Date value (yyyy-MM-dd)
Time time Time value (HH:mm:ss)
DateTime datetime Combined date and time
List list(1, 2, 3) Ordered collection
Lambda lambda[add(i, 1)] Anonymous function

Built-in Functions

Arithmetic

add(5, 3)      # 8
sub(10, 4)     # 6
mul(3, 7)      # 21
div(20, 4)     # 5
mod(17, 5)     # 2
neg(8)         # -8

Comparison

eq(a, b)      # Equal
ne(a, b)      # Not equal
gt(a, b)      # Greater than
lt(a, b)      # Less than
gte(a, b)     # Greater or equal
lte(a, b)     # Less or equal

Logic

and(a, b)     # Logical AND
or(a, b)      # Logical OR
not(a)        # Logical NOT

Date & Time

date                         # Today's date
time                         # Current time
datetime                     # Current date and time
add_days(date, 7)            # One week from today
diff_days(d1, d2)            # Days between dates
parse_date("2026-01-15")     # Parse string to date

Strings

string("Hello " name "!")          # Concatenation (space-separated)
string("He said " qt "hi" qt)      # Produces: He said "hi"
string("Line 1" nl "Line 2")       # Produces two lines

Lists

list(1, 2, 3)                          # Create a list
first(myList)                          # Get first item
sort(myList, order: descending)        # Sort descending

Conditionals

if(condition, then_value, else_value)

Lambdas

Anonymous functions use the implicit parameter i:

# Single-argument lambda with implicit 'i'
[f: lambda[mul(i, 2)]; f(5)]              # Returns 10

# Multi-argument lambda with named parameters
[compare: lambda(a, b)[gt(a, b)]; compare(10, 5)]   # Returns true
Tip: Lambdas are commonly used for sort keys and find filters.

Querying Data

Search for items by path, name, or custom criteria:

# Find by exact path
[find(path: "inbox")]

# Find by pattern (matches ISO date paths)
[find(path: pattern(digit*4 "-" digit*2 "-" digit*2))]

# Find with custom filter (modified in the last week)
[find(where: lambda[after(i.modified, add_days(date, -7))])]

Scheduling

Schedule actions to run at specific times:

Daily at a specific time

Append today's date every morning at 6 AM:

[schedule(daily_at("06:00"), later[.append date])]

One-time reminder

Schedule a one-time action for 9 AM tomorrow:

[schedule(at(datetime(add_days(date, 1), "09:00")), later[.append "Don't forget!"])]

Deferred Execution

Use later to defer evaluation until execution time:

# Without 'later' - date is captured NOW
[x: date]                    # x = today's date, fixed

# With 'later' - date is captured when run
[x: later date]              # x = deferred; evaluates later

Interactive Buttons

Create tappable buttons that execute actions:

[button("Create Today's Entry", later[new(path: date, content: string("# " date))])]

Dynamic Views

Inline content from other sources, with automatic refresh:

# Show all entries with date-based paths, most recent first
[refresh view(sort(
    find(path: pattern(digit*4 "-" digit*2 "-" digit*2)),
    key: lambda[parse_date(i.path)],
    order: descending
))]

Patterns

String matching using word-based syntax instead of regex symbols:

# Match ISO dates like "2026-01-15"
pattern(digit*4 "-" digit*2 "-" digit*2)

# Match names starting with "Meeting"
pattern("Meeting" any*(0..))
Class Matches
digit 0-9
letter a-z, A-Z
space Whitespace
any Any character
Quantifier Meaning
*4 Exactly 4 times
*(0..) Zero or more
*(1..) One or more
*(0..5) Zero to five

Complete Example

Use Case: Daily Journal in a Notes App

Combine scheduling, buttons, and dynamic views to build an automated journal system:

# Automatically create today's entry at midnight
[schedule(daily_at("00:01"), later[
    maybe_new(path: date, maybe_content: string("# " date))
])]

# Button to manually create today's entry
[button("New Entry", later[new(path: date, content: string("# " date))])]

# View all entries, newest first
[refresh view(sort(
    find(path: pattern(digit*4 "-" digit*2 "-" digit*2)),
    key: lambda[parse_date(i.path)],
    order: descending
))]

Escaping Brackets

Escape brackets in text by doubling them:

This is not a directive: [[just text]]
# Displays: This is not a directive: [just text]