← Code Compare

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.

Show: gujiGoOCamlPerlRakuRustPython
guji
sub main(): Int {
    @nums = [1, 2, 3, 4, 5, 6]
    $total = @nums
        .filter({ $_ % 2 == 0 })
        .map({ $_ * 2 })
        .reduce(0, sub($acc, $x) { $acc + $x })
    print("evens doubled, summed: $total")
    0
}

Every prelude function is data-first (§7.2), so filter/map/reduce chain left-to-right with . — there is no separate pipeline operator. The single-argument steps use a topic block { ... } whose implicit parameter is $_; reduce takes a seed plus a two-parameter lambda written as an anonymous sub. This whole chain could collapse to .filter(...).map(...).sum(), but spelling out reduce shows the fold explicitly.

Go
package main

import "fmt"

func main() {
	nums := []int{1, 2, 3, 4, 5, 6}
	total := 0
	for _, x := range nums {
		if x%2 == 0 {
			total += x * 2
		}
	}
	fmt.Printf("evens doubled, summed: %d\n", total)
}

Go's standard idiom is an explicit for ... range loop with the filter, map, and accumulate fused into one pass — there is no built-in map/filter/reduce over slices. Generic helpers exist (slices in 1.21+, and iter.Seq range-over-func in 1.23+), but for a concrete pipeline like this most Go code just writes the loop. It is the most verbose example here and also the one with zero intermediate allocations.

OCaml
let () =
  let nums = [1; 2; 3; 4; 5; 6] in
  let total =
    nums
    |> List.filter (fun x -> x mod 2 = 0)
    |> List.map (fun x -> x * 2)
    |> List.fold_left ( + ) 0
  in
  Printf.printf "evens doubled, summed: %d\n" total

OCaml's List module supplies filter, map, and fold_left as ordinary functions; the |> pipe threads the list through them left-to-right, reading top-to-bottom like the method chains. fold_left ( + ) 0 is reduce with the (+) operator passed as a plain function value. Each stage builds a fresh list, so for hot paths one would reach for List.fold_left alone or the lazy Seq module.

Perl
use strict;
use warnings;
use List::Util qw(reduce);

my @nums = (1, 2, 3, 4, 5, 6);
my $total = reduce { $a + $b } 0,
            map  { $_ * 2 }
            grep { $_ % 2 == 0 } @nums;
print "evens doubled, summed: $total\n";

Perl spells the pipeline right-to-left, so you read it inside-out: grep (filter) runs first, then map, then reduce from List::Util. The block forms use the implicit $_ topic, while reduce exposes the running pair as the package globals $a and $b. Passing 0 as the first list element seeds the fold so an empty input still yields 0.

Raku
my @nums = 1, 2, 3, 4, 5, 6;
my $total = @nums
    .grep(* %% 2)
    .map(* * 2)
    .reduce(&[+]) // 0;
say "evens doubled, summed: $total";

Raku chains methods on the list like guji: grep filters, map transforms, reduce folds. The * is the Whatever star building a one-argument closure, and %% is the built-in divisible-by operator, so * %% 2 reads as “divisible by 2.” &[+] passes the + operator as a function to reduce; the // 0 guards the empty case, since reduce on an empty list returns Nil.

Rust
fn main() {
    let nums = [1, 2, 3, 4, 5, 6];
    let total: i32 = nums
        .iter()
        .filter(|&&x| x % 2 == 0)
        .map(|&x| x * 2)
        .sum();
    println!("evens doubled, summed: {total}");
}

Rust builds a lazy iterator chain: filter and map are adapters that do nothing until a consumer drives them, and sum() (a specialized reduce/fold) is what actually runs the pipeline in a single pass with no intermediate Vec. The &&x and &x patterns dereference the borrowed i32s that .iter() yields. Writing .fold(0, |acc, x| acc + x) instead of .sum() would make the reduce explicit.

Python
from functools import reduce

nums = [1, 2, 3, 4, 5, 6]
evens = filter(lambda x: x % 2 == 0, nums)
doubled = map(lambda x: x * 2, evens)
total = reduce(lambda acc, x: acc + x, doubled, 0)
print(f"evens doubled, summed: {total}")

Python keeps map and filter as built-in functions that wrap the data and return lazy iterators, while reduce lives in functools. Each takes the function first and the iterable second — the opposite argument order from the data-first chains above. Most Pythonistas would actually write this as a comprehension, sum(x * 2 for x in nums if x % 2 == 0), which is shorter and the more idiomatic spelling.