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
    }
}
comments powered by Disqus
Peter LaValle avatar
Peter LaValle
Any links probably include affiliate ids for that sweet sweet kickback - and some programs require that I tell you. The contents of this blog are likely unrelated - as they include games, paints, and build tools.