Skip to content

Instantly share code, notes, and snippets.

@lwilli
Last active June 12, 2021 00:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lwilli/7832369077c83734666675318c20e17b to your computer and use it in GitHub Desktop.
Save lwilli/7832369077c83734666675318c20e17b to your computer and use it in GitHub Desktop.
Scala Type Cheatsheet

Scala Type Cheatsheet

Type Bounds

T <: A declares that type variable T refers to a subtype of type A (or type A itself).

T >: A expresses that the type parameter T or the abstract type T refers to a supertype of type A (or type A itself).

In most cases, A will be the type parameter of the class and B will be the type parameter of a method.

Fancy Type Bound Example

def foo[T >: A with B <: C]()

says that T must be a super type of (or equal to) A with B and a subtype of (or equal to) C.

Detailed example

Here's a contrived, but hopefully more helpful example showing what works and doesn't work with type bounds like this:

trait Color
trait Warm extends Color
trait Red extends Warm
trait Neutral extends Color
trait Expensive
trait Gold extends Warm with Expensive

type WarmAndExpensiveSuper >: Warm with Expensive
type WarmAndExpensiveSuperColor = WarmAndExpensiveSuper with Color
type Exact >: Warm with Expensive <: Color

def foo[T >: Warm with Expensive <: Color]: T
// `>: Warm with Expensive` means `T` must be a super type of `Warm` or `Expensive` or both, or itself be of type `Warm with Expensive`
// `<: Color` means `T` must be a sub type of `Color`, or itself be of type `Color`

foo[Color] // ✅ Works because `Color` is a super type of `Warm` and `Color` is `Color`
foo[Warm] // ✅ Works because `Warm` is itself `Warm` and `Warm` is a sub type of `Color`
foo[Red] // ⛔️ Doesn't work because `Red` is neither a super type of `Warm` nor `Expensive`
foo[Neutral] // ⛔️ Doesn't work because `Neutral` is neither a super type of `Warm` nor `Expensive`
foo[Gold] // ⛔️ Doesn't work because `Gold` is neither a super type of `Warm` nor `Expensive` (it's a sub type of both)
foo[Expensive] // ⛔️ Doesn't work because `Expensive` is not a sub type of `Color`

foo[WarmAndExpensiveSuper] // ⛔️ Doesn't work because `WarmAndExpensiveSuper` is not a sub type of `Color` (it's some super type of either `Warm` or `Expensive` or both)
foo[WarmAndExpensiveSuperColor] // ✅ Works because it is `WarmAndExpensiveSuper`, which is a super type of both `Warm` and `Expensive`, and it is a sub type of `Color`  
foo[Exact] // ✅ Works because `Exact` matches the type bounds exactly

Unlike type bounds, a context bound of T: A is just syntactic sugar for an implicit parameter of type A[T]. This is typically used with the type class pattern.

Example

def foo[T: A](t : T)

is syntactic sugar for

def foo[T](t : T)(implicit val a: A[T])

A =:= B means A must be exactly B

A <:< B means A must be a subtype of B (analogous to the simple type constraint <:)

A <%< B (deprecated) means A must be viewable as B, possibly via implicit conversion (analogous to the deprecated view (type) bound <%)

Example

case class Foo[A](a: A) {
  // This method can only be used if this is a Foo[String]
  def getStringLength(implicit evidence: A =:= String) = a.length
}

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment