A well-typed program never goes wrongjulien@kaching.comSilicon Valley Code Camp 2010
µ-JavaImagine Java deprived of all native types and all control-flow constructs
Encoding ℕ in µ-JavaA number is eitherzero orthe successor of another number
Encoding ℕ in µ-Javainterface Number { }class Zero implements Number { }class Successor implements Number {privatefinal Number predecessor;   Successor(Number predecessor) {this.predecessor = predecessor;    }}
Encoding ℕ in µ-JavaNumber zero = new Zero();Number one = new Successor(zero);Number two = new Successor(one);
Encoding ℕ in µ-JavaNumber zero = new Zero();Number one = new Successor(zero);Number two = new Successor(one);Number three = one.plus(two);
Encoding ℕ in µ-Javainterface Number {    Number plus(Number that);}
Encoding ℕ in µ-Javaclass Zero implements Number { public Number plus(Number that) {   }}
Encoding ℕ in µ-Javaclass Zero implements Number { public Number plus(Number that) {return that;   }}
Encoding ℕ in µ-JavaclassSuccessor implementsNumber { privatefinal Number predecessor;    Successor(Number predecessor) {this.predecessor = predecessor;}public Number plus(Number that) {   }}
Encoding ℕ in µ-JavaclassSuccessor implementsNumber { privatefinal Number predecessor;    Successor(Number predecessor) {this.predecessor = predecessor;}public Number plus(Number that) {returnnew Successor(predecessor.plus(that));   }}
Encoding ℕ in µ-JavaBase case:    0 + n = nRecurrence:    m + n = ((m - 1) + n) + 1
Growing a Languageµ-Javaexpressiveness
Today’s AgendaPractical discussion of Type Safety.Grow your language to make it safer.
“I’ve written well-typed programs before,   and they went wrong!”
This will definitely go wrongscala> defdivide(a: Int, b: Int) = a / bdivide: (a: Int,b: Int)Int
This will definitely go wrongscala> defdivide(a: Int, b: Int) = a / bdivide: (a: Int,b: Int)Intscala> divide(4, 0)java.lang.ArithmeticException: / by zeroat .divide(<console>:5)
Was it well-typed?
Was it well-typed?Yes, but the types didn’t fully convey the complexity of the domain.The type checker doesn’t know you can’t divide by 0 unless it is expressed by the types.How can you grow the language to address this issue?
Division by zero, revisitedscala> abstractclass Number(n: Int)scala> caseclass Zero extends Number(0)scala> caseclassNonZero(n: Int) extends Number(n)scala> def number(n: Int) = if (n == 0) Zero elseNonZero(n)
Division by zero, revisitedscala> abstractclass Number(n: Int) {def divide(that: NonZero) = number(n / that.n)}scala> number(4).divide(Zero)
Division by zero, revisitedscala> abstractclass Number(n: Int) {def divide(that: NonZero) = number(n / that.n)}scala> number(4).divide(Zero)error: type mismatch; found     : Zero required: NonZero
Type SafetyThe extent to which a programming language discourages or prevents type errors.A type error is erroneous program behavior caused by a discrepancy between differing data types.
Well-typed programs never go wrongPreservation Well typednessof programs remains invariant under the transition rules of the language. Progress A well typed program never gets into a state where no further transitions are possible.
Value TypesRepresents possibly infinite set of similarly kinded data transcending an application's life.Life cycles are meaningless.Value types are immutable.Usually restrict the universe of their underlying types.
Value TypesclassEmailAddressextends Value<String> { …classPrice extends Value<Long> { …class Id<E extends Entity> extends Value<Long> { …
The 1:1 PrinciplebooleanplaceLimitOrder(Action, Integer, String, Long);booleanplaceLimitOrder(Action, Quantity, Ticker, Price);
Type Safe Bit FieldApple's ticker AAPL.What about Berkshire Hathaway’s? Google says BRKA,Yahoo! BRK-A,Bloomberg BRK/A and Reuters BRKa.
Type Safe Bit FieldinterfaceSecurityTag {  interfaceGoogle extendsSecurityTag{}  interfaceYahoo extendsSecurityTag{}  interfaceBloomberg extendsSecurityTag{}  interfaceReuters extendsSecurityTag{}}
Type Safe Bit FieldclassTaggedTicker<T extendsSecurityTag> extends Value<String> { ...
Type Safe Bit FieldPrice getPriceFromGoogle(TaggedTicker<Google> ticker) { ... voidsendBuyOrderToBloomberg(TaggedTicker<Bloomberg> ticker, Quantity quantity) { ..
Type Safe Bit Fieldinterface Security {    <T extendsSecurityTag> TaggedTicker<T>getTaggedTicker(Class<T> kind);}
Type Safe Bit FieldMap<Class<? extendsSecurityTag>, Long> TAG2MASK =  ImmutableMap.       <Class<? extendsSecurityTag>, Long> builder()            .put(SecurityTag.Google.class, 0x01)            .put(SecurityTag.Yahoo.class, 0x02)            .put(SecurityTag.Bloomberg.class, 0x04)            .put(SecurityTag.Reuters.class, 0x08)            .build();
Type Safe Bit Field<T extendsSecurityTag> void set(Class<T> kind) {  tags = (tags | TAG2MASK.get(kind));}<T extendsSecurityTag> void unset(Class<T> kind) {  tags = (tags & ~TAG2MASK.get(kind));}
Invariant MapA bunch of methods in java.util.Map are contravariant on the key type.This makes refactorings extremely error prone.java.util.Map#get(Object)java.util.Map#remove(Object)java.util.Map#containsKey (Object)
Invariant MapvoiddoSomething(Map<Ticker, Quote> cache) {   …cache.remove(ticker);
Invariant MapvoiddoSomething(Map<Isin, Quote> cache) {      …cache.remove(ticker);
Invariant MapinterfaceInvariantMap<K, V> {   V get(K key);  V remove(K key);  ...
Invariant MapclassInvariantMaps{static<K, V> InvariantMap<K, V> newHashMap() {returndelegate(newHashMap<K, V>());  }static<K extends Comparable<? super K>, V> InvariantMap<K, V> newTreeMap() {returndelegate(newTreeMap<K, V>());  }    …
Optionabstractclass Option[T]caseclass None extends Option[Nothing]caseclass Some(x: T) extends Option[T]getUser(id) match {case Some(user) ⇒ …case None ⇒ …}
OptionMuch more verbose in Java...getUser(id).visit(newOptionVisitor<Unit>() {public Unit caseSome(User user) {        …publicUnit caseNone() {        …})
Option… but still useful!interfaceUserRepository {    Option<User> getUser(Id<User> id);User getUserOrThrow(Id<User> id);}
Abstracting the Control FlowWe saw how to grow a language by introducing more types.We can also improve control flow structuresby abstracting the control flow.
Abstracting the Control FlowBigDecimal total = ZERO;for (BigDecimal value : values) {if (value.compareTo(ZERO) > 0) {total.add(value);   }}return total;
Abstracting the Control Flowreturnsum(filter(       values, new Predicate<BigDecimal>() {publicboolean apply(BigDecimal t) {return t.compareTo(ZERO) > 0;             }        }));
Abstracting the Control FlowEasier to re-use operations.The “wiring” between operations is checked by the type system.
ReferencesFoundations for Programming LanguagesJohn C. MitchellI Can Has Invariant Mapz?http://eng.kaching.com/2010/07/i-can-has-invariant-mapz.htmlType Safe Bit Fields Using Higher-KindedPolymorphism       http://eng.kaching.com/2010/08/type-safe-bit-fields-using-higher.html

A well-typed program never goes wrong

  • 1.
    A well-typed programnever goes wrongjulien@kaching.comSilicon Valley Code Camp 2010
  • 2.
    µ-JavaImagine Java deprivedof all native types and all control-flow constructs
  • 3.
    Encoding ℕ inµ-JavaA number is eitherzero orthe successor of another number
  • 4.
    Encoding ℕ inµ-Javainterface Number { }class Zero implements Number { }class Successor implements Number {privatefinal Number predecessor; Successor(Number predecessor) {this.predecessor = predecessor; }}
  • 5.
    Encoding ℕ inµ-JavaNumber zero = new Zero();Number one = new Successor(zero);Number two = new Successor(one);
  • 6.
    Encoding ℕ inµ-JavaNumber zero = new Zero();Number one = new Successor(zero);Number two = new Successor(one);Number three = one.plus(two);
  • 7.
    Encoding ℕ inµ-Javainterface Number { Number plus(Number that);}
  • 8.
    Encoding ℕ inµ-Javaclass Zero implements Number { public Number plus(Number that) { }}
  • 9.
    Encoding ℕ inµ-Javaclass Zero implements Number { public Number plus(Number that) {return that; }}
  • 10.
    Encoding ℕ inµ-JavaclassSuccessor implementsNumber { privatefinal Number predecessor; Successor(Number predecessor) {this.predecessor = predecessor;}public Number plus(Number that) { }}
  • 11.
    Encoding ℕ inµ-JavaclassSuccessor implementsNumber { privatefinal Number predecessor; Successor(Number predecessor) {this.predecessor = predecessor;}public Number plus(Number that) {returnnew Successor(predecessor.plus(that)); }}
  • 12.
    Encoding ℕ inµ-JavaBase case: 0 + n = nRecurrence: m + n = ((m - 1) + n) + 1
  • 13.
  • 14.
    Today’s AgendaPractical discussionof Type Safety.Grow your language to make it safer.
  • 15.
    “I’ve written well-typedprograms before, and they went wrong!”
  • 16.
    This will definitelygo wrongscala> defdivide(a: Int, b: Int) = a / bdivide: (a: Int,b: Int)Int
  • 17.
    This will definitelygo wrongscala> defdivide(a: Int, b: Int) = a / bdivide: (a: Int,b: Int)Intscala> divide(4, 0)java.lang.ArithmeticException: / by zeroat .divide(<console>:5)
  • 18.
  • 19.
    Was it well-typed?Yes,but the types didn’t fully convey the complexity of the domain.The type checker doesn’t know you can’t divide by 0 unless it is expressed by the types.How can you grow the language to address this issue?
  • 20.
    Division by zero,revisitedscala> abstractclass Number(n: Int)scala> caseclass Zero extends Number(0)scala> caseclassNonZero(n: Int) extends Number(n)scala> def number(n: Int) = if (n == 0) Zero elseNonZero(n)
  • 21.
    Division by zero,revisitedscala> abstractclass Number(n: Int) {def divide(that: NonZero) = number(n / that.n)}scala> number(4).divide(Zero)
  • 22.
    Division by zero,revisitedscala> abstractclass Number(n: Int) {def divide(that: NonZero) = number(n / that.n)}scala> number(4).divide(Zero)error: type mismatch; found : Zero required: NonZero
  • 23.
    Type SafetyThe extentto which a programming language discourages or prevents type errors.A type error is erroneous program behavior caused by a discrepancy between differing data types.
  • 24.
    Well-typed programs nevergo wrongPreservation Well typednessof programs remains invariant under the transition rules of the language. Progress A well typed program never gets into a state where no further transitions are possible.
  • 25.
    Value TypesRepresents possiblyinfinite set of similarly kinded data transcending an application's life.Life cycles are meaningless.Value types are immutable.Usually restrict the universe of their underlying types.
  • 26.
    Value TypesclassEmailAddressextends Value<String>{ …classPrice extends Value<Long> { …class Id<E extends Entity> extends Value<Long> { …
  • 27.
    The 1:1 PrinciplebooleanplaceLimitOrder(Action,Integer, String, Long);booleanplaceLimitOrder(Action, Quantity, Ticker, Price);
  • 28.
    Type Safe BitFieldApple's ticker AAPL.What about Berkshire Hathaway’s? Google says BRKA,Yahoo! BRK-A,Bloomberg BRK/A and Reuters BRKa.
  • 29.
    Type Safe BitFieldinterfaceSecurityTag {  interfaceGoogle extendsSecurityTag{}  interfaceYahoo extendsSecurityTag{}  interfaceBloomberg extendsSecurityTag{}  interfaceReuters extendsSecurityTag{}}
  • 30.
    Type Safe BitFieldclassTaggedTicker<T extendsSecurityTag> extends Value<String> { ...
  • 31.
    Type Safe BitFieldPrice getPriceFromGoogle(TaggedTicker<Google> ticker) { ... voidsendBuyOrderToBloomberg(TaggedTicker<Bloomberg> ticker, Quantity quantity) { ..
  • 32.
    Type Safe BitFieldinterface Security { <T extendsSecurityTag> TaggedTicker<T>getTaggedTicker(Class<T> kind);}
  • 33.
    Type Safe BitFieldMap<Class<? extendsSecurityTag>, Long> TAG2MASK = ImmutableMap. <Class<? extendsSecurityTag>, Long> builder()     .put(SecurityTag.Google.class, 0x01)     .put(SecurityTag.Yahoo.class, 0x02)     .put(SecurityTag.Bloomberg.class, 0x04)     .put(SecurityTag.Reuters.class, 0x08)     .build();
  • 34.
    Type Safe BitField<T extendsSecurityTag> void set(Class<T> kind) { tags = (tags | TAG2MASK.get(kind));}<T extendsSecurityTag> void unset(Class<T> kind) { tags = (tags & ~TAG2MASK.get(kind));}
  • 35.
    Invariant MapA bunchof methods in java.util.Map are contravariant on the key type.This makes refactorings extremely error prone.java.util.Map#get(Object)java.util.Map#remove(Object)java.util.Map#containsKey (Object)
  • 36.
    Invariant MapvoiddoSomething(Map<Ticker, Quote>cache) { …cache.remove(ticker);
  • 37.
    Invariant MapvoiddoSomething(Map<Isin, Quote>cache) { …cache.remove(ticker);
  • 38.
    Invariant MapinterfaceInvariantMap<K, V>{ V get(K key); V remove(K key); ...
  • 39.
    Invariant MapclassInvariantMaps{static<K, V>InvariantMap<K, V> newHashMap() {returndelegate(newHashMap<K, V>()); }static<K extends Comparable<? super K>, V> InvariantMap<K, V> newTreeMap() {returndelegate(newTreeMap<K, V>()); } …
  • 40.
    Optionabstractclass Option[T]caseclass Noneextends Option[Nothing]caseclass Some(x: T) extends Option[T]getUser(id) match {case Some(user) ⇒ …case None ⇒ …}
  • 41.
    OptionMuch more verbosein Java...getUser(id).visit(newOptionVisitor<Unit>() {public Unit caseSome(User user) { …publicUnit caseNone() { …})
  • 42.
    Option… but stilluseful!interfaceUserRepository { Option<User> getUser(Id<User> id);User getUserOrThrow(Id<User> id);}
  • 43.
    Abstracting the ControlFlowWe saw how to grow a language by introducing more types.We can also improve control flow structuresby abstracting the control flow.
  • 44.
    Abstracting the ControlFlowBigDecimal total = ZERO;for (BigDecimal value : values) {if (value.compareTo(ZERO) > 0) {total.add(value); }}return total;
  • 45.
    Abstracting the ControlFlowreturnsum(filter( values, new Predicate<BigDecimal>() {publicboolean apply(BigDecimal t) {return t.compareTo(ZERO) > 0; } }));
  • 46.
    Abstracting the ControlFlowEasier to re-use operations.The “wiring” between operations is checked by the type system.
  • 47.
    ReferencesFoundations for ProgrammingLanguagesJohn C. MitchellI Can Has Invariant Mapz?http://eng.kaching.com/2010/07/i-can-has-invariant-mapz.htmlType Safe Bit Fields Using Higher-KindedPolymorphism http://eng.kaching.com/2010/08/type-safe-bit-fields-using-higher.html