Last active
May 19, 2021 11:48
-
-
Save dwijnand/d33436cf197daa15216b3cd35d03ba1c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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