Haskell is a language that is foreign to most programmers coming from an imperitive language like Java, C#, C++, Python and the like. You find new concepts that make learning Haskell feel like learning your first imperitive language. The first step is to acknowledge this, so you don't get frustrated right away when you aren't productive immediately. This acknowledgment allowed me to pass through many frustrating moments trying to do something useful. I realized, I wasn't trying to write a web service, I was trying to learn haskell by way of writing a web service.
If you're a productive Java or C# developer and learning Haskell, you might at times find yourself a little hazy on some of the concepts and terminology. This document is meant to clear up confusion by using terminology you are familiar with to describe Haskell's terminology and syntax.
| Feature | Java | Haskell |
|---|---|---|
| Functions |
static <A, B> String f(A a, B b) {
return "hi";
}
|
f :: a -> b -> String
f a b = "hi"
|
| Structs |
class MyType {
String a;
String b;
}
|
data MyType = MyType {
a :: String,
b :: String
}
λ> result = MyType "hi" "world"
λ> a
a :: MyType -> String
λ> a result
"hi"
λ> b result
"world"
|
| Typedef |
interface PhoneBook extends Iterable<Pair<String,String>>
|
type PhoneBook = [(String, String)]
|
| Interface / Type Class |
interface Add2 extends Add1 {
int add2(int number1, int number2);
}
class Calculator implements Add2 {
public int add2(int n1, int n2) {
return n1 + n2;
}
}
|
class (Add1 a) => Add2 a where
add2 :: a -> Integer -> Integer -> Integer
-- Instance implementation
instance Add2 Calculator where
add2 calc n1 n2 = n1 + n2
|
| Null Handling |
String getName() {
return null; // or actual value
}
String name = getName();
if (name != null) {
System.out.println(name);
}
|
getName :: Maybe String
getName = Nothing -- or Just "value"
-- Pattern matching
case getName of
Nothing -> putStrLn "No name"
Just name -> putStrLn name
-- Or using maybe function
maybe (putStrLn "No name") putStrLn getName
|
| Lists/Arrays |
List<Integer> numbers =
Arrays.asList(1, 2, 3, 4, 5);
// Map
List<Integer> doubled = numbers.stream()
.map(x -> x * 2)
.collect(Collectors.toList());
// Filter
List<Integer> evens = numbers.stream()
.filter(x -> x % 2 == 0)
.collect(Collectors.toList());
|
numbers :: [Integer]
numbers = [1, 2, 3, 4, 5]
-- Map
doubled = map (*2) numbers
-- Filter
evens = filter even numbers
-- List comprehension
evens = [x | x <- numbers, even x]
|
| Loops vs Recursion |
int sum(List<Integer> numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
return total;
}
|
-- Recursion
sum :: [Integer] -> Integer
sum [] = 0
sum (x:xs) = x + sum xs
-- Or using fold
sum = foldl (+) 0
-- Or using built-in
sum numbers
|
| Enums vs ADTs |
enum Color {
RED, GREEN, BLUE
}
enum Result {
SUCCESS,
ERROR
}
|
data Color = Red | Green | Blue
deriving (Show, Eq)
data Result a = Success a | Error String
deriving (Show, Eq)
-- Usage
myResult :: Result Int
myResult = Success 42
|
| Exception Handling |
try {
int result = divide(10, 0);
System.out.println(result);
} catch (ArithmeticException e) {
System.out.println("Error: " + e.getMessage());
}
|
divide :: Int -> Int -> Either String Int
divide _ 0 = Left "Division by zero"
divide x y = Right (x `div` y)
-- Using the result
case divide 10 0 of
Left err -> putStrLn $ "Error: " ++ err
Right val -> print val
|
| Lambda / Anonymous Functions |
List<Integer> numbers = Arrays.asList(1,2,3);
numbers.stream()
.map(x -> x * 2)
.forEach(x -> System.out.println(x));
// BiFunction
BiFunction<Integer, Integer, Integer> add =
(a, b) -> a + b;
|
numbers = [1, 2, 3]
-- Lambda syntax
map (\x -> x * 2) numbers
-- Partial application (more idiomatic)
map (*2) numbers
-- Lambda with multiple args
add = \a b -> a + b
-- Or more idiomatically
add a b = a + b
|
| Function Composition |
// Method chaining
String result = getString()
.trim()
.toLowerCase()
.replace(" ", "_");
// Composed function
Function<String, String> process =
s -> s.trim().toLowerCase();
|
-- Function composition (.)
-- Reads right to left
process = replace " " "_" . toLower . trim
-- Or using application operator ($)
result = replace " " "_" $ toLower $ trim getString
-- Or using reverse application (&)
result = getString & trim & toLower & replace " " "_"
|
| Conditionals |
int abs(int x) {
if (x < 0) {
return -x;
} else {
return x;
}
}
// Switch
String describe(int x) {
switch(x) {
case 0: return "zero";
case 1: return "one";
default: return "other";
}
}
|
-- if-then-else (expression, not statement)
abs x = if x < 0 then -x else x
-- Guards (more idiomatic)
abs x
| x < 0 = -x
| otherwise = x
-- Pattern matching
describe :: Int -> String
describe 0 = "zero"
describe 1 = "one"
describe _ = "other"
|
| Generics / Type Parameters |
class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
Box<String> box = new Box<>("hello");
|
data Box a = Box a
deriving (Show, Eq)
getValue :: Box a -> a
getValue (Box v) = v
-- Usage
box :: Box String
box = Box "hello"
λ> getValue box
"hello"
|
| Collections (Map) |
import java.util.HashMap;
import java.util.Map;
Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 30);
ages.put("Bob", 25);
Integer age = ages.get("Alice");
|
import qualified Data.Map as Map
ages :: Map.Map String Integer
ages = Map.fromList [("Alice", 30), ("Bob", 25)]
-- Lookup
age = Map.lookup "Alice" ages -- Maybe Integer
-- Insert
ages' = Map.insert "Charlie" 35 ages
|
| Pattern Matching vs Getters |
class Person {
private String name;
private int age;
public String getName() { return name; }
public int getAge() { return age; }
}
Person p = new Person("Alice", 30);
System.out.println(p.getName());
|
data Person = Person {
name :: String,
age :: Int
} deriving Show
-- Automatic accessor functions
p = Person "Alice" 30
λ> name p
"Alice"
-- Pattern matching
greet (Person n a) =
"Hello " ++ n ++ ", age " ++ show a
|
| Static Methods vs Module Functions |
class MathUtils {
public static int square(int x) {
return x * x;
}
}
int result = MathUtils.square(5);
|
-- In module MathUtils
module MathUtils (square) where
square :: Int -> Int
square x = x * x
-- Usage
import MathUtils
result = square 5
|
| String Operations |
String s = "Hello World";
String upper = s.toUpperCase();
String[] words = s.split(" ");
String joined = String.join(",", words);
boolean contains = s.contains("World");
|
import Data.Char (toUpper)
import Data.List (isInfixOf)
s = "Hello World"
upper = map toUpper s
words = split ' ' s -- using Data.List.Split
joined = intercalate "," words
contains = "World" `isInfixOf` s
|
Haskell documentation is found on hackage. After some time you'll be able to read the definitions. Sometimes the author has documented their package well with examples. Until you're more familiar, you'll be better off learning the fundamentals, so go learn you a haskell.