Loading

Sum and Product Types in Modern Java

Raghav Shankar

This is a live streamed presentation. You will automatically follow the presenter and see the slide they're currently on.

Sum and Product Types in Modern Java

History

  • Records
    • First preview, Java 14
    • Second preview, Java 15
    • Release, Java 16
  • Sealed types
    • First preview, Java 15
    • Second preview Java 16
    • Release, Java 17

Sum and Product types

➕, ✖️...     ??!

  • Sum types: A or B or C
  • Just enums with baggage.
  • Product types: A, B and C
  • Just tuples, like in python.

Sum types?

enum Event {
    Print(String),
    File(PathBuf),
}


let a = Event::Print("aaa".to_owned());
    
match a {
    Event::File(path) => {}
    Event::Print(content) => {}
};

In Java?

sealed class Event permits File, Print {}

final class File extends Event {}

final class Print extends Event {}

Why?

interface Celestial { ... }
final class Planet implements Celestial { ... }
final class Star   implements Celestial { ... }
final class Comet  implements Celestial { ... }
interface Celestial { ... }
final class Planet implements Celestial { ... }
final class Star   implements Celestial { ... }
final class Comet  implements Celestial { ... }

// !!!
final class SpaceRoadster implements Celestial { ... }

With sealed classes?

sealed interface Celestial { ... }
final class Planet implements Celestial { ... }
final class Comet  implements Celestial { ... }

sealed class Star  implements Celestial { ... }
final  class Dwarf implements Star { ... }
final  class RedGiant implements Star { ... }

// In user code

// This will be an error!
final class SpaceRoadster implements Celestial {
    ... 
}

Product types?

record Book ( int id, String title ) {}

About records

  • No need for constructor
  • All fields declared are private and final
  • Getters autodefined
  • Can have extra methods, you will see.

Use Cases

  • Functional data structures!

Optional<T>

  • Already in Java?!
  • Boo, not functional.
  • Let's make our own.
@jdk.internal.ValueBased  
public final class Optional<T> {  
    /**  
     * Common instance for {@code empty()}.  
     */
    private static final Optional<?> EMPTY = 
        new Optional<>(null);  
 
    /**  
     * If non-null, the value; if null, 
     * indicates no value is present  
     */    
    private final T value;

     ...
Optional<Integer> optInt = Optional.of(3);

if (optInt.isPresent()) {
    // This would throw an exception 
    // if the optional were empty.
    int value = optInt.get();
}

Why functional?

  • Pattern matching is nice (Java 19+)

  • I like it (sue me)

  • Space savings !1!!!

Three things

1. We are using Java 19 preview features (record patterns in switch cases)
2. Java requires exhaustive matching for sealed classes.
3. Generic types absolutely need to be mentioned at all times.

 

throw new NullPointerException();

  • We want error handling
  • But Exceptions are bad!
  • Java only does exceptions tho!
  • Or do we...

In other languages

  • Rust: Errors are just normal types!
  • Like in Go
  • But better!

 

f, err := os.Open("filename.ext")
if err != nil {
    log.Fatal(err)
}

 

enum Result<T, E> {
    Ok(T),
    Err(E)
}

 

 
use std::fs::File;
fn main() {
   // main.jpg doesn't exist
   let f = File::open("main.jpg");   
   match f {
      Ok(f)=> {
         println!("file found {:?}",f);
      },
      Err(e)=> {
           //handled error
      }
   }
}

Railway oriented Programming

if (!someCondition)
    throw InvalidArgumentException(...);

if (!someOtherCondition)
    throw InvalidStateException(...);

Results, in Java

Made with Slides.com