Java & Haskell
Similarities and Differences

Devoxx Poland 2021

Dervis Mansuroglu - @dervismn

Principal Officer & Developer

Norwegian JUG javaBin, Oslo Organizer

Who am I and why listen to me?

Oslo Software Architecture Co-Organizer

Java Champion

The Norwegian Labour and Welfare Administration

This talk is not about:

Functors & Monads

Higher Kinded Types

Type Class Theory

Category Theory

A Brief Summary

Key takeaways

  • Imparative / object oriented
  • Compiles to bytecode
  • Statically typed
  • Strict (applicative order)
  • Mutable
  • Garbage collected
    • Garbage-First GC (G1)
  • JIT compilation
  • AOT possible with GraalVM
  • Notable people:
    James Gosling, Brian Goetz, Mark Reinhold, + many more
  • Declarative / purely functional
  • Compiles to native binary
  • Strongly typed
  • Lazy (normal order)
  • Immutable
  • Garbage collected
    • Parallel Gen. Copying GC
  • Language can be extended
  • AOT compilation
  • Notable people:
    John Hughes & Lennart Augustsson, Philip Wadler, Simon Peyton Jones

Java is about clear,  readable and reliable code.

Writing as less code as possible is not a goal itself for the Java programming language.

Make it easier to build and maintain reliable code ... that's why people continue to use and trust Java. 

- Brian Goezt

Java adopts concepts from functional languages.

JEP-359 Records

JEP-360 Sealed Types

JEP-169 Value Types

JEP-305 Pattern Matching, instanceof

JEP-325 & 354 Switch Expressions
JEP-405: Record & Array Patterns

Deconstructional pattern matching like in 

Strongly typed language with powerful type inference. 

As other languages move towards functional style, Haskell get better tooling, libraries, stable releases, a growing community and increased industry adoption.

Extendible language that guarantees laziness and immutability.

Haskell is constructed by expressions, not statements. Side-effects and mutation is hard, but is still possible. Haskell has tens of extensions called pragmas, that can be enabled.

Compilation and Runtime Systems

Java src code

bytecode

machine code

JIT Compiler

Java Compiler

No optimization

JIT optimization

Haskell src code

Core code

machine code

STG code

HS Compiler

C - - code

C code

LLVM code

www.aosabook.org/en/ghc.html

Low-level imperative language.

Optimisation

Parsing

Code-generation

The JVM - Java Runtime System

Classloader

Java Class file

(Java Bytecode)

Execution

Engine

Native Method

Interface / Libraries

Heap

Stacks

Registers

Methods

RTS - Haskell Runtime System

STG-Program Code (machine code)

STG-machine

Executable binary file

Registers

Heap

Stacks

GHC Libraries

Haskell

Execution Context

(HEC)

Heap

Registers

Java

Haskell

*.java

JVM

*.hs

GHC

RTS

Important differences

Immutability

Haskell guarantees purity.

("dirty" code is handled in IO-monads)

We need impure code to be able to run our programs, but:

Separate pure and impure code as much as you can!

main = do
  putStrLn "Write a line: "
  line <- getLine
  putStrLn $ "Input was: " ++ line
-- impure
main = do
  putStrLn "Write a line: "
  line <- getLine
  putStrLn $ (append line)

-- pure
append :: String -> String
append input = "Input was: " ++ input

append is referentially transparent.

Sequencing (imperative style)

Lazy evaluation

Haskell guarantees laziness.

(aka delayed evaluation)

repl.it/@dervism/JavaLazyEval

Basic Types

Java

double, long, int, float, char, short, byte, boolean, String

Haskell

Int, Integer, Double, Float, Bool, Bits, Char, String

Primitives:
- Int#, Double#, Float#, Word#, Char#

Signed integrals:

- Int8, Int16, Int32, Int64

Haskell optimizes your code to use primitives (when possible).

 

Don't use Haskell's primitive types:

Int# causes eager evaluation!
 

Bits: Import Data.Bits, GHC.Prim + enable MagicHash extension.

Java

int[] ints = new ints[6];

Haskell

ints = array (0, 5) [(i, 0) | i <- [0..5]]

ints[5] = 10;

ints // [(5,10)]

int val = ints[5];

val = (ints ! 5)

new ints[] {1, 2, 3};

array (0,2) [(0,1), (1,2), (2,3)]

new ints[] {1, 2, 3};

listArray (0,2) [i+1 | i <- [0..2]]

listArray ('a','h') [i | i <- [0..8]]

Map<Character, Integer>

Haskell arrays are lazy and immutable.

 

Haskell provides mutable arrays

 

Haskell arrays are extremely flexible with enums.

 

Import Data.Array

https://replit.com/@dervism/Arrays

Aka ADT's

Algebraic Data Types

Sum type

This  OR  That.

Product type

This  AND  That

https://en.wikipedia.org/wiki/Tagged_union

Java Records, Haskell ADTs (one constructor), Java classes, C structs, tuples

https://en.wikipedia.org/wiki/Product_type

Java enums(*), Haskell ADTs (several data constructors)

Also called: variant, choice type, discriminated union, disjoint union, coproduct

* all values must have the same constructor

Sum type

This  OR  That.

Product type

This  AND  That

Records

Sealed classes & interfaces

Exhaustive Pattern Matching

Destructuring / Decomposition

Erik Dreyer

Haskell

data Animal = Dog | Cat | Elk
data Animal =  Dog String 
             | Cat String
             | Elk String

name (Dog n) = n
name (Cat n) = n
name (Elk n) = n

repl.it/@dervism/enumeration

Java

Function<Animal, String> name = a -> a.name;

enum Animal {
   DOG("Max"), CAT("Nala"), ELK("Sam");
   String name;
   Animal(String name) { 
     this.name = name
   }
}
enum Animal { DOG, CAT, ELK }

replit.com/@dervism/JavaEnum

Haskell

data Animal = Dog String
            | Cat String Int
            | Elk Int String Double 
            | Unknown
            
info (Dog n) = "Dog: " ++ n
info (Cat n _) = "Cat: " ++ n
info (Elk _ n _) = "Elk: " ++ n
info _ = "Unknown animal"

repl.it/@dervism/enumeration

Records

Immutable data classes with named accessors

Project Amber

Java -

enum Group { Mammal, Bird, Fish }

record Animal(String name, int age, Group group) {}

var elephant = new Animal("Elephant", 10, Mammal);

String name = elephant.name();
int age = elephant.age();
Type group = elephant.group();

Records

github.com/dervism/java14-demo

Haskell -

data Group = Mammal | Bird | Fish deriving (Show)

data Animal = Animal {
   name :: String,
   age :: Int,
   group :: Group
}

elephant = (Animal "Elephant" 10 Mammal)
elephantName = name a
elephantAge = age a
elephantGroup = group a

Records

repl.it/@dervism/enumeration

Haskell -

data Person = Person {
   name :: String,
   adress :: String
}

data Country = Country { 
   name :: String 
}

toStr a = join "," [name a, adress a]

Records

main.hs: error:
    Multiple declarations of `name'
    Declared at: main.hs:2:4
                 main.hs:7:4
{-# LANGUAGE DuplicateRecordFields #-}
data Person = Person { 
    name :: String, adress :: String 
}

data Country = Country { 
    name :: String 
}

toStr a = join "," [name (a::Person), adress a]

replit.com/@dervism/duplicate-names

Sealed Types

A step closer to ADT's

Project Amber

Haskell

data Spec = Spec {
  engineType :: Engine,
  maxSpeed :: Speed,
  featureList :: [Feature]
}

data Automobile = 
  Car Spec | Bus Spec | Bike Spec

class Transport a where
  spec :: a -> Spec

instance Transport Automobile where
  spec (Car s) = s
  spec (Bus s) = s
  spec (Bike s) = s

Java

record Spec(
  Engine engineType,
  Speed maxSpeed,
  List<Feature> featureList
) {}

interface Transport {
  Spec spec();
}

sealed interface Automobile extends Transport
    permits Car, Bus, Bike {}

final class Bus implement Automobile {
  public Bus(Spec spec) {
   this.spec = spec;
  }

  public Spec spec() {
   return spec;
  }
}

Java

record Spec(
  Engine engineType,
  Speed maxSpeed,
  List<Feature> featureList
) {}

interface Transport {
  Spec spec();
}

sealed interface Automobile extends Transport
    permits Car, Bus, Bike, Any {}

non-sealed class Any implement Automobile {
  ...
}

// non-sealed:
// support specific extension points 
// while restricting others
// https://stackoverflow.com/a/63887136

Inline classes

codes like a class, acts like an int

Project Valhalla

Java

Haskell

The Haskell compiler inlines referentially transparent functions, and "unboxes" ADTs where possible.

inline class Point {
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int x() { return x; }
    public int y() { return y; }
}

openjdk.java.net/jeps/169

cr.openjdk.java.net/~briangoetz/valhalla/sov/02-object-model.html

Java

inline class Point {
    int x;
    int y;
}
class Line {
    Point x;
    Point y;
}
class Line {
    int x1;
    int y1;

    int x2;
    int y2;
}

More about Valhalla

Toby Ajila - Valhalla experts group 

Modules & Interfaces

Java

Spaces and tabs are ignored.

Haskell

Indentation matters.

Braces and semicolons are required.

Braces and semicolons are optional.

Java

Haskell

public class MyClass {
  public int add(int x, int y){
     return x + y;
  }

  public String hello(){
     return "Hello, world!";
  }
}
module MyClass where

add :: Int -> Int -> Int
add x y = x + y

hello :: String
hello = "Hello, world!"
module MyClass where

add x y = x + y

hello = "Hello, world!"

Java

Haskell

import java.lang.*;

import java.util.Map;

import static 
   java.util.Arrays.sort;

public class MyClass {
  ...
}
import Prelude

import Data.Map
import qualified Data.Map as M

import Data.List (filter, sort)
import Data.List hiding (nub)

module MyClass where

Haskell

module com.testmodule {
 requires module.name;
 exports mypackage.name;
}
module ChessGame (
  Result(..)
  easy,
  difficult,
  run) where

run level = play level
easy = (Chess Normal)
difficult = (Chess Strong)
data Result = Won | Lost | Draw
-----------------------------------
data Ai = Normal | Strong
data Chess = Chess Ai
class Game a where
  play :: a -> Result
instance Game Chess where
  play chessAi = ....

Visibility and encapsulation via: private, public, protected, default, static

Java

Java

Haskell

public interface Person<A> {
  String name(A a);
}
class Person a where
  name :: a -> String

Haskell

class Person a where
   name :: a -> String

data Teacher = Teacher { tName :: String, tAge :: Int }
data Student = Student { sName :: String, sAge :: Int }

instance Person Teacher where
  name teacher = tName teacher
  age teacher = tAge teacher

instance Person Student where
  name student = sName student
  age student = sAge student

personAge :: Person p => p -> Int
personAge p = age p

main = do
    let 
      teacher  = (Teacher "Jane" 30)
      student = (Student "Peter" 30)
      teacherName = name teacher
      studentName = name student

  putStrLn $ "Age of " ++ teacherName ++ " " ++ (show $ personAge teacher)
  putStrLn $ "Age of " ++ studentName ++ " " ++ (show $ personAge teacher)

repl.it/@dervism/typeclasses

Java

repl.it/@dervism/JavaTypeclasses

interface Person {
  String name();
}

class Student implements Person {
  String sName; int sAge;

  public Student(String sName, int sAge) {
    this.sName = sName;
    this.sAge = sAge;
  }

  @Override
  public String name() {
    return "Hello to " + this.sName;
  }
}

class Teacher implements Person {
  String tName; int tAge;

  public Teacher(String tName, int tAge) {
    this.tName = tName;
    this.tAge = tAge;
  }
  
  @Override
  public String name() {
    return "Hello to " + this.sName;
  }
}

class Main {
  public static void main(String[] args) {
    Teacher t = new Teacher("Jane", 30);
    System.out.println(t.name());

    Student s = new Student("Peter", 30);
    System.out.println(s.name());
  }
}

Control Structures

data Person = Person {
  salary :: Int,
  taxClass :: TaxClass
}

Java

Haskell

if (salary > 900)
 tax = salary * 0.44;
else
 tax = salary * 0.36;
if salary > 900 
then salary * 0.44
else salary * 0.36
if (salary > 900)
  return salary * 0.44;
else if (salary > 500)
  return salary * 0.36;
else 
  return salary * 0.24;
if salary > 900 
then salary * 0.44
else if salary > 500
     then salary * 0.36
     else salary * 0.24

If-Then-Else

Haskell Guards (1)

if salary > 900 
then salary * 0.44
else if salary > 500
     then salary * 0.36
     else salary * 0.24
tax salary
  | salary > 900 = salary * 0.44
  | salary > 500 = salary * 0.36
  | otherwise = salary * 0.24

Haskell Guards (2)

tax salary
  | salary > 900 = salary * 0.44
  | salary > 500 = salary * 0.36
  | otherwise = salary * 0.24
tax salary = salary * taxClass
  where taxClass
          | salary > 900 = 0.44
          | salary > 500 = 0.36
          | otherwise = 0.24

Java <= 11

Haskell

switch (taxClass) {
  case CLASS_1:
   return 0.44;
  case CLASS_2:
   return 0.36;
}
case taxClass of
  CLASS_1 -> 0.44
  CLASS_2 -> 0.36
switch (taxClass) {
  case CLASS_1:
   tax = 0.44;
   break;
  case CLASS_2:
   tax = 0.36;
   break;
}

Switch-Case (1)

Java 12+

Haskell

switch (taxClass) {
  case CLASS_1 -> 0.44;
  case CLASS_2 -> 0.36;
}
case taxClass of
  CLASS_1 -> 0.44
  CLASS_2 -> 0.36

Switch-Case (2)

float clz = switch (taxClass) {
    case CLASS_1 -> 0.44;
    case CLASS_2 -> 0.36;
  }
float value = switch (taxClass) {
    case CLASS_1 -> {
        System.out.println("High tax level.");
        yield 0.44;
    }
    case CLASS_2 -> {
        System.out.println("Regular tax level.");
        yield 0.36;
    }
    default -> {
        System.out.println("Low tax level.");
        yield 0.24;
    }
};

Java

Haskell

enum Letter {A, B, C, D};
public String getLetter(Letter letter) {
  return switch (letter) {
    case A, B, C, D -> "A letter";
    default -> "Unknown";
  };
}
data Letter = A | B | C | D deriving (Enum, Eq) 
letter :: Letter -> String
letter letter 
  | elem letter [A .. D] = "A letter"
  | otherwise = "Unknown"

Switch-Case (3)

repl.it/@dervism/control-structures

Functional Programming

Why isn't it

more widely used?

Marketing

 $xxxM campaign 

Time-To-Production

High learning curve.

Object Orientation

Algol60

Fortran

Lisp

1958-60

Cobol

Algol60

Fortran

Lisp

Simula

1962

1958-60

Cobol

Algol60

Fortran

Lisp

Simula

Simula67

1967

1962

1958-60

Cobol

Algol60

Fortran

Lisp

Simula

Simula67

SmallTalk

C

1972

1967

1962

1958-60

Cobol

Pascal

Algol60

Fortran

Lisp

Simula

Simula67

SmallTalk

C

1980-86

Objective-C

C++

Eiffel

Ada

1972

1967

1962

1958-60

Cobol

Pascal

Algol60

Fortran

Lisp

Simula

Simula67

SmallTalk

C

1980-86

Objective-C

C++

Eiffel

Ada

1972

1967

1962

1958-60

Python

Java

1990-95

Cobol

Pascal

Ruby

JavaScript

Algol60

Fortran

Lisp

Simula

Simula67

SmallTalk

C

1980-86

Objective-C

C++

Eiffel

Ada

1972

1967

1962

1958-60

Python

Java

1990-95

Cobol

Pascal

Ruby

JavaScript

2009+

Rust

Swift

Go

Scala

Kotlin

2004

Algol60

Fortran

Lisp

Simula

Simula67

SmallTalk

C

1980-86

Objective-C

C++

Eiffel

Ada

1972

1967

1962

1958-60

Python

Java

1990-95

Cobol

Pascal

Ruby

JavaScript

2009+

Rust

Swift

Go

Scala

Kotlin

2004

Procedural

Object-Oriented

Multi-Paradigm

Algol60

Fortran

Lisp

Simula

Simula67

SmallTalk

C

1980-86

Objective-C

C++

Eiffel

Ada

1972

1967

1962

1958-60

Python

Java

1990-95

Cobol

Pascal

Ruby

JavaScript

2009+

Rust

Swift

Go

Scala

Kotlin

2004

Procedural

Object-Oriented

202x+

Multi-Paradigm

Functional?

Tooling

Java is one of the best supported technologies.

Haskell lacks modern tooling.

The JVM is amazing!

  1. HotSpot compiler is extremely good

  2. Several languages are supported

  3. The JVM is fast

Community

#Java

@dervismn

dervis.no

Thanks!

https://github.com/dervism

https://repl.it/@dervism

Thinking Functional

Maybe a = Just a | Nothing

Avoid null values

Either a b = Left a | Right b

Do not throw exceptions

Learn about Monads

Preform side-effects in a safe manner

Learn about Lenses

Easily change fields with immutable structures

@dervismn

dervis.no

Thanks!

https://github.com/dervism

https://repl.it/@dervism

Hackage

Search for packages.

Hoogle

Search for functions and types.

https://hoogle.haskell.org/

https://hackage.haskell.org/

record Person(name, age) {}

Person = name AND age

sealed interface Person
             permits Teacher, Student

switch (person) {

        case Teacher(name, age) -> ...

        case Student(name, _) -> ...

}

module com.testmodule {
 requires module.name;
 
 requires static module.name;
 requires transitive module.name;

 exports package.name;
 export package.name 
        to some.package;

 opens com.package;
 opens com.package 
       to another.module;
}

https://bit.ly/devoxxpl21dm