Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

53
Copyright 2018 Tendril, Inc. All rights reserved.

Transcript of Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Page 1: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Page 2: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Type Parameter Power-Up!

Variance, Bounds, and Inference

Page 3: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Motivations

INTRO

3

Page 4: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Motivations

INTRO

• Home Energy Report Generation System

• Free Monad

• Workflow stages extending common base class

• Workflow actions parameterized with a Report type

• Different Reports extend Report base class

• Single reports, Optional reports, Lists of reports

4

Page 5: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Helpful Compiler is Helpful

INTRO

[info] Compiling 1 Scala source to /Users/cphelps/learning/type-parameter-power-up/target/scala-2.12/test-classes ...

[error] /Users/cphelps/learning/type-parameter-power-up/src/test/scala/example/VarianceSpec.scala:55:14: covariant type A occurs in contravariant position in type A of value newwrapped

[error] def swap(newwrapped: A): Wrapper[A] = new Wrapper(newwrapped)

[error] ^

[error] one error found

5

Page 6: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Helpful Compiler is Helpful

INTRO

[info] Compiling 1 Scala source to /Users/cphelps/learning/type-parameter-power-up/target/scala-2.12/test-classes ...

[error] /Users/cphelps/learning/type-parameter-power-up/src/test/scala/example/VarianceSpec.scala:55:14: covariant type A occurs in contravariant position in type A of value newwrapped

[error] def swap(newwrapped: A): Wrapper[A] = new Wrapper(newwrapped)

[error] ^

[error] one error found

6

Page 7: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Motivations

INTRO

7

Page 8: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Motivations

INTRO

• Users

o Signatures of APIs– what is it looking for?

o What will the API accept?

o How do I make the API interact with my domain class hierarchy?

o How do I make sense of these weird error messages?

• API Designers

o What do I want to allow?

o What sorts of flexibility do I want to offer?

o How do I make sense of these weird error messages?

8

Page 9: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

“How I stopped worrying about variance: Just add +/- until it compiles” – Adriaan Moors

Page 10: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Overview

INTRO

• Variance

• Constraints and Typeclasses

• Dotty / Scala 3

10

Page 11: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Variance

11

Page 12: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Inheritance and Substitution

VARIANCE

• All values have a type

• Types have a supertype relationship up to Any

• Types have a subtype relationship down to Nothing

• References can store subclass instances

• Functions can be passed subclass instances

• Functions can return subclass instances

12

Page 13: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

LiskovSubstitution Principle

VARIANCE

Subtype Requirement: Let ϕ ( x ) be a property provable about objects x of type T. Then ϕ ( y ) should be true for objects y of type S where S is a subtype of T.[Liskov and Wing, 1994]

If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program.[Wikipedia]

13

Page 14: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Higher-KindedTypes

VARIANCE

• Generic Classes

• Types with parameters

• Contain or implemented in terms of another type

o List[Int]

o List[T]

14

Page 15: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Two Axes of Subclassing

VARIANCE

15

Container

Superclass

Container

Subclass

Contents

Superclass

Contents

Subclass

Page 16: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Variance Defined

VARIANCE

• Variance is the correlation of subtyping relationships of complex types and the subtyping relationships of their component types. [Tour of Scala]

• A central question that comes up when mixing OO with polymorphism is: if U is a subclass of T, is Container[U] considered a subclass of Container[T]? [Twitter Scala School]

16

Page 17: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Apply the LSP

VARIANCE

• Variance is the correlation of when we can substitute complex types and the when we can substitute their component types. [Tour of Scala]

• A central question that comes up when mixing OO with polymorphism is: if U is a subclass of T, can Container[U] be substituted for Container[T]? [Twitter Scala School]

17

Page 18: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Substituting Compatible Machines

VARIANCE

18

Page 19: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Domain Data

VARIANCE

abstract class Animal {def name: String

}

abstract class Pet extends Animal

case class Cat(name: String) extends Pet

case class Dog(name: String) extends Pet

19

Animal

Pet

DogCat

Page 20: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

InvarianceThe Default Case

VARIANCE

• No subclass relationship

• Cannot substitute Container[T] for Container[U] unless T is U

• Subclasses SubContainer[T] can be substituted for Container[T]

• Exposed vars must be invariant

• Influences type inference

