typelevel / typelevel/cats

A Nullable type for scala3

Open
#4,822 12 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Scala
Stars
5.5k
Forks
1.2k
Avg merge
2d 10h
Merged PRs (30d)
5

Description

I was using explicit nulls recently in a scala3 only project and had to deal with a java API that returned null. I took a moment to make a small wrapper to use a more FP style:

import scala.quoted.*

opaque type Nullable[+T] = T | Null

object Nullable:

  extension [T](inline nullable: Nullable[T])
    transparent inline def fold[A](inline ifNull: => A)(inline fn: T => A): A =
      ${ foldImpl('nullable, 'ifNull, 'fn) }

    inline def isNull: Boolean =
      fold(true)(_ => false)

    inline def nonNull: Boolean =
      fold(false)(_ => true)

    inline def map[B](inline fn: T => B): Nullable[B] =
      fold(null: Null)(fn)

    inline def flatMap[B](inline fn: T => Nullable[B]): Nullable[B] =
      fold(null: Null)(fn)

    inline def toOption: Option[T] =
      fold(None)(Some(_))

    inline def iterator: Iterator[T] =
      fold(Iterator.empty)(Iterator.single(_))

  inline def apply[A](inline a: A | Null): Nullable[A] =
    a

  private def foldImpl[T: Type, A: Type](
      nullable: Expr[Nullable[T]],
      ifNull: Expr[A],
      fn: Expr[T => A]
  )(using Quotes): Expr[A] =
    '{
      val n = $nullable
      given CanEqual[T | Null, Null] = CanEqual.derived
      if (n == null) $ifNull
      else {
        val safe: T = n.asInstanceOf[T]
        ${ Expr.betaReduce('{ $fn(safe) }) }
      }
    }

I think this type could have lawful implementations of many typeclasses: Monad, Traverse, Foldable, Hash, Order, Eq, Monoid, Group (maybe more algebraic typeclasses).

Should I flesh this out and make a PR to add it to cats.data perhaps? It could be nice to have a zero-cost abstraction for nullable types from Java and javascript interop.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the proposed Nullable definition in the issue and review existing cats.data types and typeclass conventions before deciding whether the abstraction belongs there. Done means a scoped proposal or PR addresses Scala 3 and Java/JavaScript interop, identifies the supported lawful typeclasses, and provides validation for the chosen behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
scala
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.