Dewy
An Everyday Language
Dewy is a language for clear, expressive computation—useful for anything from ordinary programming to complex engineering, science, and mathematics, and everything in between.
curl -fsSL https://dewy-lang.org/install.sh | bash
Linux and macOS. Windows: irm https://dewy-lang.org/install.ps1 | iex · Install details · Try examples in your browser
text = "café 👨👩👧👦 🍀"
loop i in 0.. and c in text
if c not =? ' ' {
printl"{i}: {c}"
}
0: c
1: a
2: f
3: é
5: 👨👩👧👦
7: 🍀
The compiler builds native executables and WebAssembly. It is written in Sloppy for now; Dewy will compile itself later. The browser playground runs precompiled examples, and Status lists what the compiler passes today.
Key Features
Capabilities at the heart of Dewy’s design. Every example on this page is compiled and run with the current compiler on each site build.
1from units import kg, m, N, J, W
2
3# Units are values. Juxtaposition multiplies.
4mass = 10kg
5velocity = 30m/s
6energy = 1/2 * mass * velocity^2 # 4500 J
7
8force = 1000kg * 9.8(m/s^2) # 9800 N
9work = 20N * 10m # 200 J
10power = work / 4s # 50 W
11
12printl"{energy/J} J {force/N} N {power/W} W"
Physical units, built in
Dewy treats kilograms, meters, and seconds as values with dimensions, not as plain numbers. Juxtaposing a number with a unit multiplies them, dimensions carry through arithmetic, and mismatched dimensions are errors—so 20N * 10m is joules and 3m + 5s doesn’t compile. Dimensions are checked at compile time and cost nothing at runtime.
1# Bounds are visible: [ ] includes, ( ) excludes.
2window = [0..10) # 0 1 ... 9
3evens = 0,2..100 # step inferred from the pair
4countdown = [10,8..0] # negative steps too
5
6printl(16 in? (5..15]) # false
7
8# Slices use the same bounds.
9samples = [3 1 4 1 5 9 2 6]
10printl(samples[end-3..]) # [5 9 2 6]
11printl('hello world'[0..5)) # hello
Ranges with clear bounds
Brackets say exactly which endpoints belong to a range, a second anchor sets the step—including negative steps—and the same bound forms drive loops, membership tests, and slicing.
1ratings = [
2 'star wars' -> 73
3 'star trek' -> 89
4 'legend of the galactic heroes' -> 100
5]
6
7# One keyword covers while, for, and beyond.
8loop [show rating] in ratings
9 printl'I give {show} a {rating} out of 100'
10
11# Iterators compose logically.
12loop i in 0.. and letter in 'Dewy' printl'{i}: {letter}'
One loop to rule them all
There is no for / while / do-while zoo—loop takes any condition. The in operator assigns the next value and reports whether one existed, so iterators can combine with and or or in a single loop.
1# Integers grow as needed instead of overflowing.
2big = 2^100
3printl(big)
4
5# Dividing integers gives an exact fraction.
6third = 1/3
7printl(third + 1/6) # 1/2
8
9# Decimal literals are exact too.
10printl(0.1 + 0.2 =? 0.3) # true
Exact by default
int is the mathematical integer: word-sized while the value fits, big when it doesn’t, and never silently wrapped. Dividing integers gives a rational that prints as the fraction it is, and decimal literals are exact fractions unless a float is asked for. Floats are strict IEEE 754 when you want them.
1# A type can carry a condition on its values.
2Positive:type = int64<(>? 0)>
3NonEmpty:type = array<int64 length >? 0>
4
5first = (xs:NonEmpty):>int64 => xs[0]
6span = (a:int64 b:int64<(>=? a)>):>int64 => b - a
7share = (total:int64 n:Positive):>int64 => total // n
8
9printl(first([3 5 8])) # 3
10printl(span(3 10)) # 7
11printl(share(84 2)) # 42
12
13# first([]) and share(84 0) are compile errors.
Types that carry useful facts
Refinements let a type say “positive integer” or “non-empty array”. The compiler proves each condition from literals and ordinary control flow—a guard, a store, a loop range—and rejects a call that provably breaks one. A proven fact also removes work: xs[0] on a non-empty array needs no bounds check. Refinements cover integer ranges, array and string lengths, and record fields.
1xs = [3 1 4 1 5]
2
3# The loop range proves every index is in bounds.
4total = 0
5loop i in [0..xs.length) total += xs[i]
6printl(total) # 14
7
8# When an index can't be proven, ask for a value.
9printl(xs.get(9)) # none
10printl(xs.get(2)) # 4
No traps behind your back
Indexing, division and narrowing can fail, so Dewy’s rule is that each is proven from facts the compiler can see, handled as a value (xs.get(i) is int64 | none), or checked explicitly. Where the compiler can’t yet prove one, today’s compiler still inserts a runtime check that stops the program with a report. Turning those into compile errors with fix-its is under way. See what that adds up to in Safer than Rust.
1# `&` combines implementations into one overload set.
2format = ((value:int64):>string => "integer: {value}")
3 & ((value:string):>string => "text: {value}")
4
5printl(format(42)) # integer: 42
6printl(format("Dewy")) # text: Dewy
7
8# `@` passes a function without calling it.
9double = (x:int64):>int64 => x * 2
10twice = (f:<(x:int64):>int64> x:int64) => f(f(x))
11printl(twice(@double 10)) # 40
Functions are ordinary values
Function literals are expressions like any other: pass them, store them, and combine them. The & operator merges two implementations into one overload set, and each call selects the implementation whose parameter type matches the argument. @double names the function itself, ready to pass along.
1counter = (start=0) => [
2 value = start
3 increment = () => (value += 1)
4]
5
6# make a new counter object
7count = counter(40)
8
9# called on access
10count.increment
11count.increment
12
13printl"count is {count.value}"
Objects, blocks, and functions are one thing
Dewy’s syntax is deliberately unified: functions can construct structural objects, members can capture their siblings, and accessing a zero-argument function member calls it. Fields and behavior live together without separate class machinery, and a member that updates its siblings changes the object it is called on.