Skip to content
Dinko Srkoč edited this page Aug 7, 2011 · 4 revisions

The Either type represents a value of one of two possible types. The possible values are represented by Left and Right.

One possible usage of Either is similar to Option where, by convention, Left is equivalent of None, i.e. it represents failure, while Right assumes the role of Some. The difference between Option and Either is in None/Left types where the former carries no information apart from the fact that the computation failed. The latter, on the other hand, may also carry additional message explaining what went wrong.

The other usage may be when one needs to return one of two distinct types. One example may be sorting books into list of authors and list of books without them.

Using example from Option monad, and replacing Option with Either produces the following result:

import static hr.helix.monadologie.MonadComprehension.foreach
import static hr.helix.monadologie.monads.Either.left
import static hr.helix.monadologie.monads.Either.right

def safe(Closure action) {
    try {
        right(action())
    } catch (e) {
        left("${e.getClass().simpleName}: $e.message")
    }
}

def check = { name -> name ? right(name) : left("file name not given") }
def load  = { name -> safe { new File(name).text }},
    parse = { text -> safe { text.tokenize()*.toInteger() }},
    div   = { a, b -> safe { a / b }}
    
def calc = { fileName ->
    foreach {
        file = takeFrom { check(fileName).right() }
        text = takeFrom { load(file).right() }
        data = takeFrom { parse(text).right() }
        res  = takeFrom { div(data).right() }

        yield { res }
    }
}

println calc(null) // file name validation failed
println calc("foo.txt") // file not found

def fileName = "/tmp/data"

new File(fileName).withWriter { w -> w << "aa bb" }
println calc(fileName)  // number format exception

new File(fileName).withWriter { w -> w << "1 0" }
println calc(fileName)  // division by zero

new File(fileName).withWriter { w -> w << "10 20" }
println calc(fileName)

Here is example of another use case - processing data that have two distinct groups of values.

import static hr.helix.monadologie.monads.Either.left
import static hr.helix.monadologie.monads.Either.right
import static hr.helix.monadologie.monads.Either.lefts
import static hr.helix.monadologie.monads.Either.rights

def books = [
    [title:"The Left Hand of Darkness", author:"Ursula K. LeGuin"],
    [title:"Rendezvous with Rama", author:"Arthur C. Clarke"],
    [title:"Star Maker", author:""],
    [title:"The Forever War", author:"Joe Haldeman"],
    [title:"Last and First Men"]
]

def sorted = books.collect {
    it.author ? right(it.author) : left(it.title)
}

def authors    = rights(sorted).unique(),
    otherBooks = lefts(sorted)

println "List of authors:\n${authors.join(", ")}"
println "\nBooks with undisclosed authors:\n${otherBooks.join(", ")}"

The real question here is, of course, who wrote Last and First Men and Star Maker?

Clone this wiki locally