I recently procured a copy of Graham Hutton’s most excellent book, “Programming in Haskell” (2nd Edition). I had earlier worked through the first edition, and that is the book that really opened my eyes to the power of Haskell! Having become a bit rusty with my Haskell, I decided to work through the second edition book (which is considerably larger and more comprehensive). As part of that exercise, I decided to use a lazy weekend afternoon to code up Euclid’s GCD Algorithm in Haskell, Rust and in D. Just for a quick visual comparison of how the languages look. Here’s how they look:
First off, the Haskell version, which is the best looking one in my opinion:
Macushla:Playground z0ltan$ cat Euclid.hs module Main where euclid :: Int -> Int -> Int euclid m n | m <= 0 && n <= 0 = error "GCD works for positive numbers only" | m == n = m | m < n = euclid m (n-m) | otherwise = euclid (m-n) n main :: IO () main = do putStrLn "Enter the first number: " x <- getLine putStrLn "Enter the second number: " y <- getLine let x' = read x :: Int let y' = read y :: Int putStrLn $ "The GCD of " ++ x ++ " and " ++ y ++ " is " ++ show (euclid x' y') Macushla:Playground z0ltan$ ghc Euclid.hs [1 of 1] Compiling Main ( Euclid.hs, Euclid.o ) Linking Euclid ... Macushla:Playground z0ltan$ ./Euclid Enter the first number: 12 Enter the second number: 18 The GCD of 12 and 18 is 6
Here’s the Rust version (a bit uglier, but the Pattern Matching is quite nice):
Macushla:Playground z0ltan$ cat euclid.rs use std::io; use std::str::FromStr; use std::cmp::Ordering; fn get_number(prompt: &str) -> u32 { println!("{}", prompt); let mut input = String::new(); io::stdin().read_line(&mut input) .expect("no input!"); u32::from_str(input.trim()).unwrap() } fn main() { let x = get_number("Enter the first number: "); let y = get_number("Enter the second number: "); println!("The GCD of {} and {} is {}", x, y, euclid(x, y)); } fn euclid(m: u32, n: u32) -> u32 { assert!(m > 0 && n > 0); match m.cmp(&n) { Ordering::Equal => m, Ordering::Less => euclid(m, n-m), Ordering::Greater => euclid(m-n, n), } } Macushla:Playground z0ltan$ rustc euclid.rs && ./euclid Enter the first number: 12 Enter the second number: 18 The GCD of 12 and 18 is 6
And finally, here is the D version – clean, succinct, and a pleasure to read as always:
Macushla:Playground z0ltan$ cat euclid.d import std.stdio: readln, writeln, writefln; uint get_number(string prompt) { writeln(prompt); import std.conv: to; import std.string: chomp; return readln().chomp().to!(uint); } void main() { uint x = get_number("Enter the first number: "); uint y = get_number("Enter the second number: "); writefln("The GCD of %s and %s is %s", x, y, euclid(x, y)); } uint euclid(uint m, uint n) { assert(m > 0 && n > 0); if (m < n) { return euclid(m, n-m); } else if (m == n) { return m; } else { return euclid(m-n, n); } } Macushla:Playground z0ltan$ dmd -run euclid.d Enter the first number: 12 Enter the second number: 18 The GCD of 12 and 18 is 6
One thing is for sure. Aside from syntactic differences, most modern languages are all converging in terms of paradigms and features. Hell, even keeping languages like Haskell and Idris aside, most mainstream languages are also converging on the syntactical front! Anyway, this was a fun little exercise on a slow afternoon. I personally like the Haskell version best , followed by the D version, and then finally the Rust version (ruined by the somewhat ugly I/O syntax). What do you think?