alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (TraverseUsage.scala)

This example Scala source code file (TraverseUsage.scala) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Scala by Example" TM.

Learn more about this Scala project at its project page.

Java - Scala tags/keywords

app, boolean, ilist, int, list, none, nonemptylist, option, some, state, traverseusage, validationnel, vector

The TraverseUsage.scala Scala example source code

package scalaz.example

object TraverseUsage extends App {
  import scalaz._

  import scalaz.std.list._
  import scalaz.std.vector._
  import scalaz.std.option._
  import scalaz.std.anyVal._
  import scalaz.std.string._
  import scalaz.syntax.equal._      // for === syntax
  import scalaz.syntax.validation._ // for .success and .failure syntax

  // An easy to understand first step in using the Traverse typeclass
  // is the sequence operation, which given a Traverse[F] and
  // Applicative[G] turns F[G[A]] into G[F[A]].  This is like "turning
  // the structure 'inside-out'":
  val list1: List[Option[Int]] = List(Some(1), Some(2), Some(3), Some(4))
  assert(Traverse[List].sequence(list1) === Some(List(1,2,3,4)))

  // The effect of the inner Applicative is used, so in the case of
  // the Option applicative, if any of the values are None instead of
  // Some, the result of the entire computation is None:
  val list2: List[Option[Int]] = List(Some(1), Some(2), None, Some(4))
  assert(Traverse[List].sequence(list2) === None)

  // importing the traverse syntax will enhance values which have an
  // available traverse instance:
  import scalaz.syntax.traverse._
  assert(list1.sequence === Some(List(1,2,3,4)))
  assert(list1.sequence.sequence === list1)

  // A next step in using the Traverse TypeClass is the traverse
  // method. The traverse method maps function over a structure
  // through the effects of the inner applicative. You can think of
  // this method as combining a map with a sequence, so when you find
  // yourself calling fa.map(f).sequence, it can be replaced with just
  // fa.traverse(f):
  val smallNumbers = List(1,2,3,4,5)
  val bigNumbers = List(10,20,30,40,50)
  val doubleSmall: Int => Option[Int] = (x => if(x < 30) Some(x*2) else None)

  assert(smallNumbers.traverse(doubleSmall) === Some(List(2,4,6,8,10)))
  assert(smallNumbers.traverse(doubleSmall) === smallNumbers.map(doubleSmall).sequence)

  // when we hit the 30, we get a None, which "fails" the whole computation
  assert(bigNumbers.traverse(doubleSmall) === none[List[Int]])
  assert(bigNumbers.traverse(doubleSmall) === bigNumbers.map(doubleSmall).sequence)

  // both sequence and traverse come in "Unapply" varieties: sequenceU
  // and traverseU, these are useful in the case when the scala
  // compiler fails to infer an implicit Applicative instance for the
  // "inner" type. This will commonly happen when there is a "Kind
  // mismatch", for example with Validation, which is kind *,* -> *
  // instead of the expected * -> * kind of an Applicative, since the
  // Validation type constructor takes two arguments instead of one.

  val validations: Vector[ValidationNel[String,Int]] = Vector(1.success, "failure2".failureNel, 3.success, "failure4".failureNel)

  // this would not compile:
  // val result = validations.sequence

  // it gives you the perhaps hard to understand error:
  // could not find implicit value for parameter ev: scalaz.Leibniz.===[scalaz.Validation[String,Int],G[B]

  // these however work:
  val result: ValidationNel[String, Vector[Int]] = validations.sequenceU
  assert(result === NonEmptyList("failure2","failure4").failure[Vector[Int]])

  val onlyEvenAllowed: Int => ValidationNel[String, Int] = x => if(x % 2 === 0) x.successNel else (x.toString + " is not even").failureNel

  val evens = IList(2,4,6,8)
  val notAllEvens = List(1,2,3,4)

  assert(evens.traverseU(onlyEvenAllowed) === IList(2,4,6,8).success)
  assert(notAllEvens.traverseU(onlyEvenAllowed) === NonEmptyList("1 is not even","3 is not even").failure)

  // there is a traverseS method which allows us to traverse a
  // structure with a function while carrying a state through the
  // computation.

  import scalaz.State._
  // state stores the last seen Int, returns whether of not the current was a repeat
  val checkForRepeats: Int => State[Option[Int], Boolean] = { next =>
    for {
      last <- get
      _ <- put(some(next))
    } yield (last === some(next))
  }

  val nonRepeating = List(1,2,3,4)
  val repeating = List(1,2,3,3,4)

  // traverse the lists with None as the starting state, we get back a
  // list of Booleans meaning "this element was a repeat of the
  // previous
  val res1: List[Boolean] = nonRepeating.traverseS(checkForRepeats).eval(None)
  val res2: List[Boolean] = repeating.traverseS(checkForRepeats).eval(None)

  assert(Tag.unwrap(res1.foldMap(Tags.Disjunction(_))) === false)
  assert(Tag.unwrap(res2.foldMap(Tags.Disjunction(_))) === true)

  // Here's a variation of above which might be a bit of a head
  // scratcher, but this works because a Monoid gives rise to an
  // Applicative Functor.  Because Boolean is not a * -> * type
  // constructor, we need traverseU instead of traverse to find the
  // Applicative.
  import scalaz.Applicative.monoidApplicative
  assert(Tag.unwrap(res1.traverseU(Tags.Disjunction(_))) === false)
  assert(Tag.unwrap(res2.traverseU(Tags.Disjunction(_))) === true)
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala TraverseUsage.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.