The Crow Programming Language

Appendix B: Coming from Another Language

Every language you already know has habits that Crow deliberately breaks. Each wrong reflex below produces a compile error that names the fix, so you would find these out anyway — this appendix just saves you the round-trips. Read the six facts, then the section for your language.

The six facts that cover most of it

  1. Calls use square brackets. f[x], sqrt[9], log['hi']. Parentheses only group arithmetic.
  2. One string quote, and interpolation is paired backticks. 'hello name'. No + on strings: interpolate, or join[parts, sep].
  3. Comparison and logic are words. eq ne gt lt gte lte, and or not. No ==, <, &&, !, and no +=.
  4. Blocks are indentation; there is no :, {}, end, or ;. fn f[x], if x gt 0, else if, while, for x in xs.
  5. No lambdas, no null, no exceptions. Behavior is a named fn passed by name; failure is a value with .ok and .reason that propagates through use and refuses to decide a branch.
  6. for is an expression that runs in parallel. ys = for x in xs … x * 2 is your map; a trailing if is your filter; any/all/find with a named predicate are your early-exit searches. There are no threads to write.

A seventh, for anyone with a data background: an all-numeric array is a tensor (* is matmul, .* is element-wise), and a table is a frame (use frame), not a matrix.

(The same material, phrased for pasting into an AI assistant, lives in docs/crow-for-llms.md.)

From Python

PythonCrow
f(x)f[x] — brackets call
"text" / f"hi {name}"'text' / 'hi `name`'
"a" + "b"interpolate, or join[parts, sep]
x += 1x = x + 1
== != < >=eq ne lt gte
and or notsame words, same meaning
def f(x):fn f[x] + indentation (no :)
lambdaa named fn, passed by name
classstruct + free functions
elifelse if
match/caseif/else if/else
import numpy as npuse math (bare) or use math as m
print(x)log[x]
Nonedoesn't exist — error values + .ok
True/Falsetrue/false
try/except/raiseerrors are values (chapter 7); raise is return error[reason, kind]
a, b = f()r = f[]; a = r[0]; b = r[1]
[x*2 for x in xs]for-as-expression
dictmap[k = v] (one value type) or a struct
# comment--- comment
global variables in functionsinvisible — pass parameters (chapter 4)
threads / asyncio / multiprocessingdelete them — for is already parallel; later[f] for background work
pandas.DataFrameuse frame; df[df.age > 30] → filter[df, df.age gt 30]
numpy arraysplain numeric arrays; @ → *, * → .*
pytest filestest blocks under the function (chapter 9)

From JavaScript and TypeScript

JavaScriptCrow
const x = 5; / let / varx = 5 — no keyword, no semicolon
f(x)f[x]
"str" / `hi ${name}`'str' / 'hi `name`' (backticks go inside the quotes)
"a" + binterpolate, or join
=== !== < >=eq ne lt gte
&& || !and or not
x++, x += 1x = x + 1
function f(x) { } / (x) => x * 2fn f[x] + indented body; no arrow functions
{ } blocksindentation
x ? a : bif/else statements
console.log(x)log[x]
null / undefineddon't exist — error values + .ok
try/catch/throwerrors are values; throw is return error[reason, kind]
async/await, Promisesnone — later[f] runs work off the loop; for is parallel
xs.map(f), .filter, .reducefor-as-expression; named reductions (sum …)
xs.find(p), .some, .everyfind[xs, p], any[xs, p], all[xs, p] with a named fn
{ k: v } objectmap[k = v] (one value type) or a struct
classstruct + free functions
import x from 'y'use y (then y.x)
JSON.parse(s)json[s] (error value on bad input)
xs.lengthlen[xs]
// comment--- comment
React/Vue components + fetch + RESTview blocks over reactive state, rendered on the server (chapter 12)
npm test, Jesttest blocks under the function; crow test

From R

RCrow
x <- 5x = 5
f(x)f[x]
c(1, 2, 3)[1, 2, 3]
1-based indexing, x[1]0-based, x[0]; x[-1] is the last element, not "all but the first"
function(x) x * 2a named fn, passed by name
xs %>% f() %>% g(a)xs then f then g[a]
sapply(xs, f), lapply, purrr::mapfor-as-expression
df[df$age > 30, ], dplyr::filterfilter[df, df.age gt 30]
df$coldf.col
nrow(df)len[df]
group_by %>% summarisegroup[df, 'dept', ['salary', 'mean']]
NULL, NAno null — error values: a NULL cell read from a database, or a frame's missing cell, is an error of reason missing; filters exclude missing cells
TRUE/FALSEtrue/false
== != <=eq ne lte
&& || !, & |and or not (they work element-wise on columns too)
ifelse(c, a, b)if/else (columns: build a mask, then filter)
paste0("a", x), sprintfinterpolation '`x`', fmt[x, decimals = 2]
print(x), catlog[x]
library(x)use x
stop("msg"), tryCatchreturn error['msg', kind]; check .ok
%in%v in xs
# comment--- comment
mean(x), sd(x)mean[x], std[x] after use math

From MATLAB and Octave

MATLABCrow
f(x) and x(2) — parentheses for bothf[x] and x[1] — brackets for both, 0-based
[1 2 3], [1, 2, 3][1, 2, 3] (commas required)
[1 2; 3 4][[1, 2], [3, 4]]
A * B, A .* Bthe same: * matmul, .* element-wise (also ./, .^)
A'transpose[A]
A \ bsolve[A, b]
zeros(3, 4), eye(3), linspace(0, 1, 5)zeros[3, 4], eye[3], linspace[0, 1, 5] after use math
x(end)x[-1]
length(x), numel, size(A)len[x], A.shape
function y = f(x) … endfn f[x] + indented body; last expression returns
if … elseif … else … endif / else if / else, indentation, no end
for i = 1:10 … endfor i in range[10] (0–9); the loop is parallel
while … endwhile, indentation
~=, ==, <=ne, eq, lte
&& || ~and or not
% comment--- comment
disp(x), fprintf('%.2f', x)log[x], log['`fmt[x, decimals = 2]`']
'text'the same single quotes; "text" is not accepted
struct('a', 1)struct Name [a] then Name[1]
cellfun, arrayfunfor-as-expression
try … catch … enderrors are values; check .ok
parforplain for — already parallel
tables (readtable)use frame; frame.load['file.csv']

From Julia

JuliaCrow
f(x)f[x]
function f(x) … end / f(x) = x * 2fn f[x] + indented body
x -> x * 2a named fn, passed by name
"hi $name", "$(x + 1)"'hi `name`', '`x + 1`'
1-based, x[end]0-based, x[-1]
A * B, A .* B, A .^ 2the same operators
A'transpose[A]
[x^2 for x in xs], map(f, xs)for-as-expression
Dict("a" => 1)map[a = 1]
struct P; x; y; endstruct P [x, y]
x::Int annotationsnone — inferred
using Statisticsuse math.stats
nothing, missingno null — error values
try/catch, error("…")errors are values; return error['…', kind]
Threads.@threads forplain for — already parallel
push!(v, x)v = push[v, x] — nothing mutates in place
println(x)log[x]
# comment--- comment
== != <= && || !eq ne lte and or not
1:10range[1, 11] (exclusive end)
@time, @testcrow check reports timings; test blocks

From Go

GoCrow
x := 5, var x intx = 5
func f(x int) int { }fn f[x] + indented body — no types written
f(x)f[x]
if err != nil { return err }nothing — a failed value propagates through use automatically; check .ok only where you handle it
value, err := f()v = f[] — one value that carries its own .ok and .reason
errors.New("…"), fmt.Errorferror['…', kind]
nildoesn't exist
for i := 0; i < n; i++for i in range[n] (parallel) or while (sequential)
for _, x := range xsfor x in xs
go f(), channels, sync.WaitGroupfor is already parallel; later[f] for background work
map[string]int{}map[] then set[m, k, v]
append(xs, x)xs = push[xs, x]
type P struct { … } + methodsstruct P [x, y] + free functions
switchif / else if
fmt.Println(x), fmt.Sprintf("%.2f", x)log[x], fmt[x, decimals = 2]
== != <= && || !eq ne lte and or not
x++, x += 1x = x + 1
// comment--- comment
package / import "x"use x
_test.go files, testing.Ttest blocks under the function; crow test
defernot needed — files and connections are values; nothing to close
net/http handlersuse web; route['/api', handler], serve[3000]

From Rust

RustCrow
let x = 5; / let mutx = 5 — no keyword, no mut, no semicolon
fn f(x: i64) -> i64 { }fn f[x] — types inferred at each call site (section 17 of the spec)
f(x)f[x]
Result<T, E>, ?every value is a result already; propagation is automatic; .ok/.reason/.kind where you handle it
Option<T>, None, unwrap()no null; values auto-unwrap on use; a miss is an error value
matchif / else if — no pattern matching, no enums
|x| x * 2 closuresa named fn, passed by name
xs.iter().map(f).collect()for-as-expression
rayon::par_iterplain for — already parallel
String vs &str, &, &mut, lifetimesone string type; no references — collections are immutable-by-rebuild (push returns a new array)
Vec<T>, HashMap<K, V>arrays, map[k = v]
struct + implstruct P [x, y] + free functions; no methods, no traits
enumnone — structs, error kinds, or strings
println!("{x}"), format!log['x'], fmt[x, decimals = 2]
"str", 'c''str' only; characters are 1-length strings
== != <= && || !eq ne lte and or not
x += 1x = x + 1
// comment, /// doc--- comment
use x::y;, moduse x (then x.y); every file is a module
#[test], assert_eq!test blocks with bare boolean truths; assert[cond] in scenarios
cargo run/build/testcrow run/build/test (and crow check)
async/await, tokio::spawnnone — later[f]; the live loop is owned by the runtime

