traits

Traits: a default method, impls for a record and for Int, bounded generics.

examples/traits/traits.dawn

# Traits: a default method, impls for a record and for Int, bounded generics.
#
# Also derive Ord, the operators bridging to it, and the Ord-bounded list
# builtins (docs/trait.md §6).
#
# Run: dawn run examples/traits/traits.dawn

trait Describe[T] {
  fn describe(x: T) -> String
  fn shout(x: T) -> String = describe(x) ++ "!"
}

type Card = { rank: Int, name: String } derive Show, Ord

impl Describe[Card] {
  fn describe(c: Card) -> String = "${c.name} (rank ${c.rank})"
}

impl Describe[Int] {
  fn describe(n: Int) -> String = "the number $n"
}

## The greatest element, described — Ord orders it, Describe renders it.
fn best_of[T: Ord + Describe](xs: List[T]) -> Option[String] =
  match max(xs) {
    Some(x) -> Some(shout(x))
    None -> None
  }

fn hand() -> List[Card] = [
  Card { rank: 3, name: "queen" },
  Card { rank: 1, name: "pawn" },
  Card { rank: 2, name: "rook" },
]

pub fn main() -> Unit !io = {
  # derive Ord: rank first (field order), so sorting works out of the box
  let sorted = sort(hand())
  println("${map(sorted, c => c.name)}")

  # operators bridge to the derived cmp
  let pawn = Card { rank: 1, name: "pawn" }
  let queen = Card { rank: 3, name: "queen" }
  println("${pawn < queen} ${queen <= pawn}")

  match best_of(hand()) {
    Some(s) -> println(s)
    None -> println("empty hand")
  }
  match best_of([4, 8, 6]) {
    Some(s) -> println(s)
    None -> println("empty")
  }
}

test "default methods and UFCS" {
  let c = Card { rank: 9, name: "king" }
  assert c.describe() == "king (rank 9)"
  assert c.shout() == "king (rank 9)!"
  assert shout(7) == "the number 7!"
}

test "sorting and extremes" {
  let ranks = map(sort(hand()), c => c.rank)
  assert ranks == [1, 2, 3]
  assert sort_by([3, 1, 2], (a, b) => b - a) == [3, 2, 1]
  assert min([9, 2, 5]) == Some(2)
  assert max_by(hand(), c => c.rank) == Some(Card { rank: 3, name: "queen" })
}

test "prelude cmp on scalars" {
  assert cmp(1, 2) < 0
  assert cmp("b", "a") > 0
  # `cmp(1.5, 1.5)` does not compile: Float has no Ord. NaN is unordered
  # against everything including itself, so there is no total order to give,
  # and `Ord` is what `sort` and `derive Ord` rest on. The operators still
  # work and still mean IEEE:
  assert 1.5 < 2.5
  assert not (1.5 < 1.5)
}