Scala Maybe

I wrote a Maybe (a lazy Option[T]) that I can use in Scala.

Scala Maybe

… because I wanted a lazy variant of Option[+T]. I had a use case in mind at the time, but, since then; I’ve taken another approach.

Since writing this, I’ve found an explanation of Monads that “groks” with me and multiple other places where “lazy is better” and I preffer (my) Maybe[T] over Option[T].

package peterlavalle

/**
  * like Option[+A] but lazy
  */
trait Maybe[+T] {

  /**
   * however we're implemented; ensure that any operations leading up to now are only called "once"
   */
  def cache: Maybe[T]

  /**
    * Apply some operation to the value, and, return a new monad.
    *
    * this is (more likely) lazy
    **/
  def call[V](operation: T => V): Maybe[V]

  /**
   * bind it like a monad ... but ... not lazy
   */
  def bind[V](operation: T => Maybe[V]): Maybe[V]

  /**
    * if we're empty; produce the other one
    */
  def otherwise[E >: T](them: => Maybe[E]): Maybe[E]

  /**
    * produce/convert this to an eager Option[T] in preparation for reading or matching it.
    *
    * This is (kind of) a hack
    */
  def some: Option[T]

  final def |[V](that: T => Maybe[V]): Maybe[V] = bind(that)

  final def ![V](that: T => V): Maybe[V] = call(that)

  final def ?[E >: T](them: => Maybe[E]): Maybe[E] = otherwise(them)
}

object Maybe {

  def apply[T](read: => T): Maybe[T] =
    new Maybe[T] {
      override lazy final val cache: Maybe[T] = {
        lazy val data: T = read
        apply(data)
      }

      override lazy final val some: Option[T] = Some(read)

      override final def otherwise[E >: T](them: => Maybe[E]): Maybe[E] = this

      override final def bind[V](map: T => Maybe[V]): Maybe[V] =
        map(read)

      override final def call[V](map: T => V): Maybe[V] =
        Maybe {
          map(read)
        }
    }

  def unapply[T](arg: Maybe[T]): Option[T] = arg.some

  case object Nope extends Maybe[Nothing] {
    override final val cache: Maybe[Nothing] = this

    override final val some: Option[Nothing] = None

    override final def bind[V](map: Nothing => Maybe[V]): Maybe[V] = Nope

    override final def call[V](map: Nothing => V): Maybe[V] = Nope

    override final def otherwise[E >: Nothing](them: => Maybe[E]): Maybe[E] = them
  }
}
Peter LaValle avatar
About Peter LaValle
Peter is currently a PhD student at the University of Nottingham. His day work involves applying functional programming to problems with artificial intelligence. Someday maybe he'll retire and be a graphics programmer or demoscene coder.
comments powered by Disqus