项目作者: tabdulradi

项目描述 :
Makes `A | Null` work with for-comprehensions
高级语言: Scala
项目地址: git://github.com/tabdulradi/nullable.git
创建时间: 2019-03-30T18:33:23Z
项目社区:https://github.com/tabdulradi/nullable

开源协议:Apache License 2.0

下载


Nullable

Enriches union types A | Null with an interface similar scala.Option making it usable inside for comprehensions

Usage

Add the following to your build.sbt

  1. scalacOptions += "-Yexplicit-nulls"
  2. libraryDependencies += "com.abdulradi" %% "nullable-core" % "0.4.0"

Features

Plays nice with for comprehensions

  1. import com.abdulradi.nullable.syntax.*
  2. val maybeA: Int | Null = 42
  3. val maybeB: String | Null = "foo"
  4. // Compiles fine
  5. for
  6. a <- maybeA
  7. yield 5
  8. // Also compiles, no null pointer exceptions
  9. for
  10. a <- maybeA
  11. b <- null
  12. yield ()
  13. // Compiles, and evaluates to null (because if condition doesn't match)
  14. for
  15. a <- maybeA
  16. if (a < 0)
  17. yield a

Familiar Option-like experience

  1. maybeA.map(_ => 5)
  2. maybeA.flatMap(_ => null)
  3. maybeA.flatMap(_ => maybeB)
  4. maybeA.fold(0)(_ + 1)
  5. maybeA.getOrElse(0)
  6. maybeA.contains(0)
  7. maybeA.exists(_ == 0)
  8. maybeA.toRight("Value was null")

Convert from/to Option

  1. val optA = Some(42)
  2. maybeA.toOption == optA
  3. maybeA == optA.orNull

Prevents auto flattening

While Option[Option[A]] is a valid type, the equivalent A | Null | Null is indistinguishable from A | Null. So, the library ensures that map and flatMap are used correctly used at compile time.

The following examples don’t compile

  1. for a <- maybeA yield maybeB // Shows a compile time error suggesting to use flatMap instead
  2. maybeA.flatMap(_ => 5) // Shows a compile time error suggesting message to use map instead
  3. maybeA.map(_ => null) // Shows a compile time error suggesting to use flatMap instead
  4. maybeA.map(_ => maybeB) // Shows a compile time error suggesting to use flatMap instead

Working with Generics

The following doesn’t compile, as we can’t prove A won’t be Null

  1. def useFlatMapWithNullableInScope[A](f: Int => A): A | Null =
  2. maybeA.flatMap(f)

Solution: Propagate a Nullable instance and let the library check at usage site

  1. def useFlatMapWithNullableInScope[A: Nullable](f: Int => A): A | Null =
  2. maybeA.flatMap(f)
  3. def useMapWithNotNullInScope[A: NotNull](f: Int => A): A | Null =
  4. maybeA.map(f)

Lightweight version

If you only care about for-comprehension features, but not the rest of Option-like methods, we also offer a lightweight syntax

  1. import com.abdulradi.nullable.forSyntax.*
  2. for
  3. a <- maybeA
  4. b <- maybeB
  5. res = a + b
  6. if (res % 2) == 0
  7. yield res

The rest of the methods like fold, getOrElse, etc won’t be in scope.

License & Acknowledgements

Since this library mimics the scala.Option behavior, it made sense to copy and adapt the documentation of it’s methods and sometimes the implementation too. Those bits are copyrighted by EPFL and Lightbend under Apache License 2.0, which is the same license as this library.