Skip to content

Instantly share code, notes, and snippets.

@dwijnand
Last active May 19, 2021 11:48
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 dwijnand/d33436cf197daa15216b3cd35d03ba1c to your computer and use it in GitHub Desktop.
Save dwijnand/d33436cf197daa15216b3cd35d03ba1c to your computer and use it in GitHub Desktop.
object NumProblem:
opaque type Pos <: Int = Int
opaque type Neg <: Int = Int
object Pos { def unapply(x: Int): Option[Pos] = if (x > 0) Some(x) else None }
object Neg { def unapply(x: Int): Option[Neg] = if (x < 0) Some(x) else None }
(n: Int) match
case 0 =>
case Pos(x) =>
case Neg(x) =>
end NumProblem
object PermissionProblem:
opaque type Permission = Int
val Read: Permission = 1
val Write: Permission = 2
(p: Permission) match
case Read =>
case Write =>
end PermissionProblem
object ParamClauseProblem:
type ParamClause = List[ValDef] | List[TypeDef]
object ValDefs { def unapply(pc: ParamClause): Option[List[ ValDef]] = /* empty list and all lists of ValDefs */ }
object TypeDefs { def unapply(pc: ParamClause): Option[List[TypeDef]] = /* non-empty lists of TypeDefs */ }
(pc: ParamClause) match
case ValDefs(vdefs) =>
case TypeDefs(tdefs) =>
end ParamClauseProblem
object LazyListProblem:
object #:: {
def unapply[A](xs: LazyList[A]): Option[(A, LazyList[A])] = if (s.isEmpty) None else Some((s.head, s.tail))
}
(_: LazyList[Int]) match {
case head #:: tail =>
case LazyList() =>
}
end LazyListProblem
object OptProblem: self =>
final class Opt[+A] private(private val value: A) extends AnyVal {
def isEmpty = value == null
def get = if (isEmpty) throw new NoSuchElementException("empty Opt") else value
}
object Opt:
val Empty: Opt[Nothing] = new Opt[Nothing](null)
def unapply[A](x: Opt[A]): Opt[A] = x
end Opt
(s: Opt[String]) match
case Opt(str) =>
case Opt.Empty =>
end OptProblem
object PeanoProblem:
trait Peano:
type Nat
type Zero <: Nat
type Succ <: Nat
val Zero: Zero
val Succ: SuccModule
trait SuccModule:
def apply(nat: Nat): Succ
def unapply(succ: Succ): Some[Nat]
given TypeTest[Nat, Zero]
given TypeTest[Nat, Succ]
end Peano
object PeanoInt extends Peano:
// types:
type Nat = Int
type Zero = Int
type Succ = Int
// givens:
given TypeTest[Nat, Zero] = (n: Nat) => if x == 0 then Some(x) else None
given TypeTest[Nat, Succ] = (n: Nat) => if x > 0 then Some(x) else None
// values:
val Zero: Zero = 0
val Succ: SuccModule = new:
def apply(nat: Nat): Succ = nat + 1
def unapply(succ: Succ): Some[Nat] = Some(succ - 1)
end PeanoInt
def app(peano: Peano): Unit =
import peano._
def divOpt(m: Nat, n: Nat): Option[(Nat, Nat)] = n match
case Zero => None
case s @ Succ(_) => Some(safeDiv(m, s))
end PeanoProblem
object CharFamilyProblem:
object DigitChar { def unapply(ch: Char): Option[Char] = if (isDigit(ch)) Some(ch) else None }
object Whitespace { def unapply(ch: Char): Option[Char] = if (isWhitespace(ch)) Some(ch) else None }
object OtherChar { def unapply(ch: Char): Option[Char] = if (!isDigit(ch) && !isWhitespace(ch)) Some(ch) else None }
(ch: Char) match
case DigitChar(ch) =>
case Whitespace(ch) =>
case OtherChar(ch) =>
end CharFamilyProblem
object IdCharProblem:
object UpperChar { def unapply(ch: Char): Option[Char] = if (isUpperCase(ch)) Some(ch) else None }
object LowerChar { def unapply(ch: Char): Option[Char] = if (isLowerCase(ch)) Some(ch) else None }
(ch: Char) match
case UpperChar(ch) =>
case LowerChar(ch) =>
case nonIdChar =>
end IdCharProblem
object InitLastProblem:
object :+ {
def unapply[A, CC[X] <: Seq[X], C <: SeqOps[A, CC, C]](t: C with SeqOps[A, CC, C]): Option[(C, A)] =
if (t.isEmpty) None
else Some(t.init -> t.last)
}
(xs: Seq[A]) match
case init :+ last =>
case Nil =>
end InitLastProblem
object NumProblem:
sealed type Num = Int {
case 0 => val Zero
case n if n > 0 => type Pos
case _ => type Neg
}
type Num = Int
val Zero: Num = 0
opaque type Pos <: Num = Num
opaque type Neg <: Num = Num
extension (n: Num):
def ordinal: Int = (n: Int) match { case 0 => 0 case n if n > 0 => 1 case _ => 2 }
given TypeTest[Int, Zero.type] = (n: Int) => if ((n: Num).ordinal == 0) Some(n) else None
given TypeTest[Int, Pos] = (n: Int) => if ((n: Num).ordinal == 1) Some(n) else None
given TypeTest[Int, Neg] = (n: Int) => if ((n: Num).ordinal == 2) Some(n) else None
given TypeTest[Int, Num] = (n: Int) => Some(n)
given [T <: Num](using t: TypeTest[Int, T]): TypeTest[Num, T] = (n: Num) => t.unapply(n)
object Pos { def unapply(x: Pos): PosExtractor = new PosExtractor(x) }
object Neg { def unapply(x: Neg): NegExtractor = new NegExtractor(x) }
class PosExtractor(private val x: Pos) extends AnyVal { def isEmpty: false = false ; def get = x }
class NegExtractor(private val x: Neg) extends AnyVal { def isEmpty: false = false ; def get = x }
(n: Int) match
case 0 =>
case Pos(x) =>
case Neg(x) =>
end NumProblem
object PermissionProblem:
sealed opaque type Permission = Int {
case 1 => val Read
case 2 => val Write
}
opaque type Permission = Int
val Read: Permission = 1
val Write: Permission = 2
extension (p: Permission):
def ordinal: Int = (p: Int) match { case 1 => 0 case 2 => 1 case _ => -1 }
given TypeTest[Int, Read.type] = (n: Int) => if ((n: Permission).ordinal == 0) Some(n) else None
given TypeTest[Int, Write.type] = (n: Int) => if ((n: Permission).ordinal == 1) Some(n) else None
given TypeTest[Int, Permission] = (n: Int) => if ((n: Permission).ordinal == -1) None else Some(n)
given [T <: Permission](using t: TypeTest[Int, T]): TypeTest[Permission, T] = (p: Permission) => t.unapply(p)
(p: Permission) match
case Read =>
case Write =>
end PermissionProblem
object ParamClauseProblem:
sealed type ParamClause = List[ValDef] | List[TypeDef] {
case Nil | ((_: ValDef) :: _) => type ValDefs
case (_: TypeDef) :: _ => type TypeDefs
}
type ParamClause = List[ValDef] | List[TypeDef]
opaque type ValDefs <: ParamClause = ParamClause
opaque type TypeDefs <: ParamClause = ParamClause
extension (pc: ParamClause):
def ordinal: Int = (pc: (List[ValDef] | List[TypeDef])) match {
case Nil | ((_: ValDef) :: _) => 0
case (_: TypeDef) :: _ => 1
}
given TypeTest[List[ValDef] | List[TypeDef], ValDefs] = x => if ((x: ParamClause).ordinal == 0) Some(n) else None
given TypeTest[List[ValDef] | List[TypeDef], TypeDefs] = x => if ((x: ParamClause).ordinal == 1) Some(n) else None
given TypeTest[List[ValDef] | List[TypeDef], ParamClause] = x => Some(x)
given [T <: ParamClause](using t: TypeTest[List[ValDef] | List[TypeDef], T]): TypeTest[ParamClause, T] = x => t.unapply(x)
object ValDefs { def unapply(vd: ValDefs): ValDefsExtractor = new ValDefsExtractor(vd) }
object TypeDefs { def unapply(td: TypeDefs): TypeDefsExtractor = new TypeDefsExtractor(td) }
class ValDefsExtractor(private val x: ValDefs) extends AnyVal { def isEmpty: false = false ; def get = x }
class TypeDefsExtractor(private val x: TypeDefs) extends AnyVal { def isEmpty: false = false ; def get = x }
(pc: ParamClause) match
case ValDefs(vdefs) =>
case TypeDefs(tdefs) =>
end ParamClauseProblem
object LazyListProblem:
sealed type LL[+A] = LazyList[A] {
case xs if xs.isEmpty => type LazyNil
case _ => type LazyCons[+A]
}
type LL[+A] = LazyList[A]
opaque type LazyNil <: LL[Nothing] = LL[Nothing]
opaque type LazyCons[+A] <: LL[A] = LL[A]
extension [A](xs: LL[A]):
def ordinal: Int = (xs: LazyList[A]) match
case xs if xs.isEmpty => 0
case _ => 1
given [A]: TypeTest[LazyList[A], LazyNil] = (xs: LazyList[A]) => if ((xs: LL[A]).ordinal == 0) Some(xs) else None
given [A]: TypeTest[LazyList[A], LazyCons[A]] = (xs: LazyList[A]) => if ((xs: LL[A]).ordinal == 1) Some(xs) else None
given [A]: TypeTest[LazyList[A], LL[A]] = (xs: LazyList[A]) => Some(xs)
given [A, T <: LL[A]](using t: TypeTest[LazyList[A], T]): TypeTest[LL[A], T] = (xs: LL[A]) => t.unapply(xs)
object #:: {
def unapply[A](xs: LazyCons[A]): (A, LazyList[A]) = (xs._1, xs._2)
}
(_: LazyList[Int]) match {
case head #:: tail =>
case LazyList() =>
}
end LazyListProblem
object OptProblem: self =>
sealed opaque type Opt[+A] = A {
case null => val Empty
case _ => type Value[+A]
}
opaque type Opt[+A] = A
val Empty: Opt[Nothing] = null
opaque type Value[+A] <: Opt[A] = Opt[A]
extension [A](x: Opt[A]):
def ordinal: Int = (x: A) match { case null => 0 case _ => 1 }
given TypeTest[A, Empty.type] = (x: A) => if ((x: Opt[A]).ordinal == 0) Some(x) else None
given TypeTest[A, Value[A]] = (x: A) => if ((x: Opt[A]).ordinal == 1) Some(x) else None
given TypeTest[A, Opt[A]] = (x: A) => Some(x)
given [A, T <: Opt[A]](using t: TypeTest[A, T]): TypeTest[Opt[A], T] = (x: Opt[A]) => t.unapply(x)
object Opt:
val Empty: Opt[Nothing] = self.Empty
def unapply[A](x: Value[A]): Value[A] = x
extension [A](x: Value[A]):
def isEmpty: false = false
def get: A = x
end Opt
(s: Opt[String]) match
case Opt(str) =>
case Opt.Empty =>
end OptProblem
object PeanoProblem:
trait Peano:
sealed type Nat
type Zero <: Nat
type Succ <: Nat
val Zero: Zero
val Succ: SuccModule
trait SuccModule:
def apply(nat: Nat): Succ
def unapply(succ: Succ): Some[Nat]
given TypeTest[Nat, Zero]
given TypeTest[Nat, Succ]
end Peano
object PeanoInt extends Peano:
sealed opaque type Nat = Int {
case 0 => type Zero
case n if n > 0 => type Succ
}
// types:
opaque type Nat = Int
opaque type Zero <: Nat = Nat
opaque type Succ <: Nat = Nat
extension (n: Nat):
def ordinal: Int = (n: Int) match { case 0 => 0 case n if n > 0 => 1 case _ => -1 }
// givens:
given TypeTest[Int, Zero] = (n: Int) => if ((n: Nat).ordinal == 0) Some(n) else None
given TypeTest[Int, Succ] = (n: Int) => if ((n: Nat).ordinal == 1) Some(n) else None
given TypeTest[Int, Nat] = (n: Int) => if ((n: Nat).ordinal == -1) None else Some(x)
given [T <: Nat](using t: TypeTest[Int, T]): TypeTest[Nat, T] = (n: Nat) => t.unapply(n)
// values:
val Zero: Zero = 0
val Succ: SuccModule = new:
def apply(nat: Nat): Succ = nat + 1
def unapply(succ: Succ): Some[Nat] = Some(succ - 1)
end PeanoInt
def app(peano: Peano): Unit =
import peano._
def divOpt(m: Nat, n: Nat): Option[(Nat, Nat)] = n match
case Zero => None
case s @ Succ(_) => Some(safeDiv(m, s))
end PeanoProblem
object CharFamilyProblem:
sealed opaque type CharFamily <: Char = Char {
case ch if isDigit(ch) => type DigitChar
case ch if isWhitespace(ch) => type Whitespace
case _ => type OtherChar
}
opaque type CharFamily <: Char = Char
opaque type DigitChar <: CharFamily = CharFamily
opaque type Whitespace <: CharFamily = CharFamily
opaque type OtherChar <: CharFamily = CharFamily
extension (ch: CharFamily):
def ordinal: Int = (ch: Char) match
case ch if isDigit(ch) => 0
case ch if isWhitespace(ch) => 1
case _ => 2
given TypeTest[Char, DigitChar] = (ch: Char) => if ((ch: CharFamily).ordinal == 0) Some(ch) else None
given TypeTest[Char, Whitespace] = (ch: Char) => if ((ch: CharFamily).ordinal == 1) Some(ch) else None
given TypeTest[Char, OtherChar] = (ch: Char) => if ((ch: CharFamily).ordinal == 2) Some(ch) else None
given TypeTest[Char, CharFamily] = (ch: Char) => Some(ch)
given [T <: CharFamily](using t: TypeTest[Char, T]): TypeTest[CharFamily, T] = (ch: CharFamily) => t.unapply(ch)
object DigitChar { def unapply(ch: DigitChar): DigitCharExtractor = new DigitCharExtractor(ch) }
object Whitespace { def unapply(ch: Whitespace): WhitespaceExtractor = new WhitespaceExtractor(ch) }
object OtherChar { def unapply(ch: OtherChar): OtherCharExtractor = new OtherCharExtractor(ch) }
class DigitCharExtractor(private val x: DigitChar) extends AnyVal { def isEmpty: false = false ; def get = x }
class WhitespaceExtractor(private val x: Whitespace) extends AnyVal { def isEmpty: false = false ; def get = x }
class OtherCharExtractor(private val x: OtherChar) extends AnyVal { def isEmpty: false = false ; def get = x }
(ch: Char) match
case DigitChar(ch) =>
case Whitespace(ch) =>
case OtherChar(ch) =>
(ch: Char) match // missing Whitespace
case DigitChar(ch) =>
case OtherChar(ch) =>
end CharFamilyProblem
object IdCharProblem:
sealed opaque type IdChar = Char {
case ch if isUpperCase(ch) => type UpperChar
case ch if isLowerCase(ch) => type LowerChar
}
opaque type IdChar = Char
opaque type UpperChar <: IdChar = IdChar
opaque type LowerChar <: IdChar = IdChar
extension (ch: IdChar):
def ordinal: Int = (ch: Char) match
case ch if isUpperCase(ch) => 0
case ch if isLowerCase(ch) => 1
case _ => -1
given TypeTest[Char, UpperChar] = (ch: Char) => if ((ch: IdChar).ordinal == 0) Some(ch) else None
given TypeTest[Char, LowerChar] = (ch: Char) => if ((ch: IdChar).ordinal == 1) Some(ch) else None
given TypeTest[Char, IdChar] = (ch: Char) => if ((ch: IdChar).ordinal == -1) None else Some(ch)
given [T <: IdChar] (using t: TypeTest[Char, T]): TypeTest[IdChar, T] = (ch: IdChar) => t.unapply(ch)
object UpperChar { def unapply(ch: UpperChar): UpperChar = ch }
object LowerChar { def unapply(ch: LowerChar): UpperChar = ch }
class UpperCharExtractor(private val x: UpperChar) extends AnyVal { def isEmpty: false = false ; def get = x }
class LowerCharExtractor(private val x: LowerChar) extends AnyVal { def isEmpty: false = false ; def get = x }
(ch: Char) match
case UpperChar(ch) =>
case LowerChar(ch) =>
case nonIdChar =>
(ch: IdChar) match
case upperChar: UpperChar =>
case lowerchar: LowerChar =>
(ch: Char) match
case idChar: IdChar =>
case nonIdChar =>
(ch: Char) match
case upperChar: UpperChar =>
case lowerChar: LowerChar =>
case nonIdChar =>
end IdCharProblem
object InitLastProblem:
sealed type InitLastList[A] = [CC[X] <: Seq[X], C <: SeqOps[A, CC, C]] =>> C with SeqOps[A, CC, C] {
case xs if xs.isEmpty => type InitLastNil
case xs => type InitLast
}
type InitLastList[A] = [CC[X] <: Seq[X], C <: SeqOps[A, CC, C]] =>> C with SeqOps[A, CC, C]
opaque type InitLastNil[A] <: InitLastList[A] = InitLastList[A]
opaque type InitLast[A] <: InitLastList[A] = InitLastList[A]
extension [A](xs: InitLastList[A]):
def ordinal: Int = (xs: ([CC[X] <: Seq[X], C <: SeqOps[A, CC, C]] =>> C with SeqOps[A, CC, C])) match {
case xs if xs.isEmpty => 0
case _ => 1
}
given [A] TypeTest[[CC[X] <: Seq[X], C <: SeqOps[A, CC, C]] =>> C with SeqOps[A, CC, C], InitLastNil] =
xs => if ((xs: InitLastList[A]).ordinal == 0) Some(xs) else None
given [A] TypeTest[[CC[X] <: Seq[X], C <: SeqOps[A, CC, C]] =>> C with SeqOps[A, CC, C], InitLast] =
xs => if ((xs: InitLastList[A]).ordinal == 1) Some(xs) else None
given [A] TypeTest[[CC[X] <: Seq[X], C <: SeqOps[A, CC, C]] =>> C with SeqOps[A, CC, C], InitLastList[A]] =
xs => Some(xs)
given [A, T[X] <: InitLastList[X]] (using t: TypeTest[[CC[X] <: Seq[X], C <: SeqOps[A, CC, C]] =>> C with SeqOps[A, CC, C], T]): TypeTest[InitLastList, T] =
xs => t.unapply(xs)
object :+ {
def unapply[A](xs: InitLast[A]): (Seq[A], A) = (xs.init, xs.last)
}
(xs: Seq[A]) match
case init :+ last =>
case Nil =>
(xs: Seq[A]) match
case init :+ last => // missing InitLastNil
extension [A](xs: InitLast[A]):
def _1: Seq[A] = xs.init
def _2: A = xs.last
object :+ :
def unapply[A](xs: InitLast[A]): InitLast[A] = xs
extension [A](xs: InitLast[A]):
def safeInit: Seq[A] = xs.init
def safeLast: A = xs.last
object :+ :
def unapply[A](xs: InitLast[A]): (Seq[A], A) = (xs.safeInit, xs.safeLast)
end InitLastProblem
/** This file demonstrates the alternative design that uses the annotation `@complete`.
*/
import scala.reflect.TypeTest
class complete[T <: Tuple] extends scala.annotation.Annotation
object numbers {
opaque type Nat <: Int = Int
opaque type Neg <: Int = Int
@complete[(Nat, Neg)]
type Num = Int
given TypeTest[Int, Neg] = (n: Int) => if (n < 0) Some(n) else None
given TypeTest[Int, Nat] = (n: Int) => if (n >= 0) Some(n) else None
object Nat:
def unapply(x: Nat): Some[Nat] = Some(x)
def apply(x: Int): Nat =
assert(x >= 0)
x
object Neg:
def unapply(x: Neg): Some[Neg] = Some(x)
def apply(x: Int): Neg =
assert(x < 0)
x
}
object params {
class ValDef
class TypeDef
opaque type ValDefs <: List[ValDef] = List[ValDef]
opaque type TypeDefs <: List[TypeDef] = List[TypeDef]
@complete[(ValDefs, TypeDefs)]
type ParamClause = List[ValDef] | List[TypeDef]
given TypeTest[ParamClause, ValDefs] =
(l: ParamClause) => if l.isEmpty || l.head.isInstanceOf[ValDef] then Some(l.asInstanceOf[l.type & ValDefs]) else None
given TypeTest[ParamClause, TypeDefs] =
(l: ParamClause) => if l.nonEmpty && l.head.isInstanceOf[TypeDef] then Some(l.asInstanceOf[l.type & TypeDefs]) else None
object ValDefs:
def unapply(pc: ValDefs): Option[ValDefs] = Some(pc)
def apply(vals: List[ValDef]): ValDefs = vals
object TypeDefs:
def unapply(pc: TypeDefs): Option[TypeDefs] = Some(pc)
def apply(tdefs: List[TypeDef]): TypeDefs =
assert(tdefs.nonEmpty)
tdefs
}
class Test {
import numbers._
def foo(n: Num) =
n match
case x: Neg =>
case x: Nat =>
import params._
def f(pc: ParamClause) =
pc match
case ValDefs(vs) => ()
case TypeDefs(ts) => ()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment