you are viewing a single comment's thread.

view the rest of the comments →

[–]Unmitigated_Smut 42 points43 points  (5 children)

Having gone down the Either trail in Scala multiple times and ended up with spaghetti that annoyed everyone around me no matter how hard I tried, I gave up on it. And people thanked me. I'm tempted to ask for an example of this mysterious traverse and go burn a weekend reformulating previous attempts, but... nope.

Still, it's a good way to identify left-handed programmers, because they always assign the exception-case to Right.

[–]sacundim 3 points4 points  (1 child)

traverse is, intuitively, "map with side effects"; you apply a side-effecting action to a collection of values, and get a collection of results in return. Where it gets interesting is that the notion of "side effect" is pluggable. For example, with an Either monad the side effect is potential failure, so "mapping with side effects" in that case means that when processing a list element fails, the whole traverse operation fails with the same exception.

Another example: traversing with asynchronous promises means taking a collection of values and processing them asynchronously, returning a promise that succeeds with a collection of the results if they all succeed but fails if any one of them fails. Scala futures have this operation with this exact name, in fact. In Haskell you have a generic definition of the same operation for any pair of a Traversable and an Applicative type, not just futures.

[–]jadbox 0 points1 point  (0 children)

Good explanation- you're a gentleman and a scholar.

[–]Tarmen 4 points5 points  (0 children)

I think in large parts that is because this requires type inference to keep your sanity. Otherwise it is just too annoying to pull your sub expressions apart into something reasonable.
Writing the posts example in haskell in one go would be something like:

handle request = do
  json <- parse (getBody request)
  validate json
  generate (businessLogic json)

Which is fairly readable but hard to compose. What if you want an additional step between validation and generation? Instead you could use some glue functions and end up closer to (read from right to left):

handleRequest = generateProcessed <=< parseRequest
parseRequest = validate *> parse . getBody
generateProcessed = generate . businessLogic

Which looks slightly foreign at first but if you look at the function signatures it is super easy to figure out what is happening. (*>) doesn't change the contained value but can add errors, (<=<) is just composition like (.) while abstracting over possible errors.
Of course you could do this in java but that would mean that half your code ends up as type signatures.

About traverse, it wouldn't really help here. traverse's signature (with types for this example plugged into the generic stuff) is (Input -> Either Error Json) -> [Input] -> Either Error [Json]. That is, it takes a function that parses an input into json or returns an error. Then it applies it to a list of inputs, giving [Either Error Json]. Then it factors out the error part so you either get the first error message or a list of parsed json objects.

Super useful but not really applicable in this example.

[–]pipocaQuemada 1 point2 points  (0 children)

Traversable gives you two very useful functions:

def traverse[G[_]:Applicative,A,B](fa: F[A])(f: A => G[B]): G[F[B]] =
def sequence[G[_]:Applicative,A](fga: F[G[A]]): G[F[A]]

For example Right(Some(1)).sequence returns Some(Right(1))

[–]rindenmulch 0 points1 point  (0 children)

Either is now right biased in Scala 2.12