Last active
November 10, 2023 18:35
-
-
Save johnhungerford/b3ccf0f46783db765688dba19bfba4a3 to your computer and use it in GitHub Desktop.
Zero overhead Option in Scala 3
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
//> using scala 3.3.0 | |
import scala.util.{Try, Success, Failure} | |
import scala.reflect.Typeable | |
import scala.util.NotGiven | |
import scala.annotation.implicitNotFound | |
type Opt[T] = Opt.Opt[T] | |
object Opt: | |
opaque type Opt[+T] = T | Null | |
def apply[T](value: T): Opt[T] = value | |
val None: Opt[Nothing] = null | |
object Some: | |
def apply[T](value: T): Opt[T] = value | |
extension [T](nullable: Opt[T]) | |
def isEmpty: Boolean = nullable == null | |
def isDefined: Boolean = !isEmpty | |
def nonEmpty: Boolean = isDefined | |
def fold[U](default: => U)(f: T => U): U = | |
if nullable == null then default | |
else f(nullable.asInstanceOf[T]) | |
def contains(t: T): Boolean = fold(false)(_ == t) | |
def exists(f: T => Boolean): Boolean = fold(false)(f) | |
def flatten[U](using hasInnerOpt: T =:= Opt[U]): Opt[U] = | |
fold(None)(t => hasInnerOpt(t)) | |
def toStringTagged: String = | |
fold("None")(v => s"Some($v)") | |
def map[U](f: T => U): Opt[U] = | |
flatMap(f) | |
def flatMap[U](f: T => Opt[U]): Opt[U] = | |
if nullable == null then null else f(nullable.asInstanceOf[T]) | |
def zip[U](other: => Opt[U]): Opt[(T, U)] = | |
flatMap(t => other.flatMap(u => t -> u)) | |
def getOrElse[U >: T](u: => U): U = | |
if nullable == null then u else nullable.asInstanceOf[T] | |
def get : T = | |
nullable.getOrElse(throw new NullPointerException()) | |
def toOption: Option[T] = fold(scala.None)(scala.Some.apply) | |
def toRight[L](left: => L): Either[L, T] = | |
if nullable == null then Left(left) else Right(nullable.asInstanceOf[T]) | |
def toLeft[R](right: => R): Either[T, R] = | |
if nullable == null then Right(right) else Left(nullable.asInstanceOf[T]) | |
def toList: List[T] = fold(Nil)(_ :: Nil) | |
def toSet: Set[T] = fold(Set.empty[T])(t => Set(t)) | |
def toMap[K, V](using T =:= (K, V)): Map[K, V] = | |
fold(Map.empty)(tup => Map(tup)) | |
def toVector: Vector[T] = fold(Vector.empty)(t => Vector(t)) | |
object syntax: | |
extension[L, R](either: Either[L, R]) | |
def toOpt: Opt[R] = either match | |
case Left(_) => null | |
case Right(value) => value | |
def toOptLeft: Opt[L] = either match | |
case Left(value) => value | |
case Right(_) => null | |
extension[T](_try: Try[T]) | |
def toOpt: Opt[T] = _try match | |
case Success(value) => value | |
case Failure(_) => null | |
def toOptFail: Opt[Throwable] = _try match | |
case Success(_) => null | |
case Failure(err) => err | |
extension[T](option: Option[T]) | |
def toOpt: Opt[T] = option.fold(Opt.None)(Opt.apply) | |
object Main: | |
def main(args: Array[String]): Unit = | |
import Opt.syntax.* | |
val a : Opt[String] = Opt("hi") | |
val b : Opt[String] = Opt(null) | |
val c : Opt[String] = Opt("what") | |
val d : Opt[String] = Right("hello").toOpt | |
val e : Opt[String] = Left('c').toOpt | |
println(a.toStringTagged) | |
println(b.toStringTagged) | |
println(c.toStringTagged) | |
println(d.toStringTagged) | |
println(e.toStringTagged) | |
val res1 = for { | |
aVal <- a | |
bVal <- b | |
} yield aVal + aVal | |
val res2 = for { | |
aVal <- a | |
cVal <- c | |
} yield aVal + cVal | |
println(res1.toStringTagged) | |
println(res2.toStringTagged) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment