Recursion
A function that calls itself, with a base case that stops the descent and a recursive case that shrinks the problem toward it. The task is the canonical one - factorial, where 0! = 1 is the base case and n! = n * (n-1)! is the recursion. Watch how each language spells the base case: Guji, OCaml, Rust, and Python lean on match/if, Raku splits it across two multi definitions by signature, and Perl and Go fall back on a guard return. The shapes differ; the divide-and-stop idea is the same everywhere.
sub factorial($n: Int): Int {
match $n {
0 { 1 }
_ { $n * factorial($n - 1) }
}
}
sub main() {
for $n in [0, 1, 5, 10] {
print("$n! = { factorial($n) }")
}
}Guji has no special looping construct for recursion - a sub simply names itself, so factorial calls factorial. The two cases read as a match: the literal pattern 0 is the base case, and the wildcard _ is the recursive step that multiplies $n by factorial($n - 1). Because match is an expression (§6.2), each arm just yields its value with no return, and bindings are immutable, so the recursion threads a fresh $n through each call rather than mutating a counter.
package main
import "fmt"
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n-1)
}
func main() {
for _, n := range []int{0, 1, 5, 10} {
fmt.Printf("%d! = %d\n", n, factorial(n))
}
}Go expresses recursion with a plain function that calls itself and an early return for the base case - there is no pattern matching, so the if n <= 1 guard handles both 0 and 1. Go does not perform tail-call optimization, so deep recursion grows the goroutine stack (which does grow dynamically, but is not unbounded); for large inputs the idiomatic Go is usually an explicit loop instead. fmt.Printf with %d formats the integers.
let rec factorial n =
if n <= 1 then 1
else n * factorial (n - 1)
let () =
List.iter
(fun n -> Printf.printf "%d! = %d\n" n (factorial n))
[ 0; 1; 5; 10 ]OCaml requires the rec keyword to make a let binding visible inside its own body - an ordinary let cannot see itself, so let rec factorial is what permits the self-call. Recursion is the primary iteration tool in OCaml: there is no for-with-mutation idiom for this, you express the loop as the function calling itself. Integer multiply is plain * (the .-suffixed *. is only for floats), and %d prints each int.
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
main :: IO ()
main = mapM_ (\n -> putStrLn (show n ++ "! = " ++ show (factorial n)))
[0, 1, 5, 10]Haskell writes the base and recursive cases as two equations matched top-to-bottom: factorial 0 = 1 is the base case and factorial n = ... is the recursive step, so no if or return is needed. The type is Integer, which is arbitrary precision, so factorials never overflow no matter how large n grows. Recursion is the primary iteration tool in a pure language; mapM_ walks the input list, sequencing the putStrLn effects.
use strict;
use warnings;
sub factorial {
my ($n) = @_;
return 1 if $n <= 1;
return $n * factorial($n - 1);
}
print "$_! = ", factorial($_), "\n" for (0, 1, 5, 10);A Perl sub receives its arguments in @_, so my ($n) = @_ names the parameter. The base case is a statement modifier - return 1 if $n <= 1 - Perl's compact way to guard a return, after which the recursive case falls through. The final line uses a postfix for to walk the list, with $_ as the implicit loop variable interpolated straight into the string.
multi factorial(0) { 1 }
multi factorial(Int $n) { $n * factorial($n - 1) }
say "$_! = { factorial($_) }" for 0, 1, 5, 10;Raku splits the recursion across two multi subs and lets multiple dispatch pick the right one by signature. The base case factorial(0) matches the literal 0, while factorial(Int $n) catches every other integer and recurses - the dispatcher tries the most specific candidate first, so 0 never reaches the recursive variant. This turns the base/recursive split into a declaration-level distinction rather than an if inside one body. say prints with a trailing newline.
fn factorial(n: u64) -> u64 {
match n {
0 => 1,
_ => n * factorial(n - 1),
}
}
fn main() {
for n in [0, 1, 5, 10] {
println!("{}! = {}", n, factorial(n));
}
}Rust's match makes the base case explicit: 0 => 1 and the wildcard _ for the recursive step, mirroring Guji's shape. The u64 type is chosen because factorials overflow fast - in debug builds an overflowing multiply panics rather than wrapping silently, which surfaces the bug instead of returning garbage. Rust does not guarantee tail-call elimination, so very deep recursion can overflow the stack; here the inputs stay small.
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
for n in (0, 1, 5, 10):
print(f"{n}! = {factorial(n)}")Python expresses recursion with a self-calling def and a guard return for the base case - n <= 1 folds the 0 and 1 cases together. Python integers are arbitrary precision, so factorial never overflows no matter how large n grows, unlike the fixed-width Go and Rust versions. Note the default recursion limit (around 1000 frames) caps depth, since CPython does not optimize tail calls; an f-string interpolates each result.