Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dewy at a Glance

Dewy aims to make the straightforward version of a program look straightforward. This tour shows the language's main ideas without trying to teach every rule at once.

Small Programs Stay Small

name = "Dewy"
printl"Hello, {name}!"

Bindings do not require a declaration keyword when the meaning is clear. let and const are available when you want to state mutability explicitly.

let attempts = 0
const limit = 3
attempts += 1

Expressions Compose

Conditionals and blocks produce values:

let access = if signed_in
    "account"
else
    "sign in"

let circumference = {
    let diameter = 2 * radius
    pi * diameter
}

Declarations and assignments produce void, so the circumference block expresses only its final calculation.

Functions Read Like Their Calls

let greet = (name:string greeting:string="Hello"):>void =>
    printl"{greeting}, {name}!"

greet("Ada")
greet("Grace" greeting="Welcome")

Parameters may be supplied by position or name. Defaults are evaluated for each call that needs them.

One Loop Covers the Common Cases

loop true reconnect()                       # repeat forever

loop attempts <? limit attempts += 1       # repeat while true

loop task in pending
    process(task)                           # consume an iterator

loop i in 0.. and task in pending
    printl"{i}: {task}"                     # combine iterators

A loop may also express values for a surrounding container to collect:

let active_names = [
    loop user in users
        if user.active
            user.name
]

Text Means User-Perceived Characters

Strings are immutable sequences of Unicode grapheme clusters. Iteration and indexing therefore treat a family emoji or an accented character as one element:

text = "café 👨‍👩‍👧‍👦 🍀"

loop i in 0.. and character in text
    if character not =? ' '
        printl"{i}: {character}"

Byte and scalar views remain available when a program actually needs those representations.

Values Do Not Alias by Accident

let original = [1 2 3]
let edited = original
edited[0] = 9                 # original is still [1 2 3]

Use a place when a function should deliberately update the caller's value:

let reset = (@value:int64):>void => (value = 0)

let count:int64 = 42
reset(@count)

The @ appears in both the function contract and the call, so shared mutation is visible where it matters.

Objects Need No Class Sublanguage

An object is a structural value with named fields. A constructor is an ordinary function that returns one:

let Counter:type = [value:int64 increment:<():>void>]

let counter = (start:int64=0):>Counter => [
    value = start
    increment = () => (value += 1)
]

let count = counter(40)
count.increment
count.increment
printl"count is {count.value}"

Functions inside an object can use sibling fields directly.

Types Add Meaning Where It Helps

let names:array<string> = ["Ada" "Grace"]
let answer:int64 | none = find_answer()

if answer isnt? none
    printl"the answer after this one is {answer + 1}"

Overloads use the same function syntax and are selected by their contracts:

let format = ((value:int64):>string => "integer {value}")
           & ((value:string):>string => value)

format(42)
format("already text")

Specialized Domains Use the Same Language

Physical quantities are one example of Dewy's general type model carrying useful facts:

let timeout = 300ms
sleep(timeout)

let distance = 120m
let elapsed = 10s
let speed = distance / elapsed

The unit portion can be checked and simplified at compile time rather than requiring a large runtime wrapper. The same goal—express meaning clearly, prove what can be proved, and keep runtime representation minimal—guides Dewy's facilities for applications, services, systems work, and numerical programming alike.

Continue with Getting Started, or use the Language Feature Index to jump to a particular subject.