Immutable Data

the moonlanding didn't use interchangeable backdrops

We are living in a Material World

Things don't change - they get replaced.

Immutable Data

Events Don't Change

lets ignore concurrency and distribution..for now

Single responsibility principle

  • A class should have only a single responsibility (i.e. only one potential change in the software's specification should be able to affect the specification of the class)
  • Prevent errors by removing the chance of errors.
  • If you don't need to change, don't add mutation.
  • If you do need to change, control it.

Open/closed principle

  • “software entities … should be open for extension, but closed for modification.”
  • Separation of Concern - keep your behaviour separate from your data.

HTTP REQUESTS

  • Once made - never change.
  • Method / URI / Contents - constant throughout the life cycle.
  • A proxied request...is a different request.

HTTP REQUESTS

curl -v -d name=Mark -d drink="Trim Latte" http://www.gotohal.net/restbucks/api/orders
* Hostname was NOT found in DNS cache
*   Trying 54.235.194.99...
* Connected to www.gotohal.net (54.235.194.99) port 80 (#0)
> POST /restbucks/api/orders HTTP/1.1
> User-Agent: curl/7.37.1
> Host: www.gotohal.net
> Accept: */*
> Content-Length: 26
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 26 out of 26 bytes
< HTTP/1.1 200 OK
* Server Cowboy is not blacklisted
< Server: Cowboy
< Date: Wed, 10 Dec 2014 07:49:38 GMT
< Connection: keep-alive
< Content-Type: text/plain; charset=utf-8
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
< Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Connection, DNT, Referer, User-Agent, Accept, Accept-Encoding, Accept-Language
< Content-Length: 0
< Via: 1.1 vegur
<

STRUCTURED, IMMUTABLE DATA

  • Convert framework specific HTTP data into structured, immutable data structures.
  • Move out REST/HTTP code into services ASAP.

http://immutables.org

package com.talios.jug;

import org.immutables.value.Value;

@Value.Immutable
public abstract class Order {

  @Value.Parameter
  public abstract String name();

  @Value.Parameter
  public abstract String drink();

}

....

Order o = ImmutableOrder.of(req.getFirstValue("name"),
                            req.getFirstValue("drink"));

https://github.com/google/auto

package com.talios.jug;

import com.google.auto.value.AutoValue;
import org.jetbrains.annotations.NotNull;

@AutoValue
public abstract class Order {

  public static Order of(String name, String drink) {
    return new AutoValue_Order(name, drink);
  }

  public abstract String name();

  public abstract String drink();

}

...

Order o = Order.of(req.getFirstValue("name"),
                   req.getFirstValue("drink"));

java enumerated types

package com.smxemail.jug;

public enum OrderType {
    TABLED,
    TAKE_AWAY,
    DELIVERED
}
  • Each element is a singleton instance.
  • toString(), hashCode(), equals() generated.
  • Instance == Equality:  order1 == order2
  • Type has no built in functionality

JAVA ENUMERATED TYPES

package com.smxemail.jug;

public enum OrderType {

    TABLED(0.0d),
    TAKE_AWAY(0.0d),
    DELIVERED(1.5d);

    private Double surcharge;

    private OrderType(Double surcharge) {
      this.surcharge = surcharge;
    }

    public Double getSurcharge() {
      return surcharge;
    }

}
  • Each element is a singleton instance.
  • toString(), hashCode(), equals() generated.
  • Instance equality:  order1 == order2
  • Single shape
  • Class load fixed state, and fixed functionality built in.

immutable data

Algebraic Data Types

maybe isn't a monad

Algebraic Data Types

  • A Grouping of Immutable Data Types
  • Closed for extension by third parties
  • Kinda like an enum...
  • ...with optional and disparate shaped values

Why use Algebraic DataTypes?

ADTs turn that 180 degrees around. With an ADT the "hierarchy" is sealed against extension. You can't add new cases without opening up the base source. But you can very, very easily add operations without even having access to the ADT's source — James Iry

 

ALGEBRAIC DATA TYPES

data List a = Nil | Cons a (List a)
data Maybe a = Nothing | Just a
data Order = Tabled TablePosition String
           | Takeout String String
           | Delivery String String Double
data NamedOrder = Tabled   { position :: TablePosition, drink :: String}
                | Takeout  { name :: String, drink :: String }
                | Delivery { name :: String, drink :: String, surcharge :: Double }
order = Delivery "Mark" "Trim Latte" 1.5

case order of (Delivery name drink surcharge) -> ...do something with surcharge
              (Takeout name drink)            -> ...drink up! don't slip...
let description = (name order) ++ " ordered the drink: " ++ (drink order)

http://jamesiry.github.io/jADT

package com.talios.jug;

Order = Tabled(final TablePosition position, final String drink)
      | Takeout(final String name, final String drink)
      | Delivery(final String name, final String drink, final Double surcharge);
Order order = Order._Takeout("Mark", "Trim Latte");
String drink = order._case(new Order.MatchBlock<String>() {
  public String _case(Tabled order) {
    return order.drink;
  }
  public String _case(Takeout order) {
    return order.drink;
  }
  public String _case(Delivery order) {
    return order.drink;
  }
};
public enum TablePosition { T1, T2, T3, T4, T5, T6 }

HTTPS://GITHUB.COM/SVIPERLL/ADT4J

public enum TablePosition { T1, T2, T3, T4, T5, T6 }

@GenerateValueClassForVisitor(resultVariableName="R")
interface OrderVisitor<R> {
  R tabled(TablePosition position, String drink);
  R takeout(String name, String drink);
  R delivery(String name, String drink, Double surcharge);
}

...

Order o  = Order.tabled(TablePosition.T4, "Trim Latte");
Order o2 = Order.delivery("Richard", "Hot Chocolate", 1.5d);
Order o3 = Order.takeout("Mark", "Trim Latte");
...

String drink = someOrder.accept(new OrderVisitor<String>() {
  public String tabled(TablePosition position, String drink) {
    return drink;
  }
  
  public String takeout(String name, String drink) {
    return drink;
  }

  public String delivery(String name, String drink, Double surcharge) {
    return drink;
  }
});
  • Each elements can have disparate shapes
  • Immutable once created
  • No internal functionality, extensible at callsite...but uglier.
  • Totality checking

HTTPS://GITHUB.COM/SVIPERLL/ADT4J

public enum TablePosition { T1, T2, T3, T4, T5, T6 }

@GenerateValueClassForVisitor(resultVariableName="R")
interface OrderVisitor<R> {
  R tabled(TablePosition position, @Getter("drink") String drink);
  R takeout(String name, @Getter("drink") String drink);
  R delivery(String name, @Getter("drink") String drink, Double surcharge);
}

...

Order o  = Order.inhouse("Mark", "Trim Latte");
Order o2 = Order.delivery("Richard", "Hot Chocolate", 1.5d);

...

String drink = someOrder.drink();
  • Semantic "Helper" methods can be defined for easier consumption.
  • Currently not that easy on the eye.

HTTPS://GITHUB.COM/SVIPERLL/ADT4J

public enum TablePosition { T1, T2, T3, T4, T5, T6 }

@GenerateValueClassForVisitor(resultVariableName="R", delegateTo=OrderDefaults.class)
interface OrderVisitor<R> {
  R tabled(TablePosition position, String drink);
  R takeout(String name, String drink);
  R delivery(String name, String drink, Double surcharge);
}

interface OrderDefaults {
  static boolean drinkUp(Order order) {
    order.accept(new OrderVisitor<String>() {
      public String tabled(TablePosition position, String drink) {
        return true;
      }  
      public String takeout(String name, String drink) {
        if (Weather.isRaining() && Authentication.getCurrentUser().isRunningLate()) {
          throw new SpillageException("Ewps.");
        }
      }
      public String delivery(String name, String drink, Double surcharge) {
        throw new InvalidStateException("Unable to drink " + drink + ", onroute!");
      }
    });
  }
}

...

Order.delivery("Richard", "Hot Chocolate", 1.5d)
     .drinkUp();
  • Internally extensible with static methods / delegates
    Experimental fork in-progress by me.

Immutable Data

By talios

Immutable Data

Immutable Data and Abstract Data Types

  • 1,267