20

Page 21: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Invariance Examples

VARIANCE

class InvariantWrapper[A](wrapped: A) {def unwrapped: A = wrapped

}

val w: InvariantWrapper[Cat] = new InvariantWrapper(Cat(”Morris"))

class SubWrapper[A](wrapped: A) extends InvariantWrapper(wrapped) {}

val sw: InvariantWrapper[Cat] = new SubWrapper(Cat("Milo"))

val aw: InvariantWrapper[Animal] = new InvariantWrapper[Cat](Cat("Bill"))

21

Animal

Pet

DogCat

Page 22: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Invariance Examples

VARIANCE

class InvariantWrapper[A](wrapped: A) {def unwrapped: A = wrapped

}

val w: InvariantWrapper[Cat] = new InvariantWrapper(Cat(”Morris"))

class SubWrapper[A](wrapped: A) extends InvariantWrapper(wrapped) {}

val sw: InvariantWrapper[Cat] = new SubWrapper(Cat("Milo"))

val aw: InvariantWrapper[Animal] = new InvariantWrapper[Cat](Cat("Bill"))

22

type mismatch; found: InvariantWrapper[Cat] required: InvariantWrapper[Animal]

Note: example.Cat <: example.Animal, but class InvariantWrapper is invariant in type A.

Animal

Pet

DogCat

Page 23: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

CovarianceSources of Values

VARIANCE

• Subclass relationship when contents have a subclass relationship

• Can substitute Container[U] for Container[T] if U is a subclass of T

• Useful for extracting the contents of the container

o Instances of U can be used where T instances are expected

o Container[U] will produce T instances

o Types are sound

• Problematic when adding or changing values

23

Page 24: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Covariance Examples

VARIANCE

class CovariantWrapper[+A](wrapped: A) {def unwrapped: A = wrapped

}

def doIt(thing: CovariantWrapper[Animal]) = ???

doIt(new CovariantWrapper[Cat](Cat("Garfield")))doIt(new CovariantWrapper[Dog](Dog(“Odie”)))

doIt(new CovariantWrapper[Any](Cat("Nermal")))

24

Animal

Pet

DogCat

Page 25: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Covariance Examples

VARIANCE

class CovariantWrapper[+A](wrapped: A) {def unwrapped: A = wrapped

}

def doIt(thing: CovariantWrapper[Animal]) = ???

doIt(new CovariantWrapper[Cat](Cat("Garfield")))doIt(new CovariantWrapper[Dog](Dog(“Odie”)))

doIt(new CovariantWrapper[Any](Cat("Nermal")))

25

type mismatch; found: CovariantWrapper[Any] required: CovariantWrapper[Animal]

Animal

Pet

DogCat

Page 26: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

ContravarianceConsumers of Values

VARIANCE

• Subclass relationship when contents have a superclass relationship

• Can substitute Container[T] for Container[U] if T is a superclass of U

• Useful for processors or consumers of values

o Instances of U can be used where T instances are expected

o Consumer[U] can consume T instances

o Types are sound

• Problematic when returning or producing values

26

Page 27: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

ContravarianceExamples

VARIANCE

abstract class Keeper[-A] {def tend(input: A): Unit

}class DogSitter extends Keeper[Dog] {override def tend(input: Dog): Unit = ???

}class ZooKeeper extends Keeper[Animal] { ... }class PetSitter extends Keeper[Pet] { ... }

val ds: Keeper[Dog] = new DogSitterds.tend(Dog("Tintin"))

val zoo: Keeper[Dog] = new ZooKeeperzoo.tend(Dog("Scooby"))

val petco: Keeper[Pet] = new DogSitterpetco.tend(Dog("Ceasar"))

27

Animal

Pet

DogCat

Page 28: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

ContravarianceExamples

VARIANCE

abstract class Keeper[-A] {def tend(input: A): Unit

}class DogSitter extends Keeper[Dog] {override def tend(input: Dog): Unit = ???

}class ZooKeeper extends Keeper[Animal] { ... }class PetSitter extends Keeper[Pet] { ... }

val ds: Keeper[Dog] = new DogSitterds.tend(Dog("Tintin"))

val zoo: Keeper[Dog] = new ZooKeeperzoo.tend(Dog("Scooby"))

val petco: Keeper[Pet] = new DogSitterpetco.tend(Dog("Ceasar"))

28

type mismatch; found : DogSitter required: Keeper[example.Pet]

Animal

Pet

DogCat

Page 29: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Positions

VARIANCE

• “Covariant Position” – method returns

• “Contravariant Position” – method arguments

• “Invariant Position” - mutable vars

• Covariant parameters cannot appear in contravariant position

• Contravariant parameters cannot appear in covariant position

• Covariant or contravariant parameters cannot appear in invariant position

o Scala 2.12.6 and IntelliJ error messages misleading

29

Page 30: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

The Problem with Contravariant Positions

VARIANCE

class Box[+Pet](p: Pet) {// won't compile – Covariant in contravariant posdef swap(newp: Pet): Box[Pet] = new Box[Pet](newp)

}

val mypet = new Box[Cat](Cat(“Morris”))

// Seems okay so farmypet.swap(Cat("Cassie"))

// Oops, we can't store a Dog in a Box[Cat]mypet.swap(Dog("Ceasar"))

30

Page 31: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

The Problem with Covariant Positions

VARIANCE

class Sitter[-Pet](p: Pet) {// won't compile - contravariant in covariant posdef walk(): Pet = ???

}

val mySitter: Sitter[Cat] = new Sitter[Pet](Cat("Doraemon"))

// seems okayval walkedPet: Pet = mySitter.walk()

// if walk returns a Cat, everything is ok// if it returns a Dog, we have a problems// if it returns a Pet, we have a problem.val walked: Cat = mySitter.walk()

31

Page 32: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Function Inputs

VARIANCE

val p1: Petval p2: Petf(p1, p2)

def f(a: Pet, b: Pet): Pet ✅

def f(a: Animal, b: Pet): Pet ✅

def f(a: Any, b: Pet): Pet ✅

def f(a: Any, b: Any): Pet ✅

def f(a: Cat, b: Dog): Pet ❎

Function2[-T1, -T2, R]

32

Animal

Pet

DogCat

Page 33: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Function Outputs

VARIANCE

val p1: Petval p2: Pet

val r: Animal = f(p1, p2)

def f(a: Pet, b: Pet): Animal ✅

def f(a: Pet, b: Pet): Pet ✅

def f(a: Pet, b: Pet): Cat ✅

def f(a: Pet, b: Pet): Any ❎

Function2[-T1, -T2, +R]

33

Animal

Pet

DogCat

Page 34: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

When to Use

VARIANCE

• Covariance

o Containers

o Producers

o Representing inputs

• Contravariance

o Consumers

o Processors or Visitors

o Representing outputs

• Invariance

o Distinct types

o Markers or Labels

o Influence inference34

Page 35: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Constraints and Typeclasses

35

Page 36: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Upper and Lower Bounds

CONSTRAINTS AND TYPECLASSES

• Constrain the valid types

• Upper bound – parameter is same or subtype

o def pet[P <: Pet](animal: P): Unit = ???

• Lower bound – parameter is same or supertype

o def pre[B >: A](x: B, xs: List[A]): List[B] = ???

Page 37: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Bounds and Variance

CONSTRAINTS AND TYPECLASSES

abstract class List[+A] { ... }

case object Nil extends List[Nothing] { ... }

case class Cons[B] (elem: B, tail: List[B])

extends List[B] { ... }

37

Page 38: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Bounds and Variance

CONSTRAINTS AND TYPECLASSES

abstract class List[+A] {

def prepend(elem: A): List[A] = ???

}

case object Nil extends List[Nothing] { ... }

case class Cons[B] (elem: B, tail: List[B])

extends List[B] { ... }

38

Page 39: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Bounds and Variance

CONSTRAINTS AND TYPECLASSES

abstract class List[+A] {

def prepend(elem: A): List[A] = ???

}

case object Nil extends List[Nothing] { ... }

case class Cons[B] (elem: B, tail: List[B])

extends List[B] { ... }

39

covariant type A occurs in contravariant position in type A of value elem

Page 40: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Bounds and Variance

CONSTRAINTS AND TYPECLASSES

abstract class List[+A] {

def prepend[B](elem: B): List[B] =

new Cons(elem, this)

}

case object Nil extends List[Nothing] { ... }

case class Cons[B] (elem: B, tail: List[B])

extends List[B] { ... }

40

Page 41: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Bounds and Variance

CONSTRAINTS AND TYPECLASSES

abstract class List[+A] {

def prepend[B](elem: B): List[B] =

new Cons(elem, this)

}

case object Nil extends List[Nothing] { ... }

case class Cons[B] (elem: B, tail: List[B])

extends List[B] { ... }

41

type mismatch; found: List[A] required: List[B]

Page 42: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Bounds and Variance

CONSTRAINTS AND TYPECLASSES

abstract class List[+A] {

def prepend[B >: A] (elem: B): List[B] = ???

}

case object Nil extends List[Nothing] { ... }

case class Cons[B] (elem: B, tail: List[B])

extends List[B] { ... }

42

Page 43: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Change to a Wider Type

CONSTRAINTS AND TYPECLASSES

scala> List(1, 2, 3, 4, 5)res0: List[Int] = List(1, 2, 3, 4, 5)

scala> 0 :: res0res1: List[Int] = List(0, 1, 2, 3, 4, 5)

scala> 1.5 :: res0res2: List[AnyVal] = List(1.5, 1, 2, 3, 4, 5)

scala> res2(0)res3: AnyVal = 1.5

scala> res2(1)res4: AnyVal = 1

43

Page 44: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

View Bounds

CONSTRAINTS AND TYPECLASSES

• Deprecated from 2.11

• [ A <% B ]

• Indicates A is convertable to B by implicit conversion

• Introduces evidence parameter

o def view[AA <% A](x: AA): A = ???

o def view$[AA](x: AA)(implicit ev1$: AA => A) = ???

44

Page 45: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Context Bounds

CONSTRAINTS AND TYPECLASSES

• [ A : Ctx ]

• Indicates A has some context Ctx

o There exists a Ctx[A] in implicit scope

• Introduces an evidence parameter

o def ctxBound[X: M](x: X) = ???

o def ctxBound[X](x: X)(implicit ev1$: M[X]) = ???

• Access the context with the implicitly keyword

o val m = implicitly[M[X]]

o m.someMethod()

45

Page 46: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Typeclass Pattern

CONSTRAINTS AND TYPECLASSES

• External implementation of an interface

o No need to extend directly

o Possible without access to class source

• Introduce via a context bound

• Implement in terms of of other instances

o Adder[Pair[Int]] defined in terms of Adder[Int]

46

Page 47: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Typeclass Pattern

CONSTRAINTS AND TYPECLASSES

• Define an interface

• Implement for your class

• Introduce to implicit scope

• Pass to function as implicit parameter OR as type constraint

• Call methods from the interface

47

Page 48: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

TypeclassExample

CONSTRAINTS AND TYPECLASSES

trait Adder[A] {def add(x: A, y: A): A

}

implicit val IntAdder = new Adder[Int] {override def add(x: Int, y: Int): Int = x + y

}

def addThings[T: Adder](x: T, y: T): T = {implicitly[Adder[T]].add(x, y)

}

addThings(5, 7)

48

Page 49: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Inductive TypeclassDefinition

case class Pair[T](fst: T, snd: T)

implicit def pairAdder[T: Adder] = new Adder[Pair[T]] {override def add(x: Pair[T], y: Pair[T]): Pair[T] = {val ta = implicitly[Adder[T]]Pair(ta.add(x.fst, y.fst),

ta.add(x.snd, y.snd)) }

}

49

Page 50: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Dotty / Scala 3

DOTTY

Page 51: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Dotty Changes

DOTTY

• “Scala 3 is fundamentally the same language as Scala 2”

• Variance and type bounds still exist – essentially the same

o Current dotc slightly different error messages

• No existential types (but other constructs instead)

• Structural types – different implementation

Page 52: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

Takeaways• Variance

o Substitution

o Arises naturally as a consequence of Liskov Substitution

o Deeply entwined with subclassing in higher-kinded types

• Bounds

o Interact with variance -> widening

o Interact with implicits -> typeclasses

52

Page 53: Copyright 2018 Tendril, Inc. All rights reserved....Created Date: 6/21/2018 3:08:38 PM

Copyright 2018 Tendril, Inc. All rights reserved.

@CJPhelpsgithub.com/chrisphelps/type-parameter-power-up