Code Compare

The same task, seven ways

Pick a topic to see it written in guji, Go, OCaml, Perl, Raku, Rust and Python — side by side, with a note on what's idiomatic in each.

01

Hello, World

Every language's smallest complete program: name an entry point and print one line to standard output. Watch how much ceremony each one demands — from Perl's and Python's bare one-liners, to Go's explicit package/func main, to guji's typed sub main(): Int whose Int return doubles as the process exit code. Notice too how the string reaches the screen: a statement (print), a function (println!), a method, or a top-level expression.

02

Variables, Binding & Basic Types

The same tiny program in seven languages: bind four immutable values of the four basic scalar types (an Int, a Float, a Str, and a Bool), then use one mutable accumulator to sum 1..count and print a summary. Watch two axes vary: how mutability is opted into (immutable-by-default with mut/let/ref, versus mutable-by-default), and how a value's type is signalled — a leading sigil that is part of the name ($, @, % in guji, Perl, and Raku) versus a bare name whose type lives only in a declaration or annotation (Go, OCaml, Rust, Python).

03

Functions & Closures

The same small task in every language: define a named function square, write a closure factory make_adder(n) that captures n and returns a new function, then map a list through both — squaring each element, then adding 5. Watch for how a closure captures n (does it need an explicit return? a named lambda keyword? an arrow?), how anonymous functions read ({ ... } topic blocks vs. lambda/fun/|x|/sub {}), and how functions compose over a list (map with a lambda vs. a comprehension).

04

Collections: map, filter, reduce

The same pipeline in all seven languages: take a list of integers, keep the even ones, double each, then sum the result (answer: 24). Watch where each language puts the transformation — chained methods on the data (guji, Raku, Rust), free functions wrapping the data (Python, OCaml), or right-to-left composition you read inside-out (Perl). Notice too whether reduce needs an explicit seed and whether the intermediate steps allocate new lists or stream lazily.

05

Sum Types & Pattern Matching

Model a value that is one of several shapes and take it apart by structure, not by a chain of type tests. The task is the canonical one: a Shape is either a Circle (radius) or a Rect (width, height), and area matches on the variant to pull out its fields. Watch which languages have real sum types with exhaustiveness checking (guji, OCaml, Rust), which bolt structural match onto classes (Python 3.10+), and which have no native sum type at all and must fake it — Go with an interface, Perl with a tagged array and an if/elsif ladder.

06

Regex & text processing: split, match, replace

One small task in all seven languages: take the string "alice=30, bob=25, carol=35", split it into key=value entries, match each with a named-capture regex to print "alice is 30; bob is 25; carol is 35", then replace every name=age with age:name to get "30:alice, 25:bob, 35:carol". Watch where the regex lives — a first-class literal woven into the language (guji, Perl, Raku), a compiled object from a library (Rust, Python, OCaml), or the standard library's regexp package (Go). Notice especially how each names its captures ((?<name>…) vs (?P<name>…)) and how the replacement template refers back to them.

07

Errors: Result, Option & Exceptions

The same fallible task in all seven languages: parse a string to an integer and compute 100 / n, where two distinct things can go wrong — the string isn't a number, and the number is zero. Watch the fundamental split: do errors travel in the type (a Result/Either value you must destructure, with ?-style early-return) or out-of-band as exceptions you try/catch? Notice how guji, Rust, and OCaml make every failure visible in the signature, while Python and Perl reach for thrown exceptions, and Go threads an explicit err value through each step.

08

Concurrency: threads, goroutines & channels

The same job in all seven languages: fan out five concurrent workers, each computing the square of a number, send each result over a channel (or queue), and have the main task collect and sum them (answer: 55). Watch the two big axes: how a task is spawned (hatch/go/thread/async/Thread) and how results travel back — guji and Go pass them through a typed channel with no shared mutable state, Rust moves ownership across a mpsc channel, while Perl and Python hand them through a thread-safe queue. Notice who has to close the channel and who joins the workers before reading the total.

09

Reading Input: lines, files & basic I/O

The same little cat -n: read input line by line and print each line prefixed with its 1-based line number. Watch how each language gets a stream of lines and how it counts: a buffered scanner with a manual counter (Go), a buffered reader whose .lines() iterator chains straight into enumerate (Rust, Python), the implicit line-number variable $. (Perl), built-in .lines with .kv (Raku), or a recursive read-until-End_of_file loop (OCaml). guji v0 is the outlier: its only I/O primitive is print, so input is modelled as an in-program value split with .lines().

09

Types & Records

The same record in all seven languages: define a Point type with integer x and y fields, construct one, then translate it by (3, 4) to get a second point. Watch how a record type is declared (a class/struct/record/blessed hash) and whether updates mutate or copy: the functional-first languages here return a brand-new Point rather than changing the original. Notice too how much boilerplate each needs — a derived constructor, explicit field accessors, or just an annotated field list.