Felix Mulder
def spiritOfDoing(you: Programmer, client: DomainExpert): Task[Joy] =
for {
domain <- problemDescription(client)
goals <- you.understand(domain)
solution <- goals.attempt
conclusion <- conclusion match {
case client.Satisfied(conclusion) => solution.pure[Task] // Done!
case lessonsLearned => spiritOfDoing(you + lessonsLearned,
client + lessonsLearned)
}
} yield conclusion
def spiritOfWhatAmIDoing(you: Programmer,
client: DomainExpert): Task[Option[Joy]] =
for {
domain <- problemDescription(client)
_ = complainAboutThings(domain)
goals <- you.understand(domain)
_ = complainAboutThings(client)
solution <- goals.attempt
_ = complainAboutThings(solution)
conclusion <- conclusion match {
case client.Satisfied(conclusion) =>
complainAboutThings(BeingDoneFinally())
solution.pure[Task] // Done!
case ex @ you.RageQuit => Task.fail(ex)
case lessonsLearned =>
bitchAndMoan()
spiritOfDoing(you + lessonsLearned + you.pentUpAggression(solution),
client + lessonsLearned)
}
} yield conclusion
// 1 + 2 - 3 => Add(Lit(1), Add(Lit(2), Neg(Lit(3))))
trait Exp[T]
class Lit[T](t: T) extends Exp[T]
class Neg[T](t: T) extends Exp[T]
class Add[T](t1: T, t2: T)
trait Exp[T]
case class Lit[T](t: T) extends Exp[T]
case class Neg[T](t: T) extends Exp[T]
case class Add[T](t1: T, t2: T) extends Exp[T]
sealed trait Exp[T]
final case class Lit[T](t: T) extends Exp[T]
final case class Neg[T](t: T) extends Exp[T]
final case class Add[T](t1: T, t2: T) extends Exp[T]
enum Color {
case Red
case Green
case Blue
}
enum Color { case Red, Green, Blue }
sealed trait Exp[T]
final case class Lit[T](t: T) extends Exp[T]
final case class Neg[T](t: T) extends Exp[T]
final case class Add[T](t1: T, t2: T) extends Exp[T]
enum Exp[T] {
case Lit(t: T)
case Add(t1: T, t2: T)
case Neg(t: T)
}
enum Option[+T] {
case Some(t: T) // extends Option[T]
case None // extends Option[Nothing]
}
enum Option[+T] extends Serializable {
case Some(t: T)
case None
}
enum class Option[+T] extends Serializable
object Option {
case Some(t: T)
case None
def apply[T](t: T): Option[T] =
if (t != null) Some(t) else None
}
val x = Some(1) // x: Option[Int]
val y = new Some(1) // y: Option.Some[Int]
In any language design, the total time spent discussing a feature in this list is proportional to two raised to the power of its position.
- 0. Semantics
- 1. Syntax
- 2. Lexical syntax
- 3. Lexical syntax of comments
def foo[A <: AnyRef](a: A): a.type = a
trait Foo[A] { type X }
def bar[A <: AnyRef](a1: A)
(implicit a2: Foo[a1.type]): a2.X = a
trait Foo[A] { type X }
def bar[A <: AnyRef, B](a1: A)
(implicit a2: Foo[a1.type]{ type X = B },
a3: Foo[B]): a3.X = a
trait Foo[A] { type X }
def bar[A <: AnyRef, B, C](a1: A)
(implicit a2: Foo[a1.type]{ type X = B },
a3: Foo[B ]{ type X = C },
a3: Foo[C ]): a3.X = a
trait Foo[A] { type X }
def bar[A <: AnyRef, B, C, D](a1: A)
(implicit a2: Foo[a1.type]{ type X = B },
a3: Foo[B ]{ type X = C },
a4: Foo[C ]{ type X = D },
a5: Foo[D ]): a5.X = a
trait Foo[A] { type X }
type Aux[A, B0] = Foo[A] { type X = B0 }
def bar[A <: AnyRef, B](a1: A)
(implicit a2: Aux[a1.type, B],
a3: Foo[B]): a3.X = a
trait Foo[A] { type X }
type Aux[A, B0] = Foo[A] { type X = B0 }
def bar[A <: AnyRef, B](a1: A)
(implicit a2: Aux[a1.type, B],
a3: Foo[B]): a3.X = a
def bar[A <: AnyRef](a1: A)
(implicit a2: Foo[a1.type],
a3: Foo[a2.X]): a3.X = a
trait List[+A] {
def foldRight[B](z: B)(f: (A, B) => B): B = ???
}
List(1, 2, 3).foldRight(List.empty)(_ :: _) // scalac: nope
// dotty: yep
implicit A => B
def saveUser(u: User)(implicit dbx: DBContext): DBIO[User] = {
...
}
def saveUser(u: User): DBIO[User] = { dbx =>
...
}
def saveUser(u: User): DBIO[User] = { implicit dbx =>
...
}
type DBIO[T] = implicit DBContext => T
def saveUser(u: User): DBIO[User] = {
// here we have a DBContext implicitly available, yay!
...
}
def saveUser(u: User): DBIO[User] =
implicit ctx => u
trait Exp[T] {
def add(t1: T, t2: T): T
def lit(i: Int) = T
}
implicit val intExp: Exp[Int] = new Exp[Int] {
def add(t1: Int, t2: Int) = t1 + t2
def lit(i: Int) = i
}
// 8 + (2 + 2 + 2)
def expr1[T](implicit e: Exp[T]) =
e.add(e.lit(8), e.add(e.lit(2), e.add(e.lit(2), e.lit(2))))
object ExpSyntax {
def lit[T](i: Int)(implicit e: Exp[T]): T = e.lit(i)
def add[T](l: T, r: T)(implicit e: Exp[T]): T = e.add(l, r)
}
import ExpSyntax._
trait Exp[T] {
def add(t1: T, t2: T): T
def lit(i: Int) = T
}
implicit val intExp: Exp[Int] = new Exp[Int] {
def add(t1: Int, t2: Int) = t1 + t2
def lit(i: Int) = i
}
// 8 + (2 + 2 + 2)
def expr1[T: Exp] =
add(lit(8), add(lit(2), add(lit(2), lit(2))))
trait Mul[T] {
def mul(t1: T, t2: T): T
}
implicit val intMul: Mul[Int] = new Mul[Int] {
def mul(i1: Int, i2: Int) = i1 * i2
}
// 8 + 3 * 2
def expr2[T : Exp : Mul] =
add(lit(8), mul(lit(3), lit(2)))
type Ring[T] = implicit (Exp[T], Mul[T]) => T
// 8 + 3 * 2
def expr2: Ring[T] =
add(lit(8), mul(lit(3), lit(2)))
Revisiting Tagless Final Interpreters - Olivier Blanvillain