Java & Haskell
Similarities and Differences

Dervis Mansuroglu - @dervis_m

Developer & Competency Manager

Norwegian JUG javaBin, Oslo Organizer

Who am I and why listen to me?

Oslo Software Architecture Co-Organizer

Java Champion

This talk is not about:

Monads

Higher kinded types

Function composition

Category theory

Functors

A Brief Summary

Key takeaways

Java

  • 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

Haskell

  • 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 (Chalmers Uni), Philip L. Wadler

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

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

Algebraic Data Types

Aka ADT's

Sum type

This  OR  That.

Product type

This  AND  That

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

Records, tuples, ADT (with one constructor), Java classes, C structs

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

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

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

* cant extend with additional data

Java

Haskell

enum Animal {
   DOG("Max"), CAT("Nala"),
   ELK("Sam");

   String name;
   Animal(String name) { 
     this.name = name
   }
}
data Animal = Dog | Cat | Elk
enum Animal {
   DOG, CAT, ELK
}
data Animal =  Dog String 
             | Cat String
             | Elk String

name (Dog n) = n
name (Cat n) = n
name (Elk n) = n
data Animal = Dog String
            | Cat String Int
            | Elk Int Double | Unknown
            
info (Dog s) = "Name = " ++ s
info (Cat s i) = s ++ show i ++ " years."
info (Elk _ d) = "Elk weight" ++ show d
info _ = "Unknown animal"

Records

ADT's with named accessors

Project Amber

Java 14 - Records

enum AnimalType { Mammal, Bird, Fish }

record Animal(String name, int age, AnimalType type) {}

@Test
void propertiesEquals() {
  var elephant = new Animal("Elephant", 10, Mammal);

  assertEquals(elephant.name(), "Elephant");
  assertEquals(elephant.age(), 10);
  assertEquals(elephant.type(), Mammal);
}

https://github.com/dervism/java14-demo

Haskell - Record type

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

data Animal = Animal {
   name :: String,
   age :: Int,
   type :: AnimalType
}

instance Show Animal where
  show (Animal a b c) = a ++ "," ++ show c

main = do
  print $ (Animal "Elephant" 10 Mammal)
  putStrLn $ name (Animal "Elephant" 10 Mammal)
  
 -- Output: Elephant,Mammal
 -- Output: Elephant

repl.it/@dervism/enumeration

ADT's in Java 8

https://chadaustin.me/2015/07/sum-types/

 

http://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt

Sealed Types

A step closer to ADT's

Project Amber

Java

Haskell

sealed interface Node
    permits A, B, C { ... }
data Spec = Spec {
  engineType :: Engine,
  maxSpeed :: Speed,
  featureList :: [Feature]
}

data Transport = Car Spec 
       | Bus Spec | Plane Spec

class Automobile a where
  spec :: a -> Spec

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

Inline classes

codes like a class, acts like an int

Project Valhalla

Java

Haskell

With the exception of mutable types (such as MVars/TVars), all of Haskell's data types are first-class values.

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

Modules, Classes & Methods

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

...

Java 9+

Haskell

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 anotherModule;
}
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 = ....
module-info.java
 

Java

Haskell

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

Java

Haskell

interface Person<A> {
  String name(A a);
}

class Teacher {
  String tName;
  int tAge;

  public Teacher(String tName, int tAge) {
    this.tName = tName;
    this.tAge = tAge;
  }
}

class TeacherInstance implements Person<Teacher> {
  @Override
  public String name(Teacher teacher) {
    return "Hello to " + teacher.tName;
  }
}

class Student implements Person<Student> {
  String sName;
  int sAge;

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

  @Override
  public String name(Student student) { // low cohesion
    return "Hello to " + student.sName;
  }
}

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

    Student student = new Student("Peter", 30);
    System.out.println(student.name(student)); // ???
  }
}
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 = "Hello to " ++ (tName teacher)

instance Person Student where
  name student = "Hello to " ++ (sName student)

main = do
  let teacher = name (Teacher "Jane" 30)
      student = name (Student "Peter" 30)
  
  putStrLn $ teacher ++ "\n" ++ student

repl.it/@dervism/JavaTypeclasses

repl.it/@dervism/typeclasses

interface Person {
  String name();
}

class Teacher {
  String tName;
  int tAge;

  public Teacher(String tName, int tAge) {
    this.tName = tName;
    this.tAge = tAge;
  }
}

class TeacherInstance implements Person {
  private Teacher teacher;
  
  public TeacherInstance(Teacher teacher) {
    this.teacher = teacher;
  }
  
  @Override
  public String name() {
    return "Hello to " + teacher.tName;
  }
}

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 Main {
  public static void main(String[] args) {
    TeacherInstance teacherInstance = new TeacherInstance(new Teacher("Jane", 30));
    System.out.println(teacherInstance.name());

    Student student = new Student("Peter", 30);
    System.out.println(student.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, up to v.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

Java 13 (Project Amber)

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 ..] = "A letter"
  | otherwise = "Unknown"

Functional Programming

Why hasn't Haskell

taken over the world?

Object Orientation

Algol60, Simula, C/C++, Unix

Marketing

 $xxxM campaign 

Tooling

Java is one of the best supported technologies.

Performance

The JVM is amazing!

  1. HotSpot compiler is extremely good

  2. The JVM has better, or equal performance

Time-To-Production

High learning curve.

Community

#Java

Haskell on the JVM

Two alternatives exits

Frege

ETA-Lang

@dervis_m

dervis.no

Thanks!

https://github.com/dervism

https://repl.it/@dervism

http://bit.ly/jfokus2020dm

Java & Haskell - Similarities and Differences

By Dervis Mansuroglu

Private

Java & Haskell - Similarities and Differences

The Java programming language has evolved a lot over the last year. With future releases from projects like Panama, Loom, Amber, and Valhalla, Java will continue to adopt features that developers know from languages like Kotlin and Scala. One area that Java still differs, is functional programming. There is especially one functional language that people either hate or love: Haskell. In this talk, I will take a look at the similarities and differences between Java and Haskell. For developers who have never tried Haskell, it will be good opportunity to see how it compares with features we know in Java. We will also discuss why Haskell hasn't taken over the world yet. After listening to this talk, you will learn how you can benefit from learning a purely functional language and you will also learn about Java's strengths compared to Haskell.