From Elixir and Erlang

ElixirCrow
def f(x), do: … / fn x -> … endfn f[x] + indented body; no anonymous fns
f(x), f.(x)f[x]
x |> f() |> g(a)x then f then g[a]
{:ok, v} / {:error, reason} + caseone value: .ok, .reason, .kind; no tuples, no pattern matching
with, case, condif / else if; failures propagate on their own
Enum.map, Enum.filter, Enum.reducefor-as-expression; named reductions
Enum.any?, Enum.findany[xs, p], find[xs, p]
%{a: 1}map[a = 1] (one value type); records are struct
defstructstruct P [x, y]
nildoesn't exist
IO.puts, "#{x}"log[x], '`x`'
# comment--- comment
Task.async, spawn, GenServerfor is parallel; later[f] for off-loop work; reactive state is the mailbox
Phoenix LiveView render/handle_event/assignview render[], handlers as named fns bound with click = inc, top-level reactive variables (chapter 12) — the same model, without the boilerplate
ExUnittest blocks under the function
immutability everywherethe same — push, set return new values

From Ruby and Crystal

RubyCrow
def f(x) … endfn f[x] + indentation, no end
f(x), f xf[x] — always brackets
xs.each { |x| … }, .map, .selectfor-as-expression (parallel); no blocks or procs
"#{x}"'`x`'
'a' + 'b', <<interpolate, or join
nildoesn't exist
begin/rescue/raiseerrors are values; return error[reason, kind]
unless, x if cif not c; no modifiers
elsifelse if
x ? a : bif/else
class + methods, attr_accessorstruct P [x, y] + free functions
{ a: 1 }, symbolsmap[a = 1]; no symbols — strings
puts xlog[x]
== != <= && || !eq ne lte and or not
x += 1x = x + 1
# comment--- comment
require, require_relativeuse x
RSpec / Minitesttest blocks under the function
Crystal's x : Int32 annotationsnone — inferred

From Swift

SwiftCrow
let x = 5 / varx = 5
func f(_ x: Int) -> Int { }fn f[x] + indented body
f(x), f(x: 1)f[x], f[x = 1] (named args need a default)
"hi \(name)"'hi `name`'
Optional, ?, !, if let, guard letno optionals — a failed value carries .ok; check it once, at the boundary
throws, try, do/catcherrors are values; return error[reason, kind]
{ x in x * 2 } closuresa named fn, passed by name
xs.map { }, .filter, .first(where:)for-as-expression, find[xs, p]
struct with methods, class, enumstruct P [x, y] + free functions; no enums
[1, 2, 3], ["a": 1][1, 2, 3], map[a = 1]
switchif / else if
print(x)log[x]
== != <= && || !eq ne lte and or not
x += 1x = x + 1
// comment--- comment
import Foundationuse math, use frame, …
async/await, Task {}none — later[f]; for is parallel
SwiftUI Viewview blocks — a tree, rendered by the runtime (chapter 12)
XCTesttest blocks under the function

From Zig

ZigCrow
const x = 5; / varx = 5
fn f(x: i64) i64 { }fn f[x] — no types written, inferred per call site
!T error unions, try, catch, orelseevery value is a result; propagation is automatic; .ok/.reason where you handle it
?T optionals, nullnone — error values
switchif / else if
for (xs) |x| { }for x in xs — parallel, and an expression
while (i < n) : (i += 1)while i lt n … i = i + 1
std.debug.print("{d}\n", .{x})log[x]
[_]i64{1, 2, 3}, slices, allocators[1, 2, 3] — no allocation to manage
struct { } with methodsstruct P [x, y] + free functions
defer, comptimeneither exists; nothing to free, nothing to compute at compile time
== != <= and or !eq ne lte and or not
x += 1x = x + 1
// comment--- comment
@import("std")use math, use io, …
test "name" { }test name[] — the same idea, attached to the function it tests
zig build, zig testcrow build, crow test, crow check