Picture.circle(100).fillColor(Color.red).draw()
object ExampleServer {
def run[F[_]: Async: Network]: F[Nothing] = // etc.
}
def scalar[Ui[_]: Controls: Layout](): Ui[(String, Boolean)] =
Layout[Ui].and(
Controls[Ui].text("What is your name?"),
Controls[Ui].choice(
"Are you enjoying Scalar?",
"Yes" -> true,
"Heck yes!" -> true
)
)
val consoleUi = scalar[Console]()
val (name, enjoyment) = consoleUi()
def scalar[Ui[_]: Controls: Layout](): Ui[(String, Boolean)] =
Layout[Ui].and(
Controls[Ui].text("What is your name?"),
Controls[Ui].choice(
"Are you enjoying Scalar?",
"Yes" -> true,
"Heck yes!" -> true
)
)
val consoleUi = scalar[Console]()
val (name, enjoyment) = consoleUi()
def scalar[Ui[_]: Controls: Layout](): Ui[(String, Boolean)] =
Layout[Ui].and(
Controls[Ui].text("What is your name?"),
Controls[Ui].choice(
"Are you enjoying Scalar?",
"Yes" -> true,
"Heck yes!" -> true
)
)
val consoleUi = scalar[Console]()
val (name, enjoyment) = consoleUi()
def scalar[Ui[_]: Controls: Layout](): Ui[(String, Boolean)] =
Layout[Ui].and(
Controls[Ui].text("What is your name?"),
Controls[Ui].choice(
"Are you enjoying Scalar?",
"Yes" -> true,
"Heck yes!" -> true
)
)
val consoleUi = scalar[Console]()
val (name, enjoyment) = consoleUi()
// String => Ui[String]
Controls[Ui].text("What is your name?"),
// (Ui[A], Ui[B]) => Ui[(A, B)]
Layout[Ui].and(firstUi, secondUi)
def scalar[Ui[_]: Controls: Layout]() = ...
def scalar[Ui[_]: Controls: Layout: Events]() = ...
val webUi = scalar[Dom]()
webUi.mountAtId("mount-point")
def scalar[Ui[_]: Controls: Layout]() =
Layout[Ui].and(
Controls[Ui].text("What is your name?"),
Controls[Ui].choice(
"Are you enjoying Scalar?",
"Yes" -> true,
"Heck yes!" -> true
)
)
Higher-kinded types, given values, using clauses, context bounds
val ui =
Controls
.text("What is your name?")
.and(
Controls.choice(
"Are you enjoying Scalar?",
Seq("Yes" -> true, "Heck yes!" -> true)
)
)
trait Algebra[Ui[_]]
trait Controls[Ui[_]] extends Algebra[Ui] {
def text(prompt: String): Ui[String]
// etc...
}
trait Layout[Ui[_]] extends Algebra[Ui] {
def and[A, B](t: Ui[A], b: Ui[B]): Ui[(A, B)]
}
// etc...
trait Algebra {
type Ui[_]
}
trait Controls extends Algebra {
def text(prompt: String): Ui[String]
// etc...
}
trait Layout extends Algebra {
def and[A, B](t: Ui[A], b: Ui[B]): Ui[(A, B)]
}
// etc...
def scalar[Ui[_]: Controls: Layout](): Ui[(String, Boolean)] =
Layout[Ui].and(
Controls[Ui].text("What is your name?"),
Controls[Ui].choice(
"Are you enjoying Scalar?",
"Yes" -> true,
"Heck yes!" -> true
)
)
def scalar()(
using alg: Controls & Layout
): alg.Ui[(String, Boolean)] =
alg.and(
alg.text("What is your name?"),
alg.choice(
"Are you enjoying Scalar?",
"Yes" -> true,
"Heck yes!" -> true
)
)
def scalar()(
using alg: Controls & Layout
): alg.Ui[(String, Boolean)] =
alg.and(
alg.text("What is your name?"),
alg.choice(
"Are you enjoying Scalar?",
"Yes" -> true,
"Heck yes!" -> true
)
)
trait Program[-Alg <: Algebra, A] {
def apply(using alg: Alg): alg.Ui[A]
}
val scalar =
Program[Controls & Layout, (String, Boolean)] {
def apply(using alg: Controls & Layout) =
alg.and(
alg.text("What is your name?"),
alg.choice(
"Are you enjoying Scalar?",
"Yes" -> true,
"Heck yes!" -> true
)
)
}
object Controls {
def text(prompt: String): Program[Controls, String] =
Program[Controls, String] {
def apply(using alg: Controls): alg.Ui[String] =
alg.text(prompt)
}
}
extension [Alg <: Algebra, A](p: Program[Alg, A]) {
def and[Alg2 <: Algebra, B](
second: Program[Alg2, B]
): Program[Alg & Alg2 & Layout, (A, B)] =
Program[Alg & Alg2 & Layout, (A, B)] {
def apply(using alg: Alg & Alg2 & Layout): alg.Ui[(A, B)] =
alg.and(p, second)
}
extension [Alg <: Algebra, A](p: Program[Alg, A]) {
def and[Alg2 <: Algebra, B](
second: Program[Alg2, B]
): Program[Alg & Alg2 & Layout, (A, B)] =
Program[Alg & Alg2 & Layout, (A, B)] {
def apply(using alg: Alg & Alg2 & Layout): alg.Ui[(A, B)] =
alg.and(p, second)
}
val c1: Program[Controls, String] = Controls.text(...)
val c2: Program[Controls, Boolean] = Controls.choice(...)
// Inferred type is
// Program[Controls & Layout, (String, Boolean)]
c1.and(c2)
val ui =
Controls
.text("What is your name?")
.and(
Controls.choice(
"Are you enjoying Scalar?",
Seq("Yes" -> true, "Heck yes!" -> true)
)
)
User code reads like normal code
Extend functionality for platform specific actions (e.g. mobile functionality like location)
Extend interpretations for different platforms (e.g. new UI toolkits)
object ExampleServer {
def run[F[_]: Async: Network]: F[Nothing] = // etc.
}
if we stop using abstractions that offer no value!
@noelwelsh.bsky.social
@noelwelsh@types.pl
noel@noelwelsh.com
Draft book at https://scalawithcats.com/