Harsh: Rust without the braces. Functional programming: comprehensions, pipes and partial functions. Linear algebra: vectors, matrices and matrix arithmetic. Artificial intelligence and data science.

A layout transformation over Rust: indentation instead of braces, and four things Rust has no syntax for -- pipes, partial application, comprehensions and linear algebra. Every Rust crate works unchanged.

Harsh
use std.collections.HashMap

/// Count how often each word appears.
fn count words: &[&str] -> HashMap<String, usize>:
    let mut counts = HashMap.new$
    for w in words:
        *counts <- entry (w <- to_lowercase$)
                <- or_insert 0 += 1
    counts

fn main$:
    let text = "the fox and the dog and the cat"
    let words: Vec<&str> = text <- split_whitespace$
                                <- collect$
    let counts = count (&words)
    let mut top: Vec<_> = counts <- iter$
                                 <- collect$
    top <- sort_by
        |a, b|:
            b.1
              <- cmp a.1
              <- then (a.0 <- cmp b.0)
    for (word, n) in top <- iter$ <- take 3:
        println! "{word}: {n}"
Rust
use std::collections::HashMap;

/// Count how often each word appears.
fn count(words: &[&str]) -> HashMap<String, usize> {
    let mut counts = HashMap::new();
    for w in words {
        *counts.entry(w.to_lowercase())
               .or_insert(0) += 1
    }
    counts
}

fn main() {
    let text = "the fox and the dog and the cat";
    let words: Vec<&str> = text.split_whitespace()
                               .collect();
    let counts = count(&words);
    let mut top: Vec<_> = counts.iter()
                                .collect();
    top.sort_by(|a, b| {
        b.1
         .cmp(a.1)
         .then(a.0.cmp(b.0))
    });
    for (word, n) in top.iter().take(3) {
        println!("{word}: {n}")
    }
}

Why no braces

Here is what a brace language looks and reads like, once you notice it, and the message it was trying to convey:

I(would, like, to, go) {
    to the market so I(could buy) {
        some groceries
    }
    to the laundromat in order to(get) {
        clothes washed
    }
}
I would like to go
    to the market so I could buy
        some groceries
    to the laundromat in order to get
        clothes washed

The second is already in the first -- the indentation was there all along; the braces and parentheses only repeat it, in a form the eye has to skip past. Harsh weeds out the braces to let Rust shine.

Braces are for the compiler; indentation is for humans.

-- Claude, asked early in the work to justify removing braces from Rust at all

What Harsh adds

Braces are where Harsh starts, not where it stops. It adds four things Rust has no syntax for -- and they are the reason to use it.

Pipes and partial application

Give a function fewer arguments than it takes, and you get a function waiting for the rest. |> fills parameters from the left, <| from the right; the closure is flat and costs nothing at runtime.

Harsh
fn scale (factor: f64) (offset: f64) (x: f64)
        -> f64:
    x * factor + offset

fn main$:
    // Two of three arguments given:
    //     a closure waiting for x.
    let f = 2.0 0.5 |> scale
    let readings = vec! 1.0 2.0 3.0
    let scaled: Vec<f64> =
        readings <- iter$
                 <- map (|&x| f x)
                 <- collect$
    println! "{scaled:?}"
    // All three given: a plain call.
    println! "{}" (7.0 |> f)
Rust
fn scale(factor: f64, offset: f64, x: f64)
        -> f64 {
    x * factor + offset
}

fn main() {
    // Two of three arguments given:
    //     a closure waiting for x.
    let f = move |__hrs1| scale(2.0, 0.5, __hrs1);
    let readings = vec!(1.0, 2.0, 3.0);
    let scaled: Vec<f64> =
        readings.iter()
                .map(|&x| f(x))
                .collect();
    println!("{scaled:?}");
    // All three given: a plain call.
    println!("{}", f(7.0))
}

Generator comprehensions

Say what a collection holds, not how to fill it. g~ is lazy; list~, set~ and dict~ collect it. Each if belongs to the for before it.

Harsh
fn main$:
    // Pythagorean triples below 20:
    //     say what the list holds.
    let triples =
        list~ (a, b, c)
            for a in 1..20
            for b in a..20
            for c in b..20 if a * a + b * b == c * c
    println! "{triples:?}"
Rust
fn main() {
    // Pythagorean triples below 20:
    //     say how to fill the list.
    let triples: Vec<_> = (1..20)
        .flat_map(|a| {
            (a..20).flat_map(move |b| {
                (b..20).map(move |c| (a, b, c))
            })
        })
        .filter(|&(a, b, c)| a * a + b * b == c * c)
        .collect();
    println!("{triples:?}")
}

Linear algebra, as in Julia

Julia's matrix literal, Julia's *, slicing and broadcasting, and f<> a for f.(a) -- on nalgebra, through hrs_std.

Harsh
use hrs_std.*

fn main$:
    // A line fitted by least squares:
    //     Julia's literal, Julia's solve.
    let x = m~ [1.0 1.0; 1.0 2.0; 1.0 3.0]
    let y = v~ [1.0, 2.0, 2.9]
    let beta = x <- solve (&y)
    println! "{beta}"
Rust
use nalgebra::{DMatrix, DVector};

fn main() {
    // A line fitted by least squares,
    //     with nalgebra directly.
    let x = DMatrix::from_row_slice(
        3, 2, &[1.0, 1.0, 1.0, 2.0, 1.0, 3.0],
    );
    let y = DVector::from_vec(vec![1.0, 2.0, 2.9]);
    let beta = x.svd(true, true)
                .solve(&y, 1e-12)
                .unwrap();
    println!("{beta}")
}

Everything Rust has

Harsh is a superset of Rust. It states rules only for what it changes; wherever it is silent, Rust's rules apply, and braces mean what they mean in Rust. There is no semantic model in the transpiler -- generics, lifetimes, async, unsafe, and Rust syntax that does not exist yet pass through untouched. Every Rust crate works unchanged, and hrs-from converts existing Rust to Harsh -- byte-exact on the way back.

Rust for functional programming, data science and machine learning.

#[Ha<rs>.h]