← Code Compare

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.

Show: GujiGoOCamlHaskellPerlRakuRustPython
Guji
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.

Go
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.

OCaml
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.

Haskell
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.

Perl
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.

Raku
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.

Rust
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.

Python
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.