"Hello" ++ ", World!" -- "Hello world!"
[1..5] == [1, 2, 3, 4, 5]
['A'..'F'] == "ABCDEF"
[1..]
[1..3] ++ [4..6] -- [1, 2, 3, 4, 5, 6]
[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10]
[(x, y) | x <- [1..3], y <- [1..3], x + y > 4] -- [(2,3), (3,2), (3,3)]
add :: Integer -> Integer -> Integer
add a b = a + b
add 1 3 -- 4
addOne = add 1 -- :: Integer -> Integer
addOne 4 -- 5
main :: IO ()
main = putStrLn "Hello, World!"
factorial :: Integer -> Integer
factorial n = if n < 2
then 1
else n * factorial (n - 1)
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
Products
data Person = Person Name String Int
data Person = Person {
name :: Name,
address :: String,
age :: Int
}
Sum
data Bool = False | True
data Maybe a = Nothing | Just a
data Error err a = Error err | Result a
Algebraic Data Types
data DeliveryStatus = Packing | Shipping Int | Delivered
name | objects | morphism |
---|---|---|
Set | sets | functions |
Grp | groups | group homomorfisms |
VectK | vector spaces over field K | linear transformations |
One configuration source
Config configFromArgs = fromArgs(args);
startApplication(configFromArgs);
Two configuration sources
Config configFromArgs = fromArgs(args);
Config configFromFile = fromFile("config.yml");
Config config = combine(configFromFile, configFromArgs);
startApplication(config);
Multiple configuration sources
Config config = combineAll(List.of(configFromServer,
configFromFile,
configFromSystemEnv,
configFromArgs));
startApplication(config);
public <T> T apply(T a, T b);
\begin{multline}
\shoveleft (G, ⋅ : G × G → G)
\shoveleft (x ⋅ y) ⋅ z = x ⋅ (y ⋅ z) \
\shoveleft e ⋅ x = x ⋅ e = x \
\end{multline}
Java
public interface Monoid<A> {
A apply(A a, A b);
A empty();
}
Haskell
class Monoid a where
(<>) :: a -> a -> a
mempty :: a
Java
class StringMonoid implements Monoid<String> {
public String empty() { return ""; }
public String apply(String a, String b) {
return a + b;
}
}
StringMonoid m = new StringMonoid();
m.apply("Hello", m.apply(" ", "World"));
Haskell
instance Monoid String where
(<>) = (++)
mempty = ""
"Hello" <> " " <> "World"
Java
class IntSumMonoid implements Monoid<Integer> {
public Integer empty() { return 0; }
public Integer apply(Integer a, Integer b) {
return a + b;
}
}
IntSumMonoid m = new IntSumMonoid();
m.apply(10, m.apply(2, 3));
Haskell
instance Num a => Monoid (Sum a) where
Sum a <> Sum b = Sum (a + b)
mempty = Sum 0
Sum 10 <> Sum 2 <> Sum 3
Java
class IntProdMonoid implements Monoid<Integer> {
public Integer empty() { return 1; }
public Integer apply(Integer a, Integer b) {
return a * b;
}
}
IntProdMonoid m = new IntProdMonoid();
m.apply(10, m.apply(2, 3));
Haskell
instance Num a => Monoid (Product a) where
Product a <> Product b = Product (a * b)
mempty = Product 1
Product 10 <> Product 2 <> Product 3
Java
public static <T> T mconcat(Monoid<T> monoid, List<T> list)
Haskell
mconcat :: Monoid a => [a] -> a
Customer customer = findCustomerByName(name);
String city = null;
if (customer != null) {
city = customer.getAddress().getCity();
}
List<Customer> customers = findAllCustomers();
List<String> cities = new ArrayList<String>();
for (Customer customer : customers) {
String city = customer.getAddress().getCity();
cities.add(city);
}
Future<Customer> customer = findCustomerByName(name);
String city = customer.get().getAddress().getCity();
Java
interface Functor<A> {
<B> Functor<B> map(Function<A, B> fn);
}
Haskell
class Functor f where
fmap :: (a -> b) -> f a -> f b
\begin{multline}
\shoveleft f : X → Y ∈ C, g : Y → Z ∈ C
\shoveleft F(\text{id}_x)=\text{id}F(x) \
\shoveleft F(g ˆ f) = F(g) ˆ F(f) \
\end{multline}
Identity Law
functor.map(x -> x) == functor
Composition Law
functor.map(x -> f(g(x))) == functor.map(g).map(f)
class Optional<T> implements Functor<T> {
private final T value;
private Optional(T value) {
this.value = value;
}
@Override
public <R> Optional<R> map(Function<T, R> f) {
if (value == null)
return empty();
else
return of(f.apply(value));
}
public static <T> Optional<T> of(T a) {
return new Optional<T>(a);
}
public static <T> Optional<T> empty() {
return new Optional<T>(null);
}
}
Optional<Customer> customer = findCustomerByName(name);
Optional<String> city = customer
.map(Customer::getAddress)
.map(Address::getCity);
Haskell
Maybe a = Nothing | Just a
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
fmap length (Just "Hello!")
length <$> Just "Hello!"
class FList<T> extends ArrayList<T> implements Functor<T> {
@Override
public <R> FList<R> map(Function<T, R> f) {
FList<R> result = new FList<>();
for (int i = 0; i < size(); i++) {
R newElement = f.apply(get(i));
result.add(newElement);
}
return result;
}
}
FList<Customer> customers = getAllCustomers();
FList<String> cities = customers
.map(Customer::getAddress)
.map(Address::getCity);
Haskell
instance Functor [] where
fmap = map
fmap (* 2) [1, 2, 3, 4]
(* 2) <$> [1, 2, 3, 4]
class Promise<T> implements Functor<T> {
public <R> Promise<R> map(Function<T, R> f) { ... }
}
Promise<Customer> customer = customerServiceApi.getCustomerById(id);
Promise<String> city = customer
.map(Customer::getAddress)
.map(Address::getCity);
public Optional<Manager> findLocalManager(String city) { ... }
public Optional<Customer> findCustomerByName(String name) { ... }
public Optional<Manager> findLocalManager(String city) { ... }
//...
Optional<Customer> customer = findCustomerByName(name);
Optional<Optional<Manager>> manager = customer
.map(Customer::getAddress)
.map(Address::getCity)
.map(city -> findLocalManager(city));
map
public <B> Functor<B> map(Function<A, B> fn);
fmap :: (a -> b) -> f a -> f b
flatMap
public <B> Functor<B> flatMap(Function<A, Functor<B>> fn);
flatMap :: (a -> f b) -> f a -> f b
Java
public interface Monad<T, M extends Monad<?, ?>> extends Functor<T> {
M flatMap(Function<T, M> f);
}
Haskell
class Functor m => Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
public Optional<Manager> findLocalManager(String city) { ... }
//...
Optional<Customer> customer = findCustomerByName(name);
Optional<Manager> manager = customer
.map(Customer::getAddress)
.map(Address::getCity)
.flatMap(this::findLocalManager);
class Person {
private PersonalData personalData;
}
class PersonalData {
private Contact contact;
}
class Contact {
private Address address;
}
class Address {
private String city;
}
String city = null;
if (person.getPersonalData() != null
&& person.getPersonalData().getContact() != null
&& person.getPersonalData().getContact().getAddress() != null) {
city = person.getPersonalData().getContact().getAddress().getCity();
}
class Person {
private Optional<PersonalData> personalData;
}
class PersonalData {
private Optional<Contact> contact;
}
class Contact {
private Optional<Address> address;
}
class Address {
private String city;
}
Optional<String> city = person.getPersonalData()
.flatMap(PersonalData::getContact)
.flatMap(Contact::getAddress)
.map(Address::getCity);
public Promise<Customer> getCustomerByName(String name) { ... }
public Promise<Manager> getLocalManager(Address address) { ... }
public Promise<Meeting> scheduleMeeting(Manager m, Customer c) { ... }
Promise<Meeting> meeting = getCustomerByName(name)
.flatMap(customer ->
getLocalManager(customer.getAddress())
.flatMap(manager ->
scheduleMeeting(manager, customer)));
Scala:
val meeting = getCustomerByName(name)
.flatMap(customer =>
getLocalManager(customer.address)
.flatMap(manager =>
scheduleMeeting(manager, customer))
)
val meeting = for {
customer <- getCustomerByName(name)
manager <- getLocalManager(customer.address)
meeting <- scheduleMeeting(manager, customer)
} yield meeting
Java
public Either<Err, Customer> getCustomerByName(String name) { ... }
public Either<Err, Manager> getLocalManager(Address address) { ... }
public Either<Err, Meeting> scheduleMeeting(Manager m, Customer c) { ... }
Either<Err, Meeting> meeting = getCustomerByName(name)
.flatMap(customer ->
getLocalManager(customer.getAddress())
.flatMap(manager ->
scheduleMeeting(manager, customer)));
Scala
val meeting = for {
customer <- getCustomerByName(name)
manager <- getLocalManager(customer.address)
meeting <- scheduleMeeting(manager, customer)
} yield meeting
Simplicity and elegance are unpopular because they require hard work and discipline to achieve and education to be appreciated.
And to make matters worse: complexity sells better.
— Edsger Dijkstra