简体   繁体   中英

scala cats traverse for list

I am trying to understand the traverse for list using this page, https://www.scala-exercises.org/cats/traverse

and I have a very basic question (sorry for those who think it is too easy or obvious). just check below signature

trait Traverse[F[_]] {
  def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]
}
import cats.data.{ NonEmptyList, OneAnd, Validated, ValidatedNel }
import cats.implicits._

def parseIntEither(s: String): Either[NumberFormatException, Int] =
  Either.catchOnly[NumberFormatException](s.toInt)
List("1", "abc", "3").traverse(parseIntEither).isLeft should be(true)

you can see the signature traverse is expecting 2 arguments, the first argument to be a List, and the 2nd argument to be the 'f'. then why the above is calling it only with 1 argument? and instead the list becomes the 'instance'/'object' that the traverse method is called upon.

I am puzzled.

Thanks a lot.

Because if you have (n+1)-parameter method foo

class Arg1
class Arg2
  
object Obj {
  def foo(arg1: Arg1, arg2: Arg2) = ???
}

you can define a syntax (extension method, eg with the same name foo ) delegating a call to the former foo

implicit class Arg1Ops(val arg1: Arg1) extends AnyVal {
  def foo(arg2: Arg2) = Obj.foo(arg1, arg2)
}

    // which is basically the same as
//  class Arg1Ops(val arg1: Arg1) extends AnyVal {
//    def foo(arg2: Arg2) = Obj.foo(arg1, arg2)
//  }
//
//  implicit def toArg1Ops(arg1: Arg1): Arg1Ops = new Arg1Ops(arg1)

and call foo as a n-parameter method on the first parameter as a caller

val a1 = new Arg1
val a2 = new Arg2

a1.foo(a2)

In your example List("1", "abc", "3") is like arg1 and parseIntEither is like arg2 .

In Cats Traverse is annotated with Simulacrum macro-annotation @typeclass

Traverse.scala

@typeclass trait Traverse[F[_]] extends Functor[F] with Foldable[F] with UnorderedTraverse[F] { ...

and this annotation generates a syntax for Traverse

syntax/package.scala#L64

object traverse extends TraverseSyntax

syntax/traverse.scala

trait TraverseSyntax extends Traverse.ToTraverseOps

Traverse.scala

object Traverse extends scala.AnyRef with java.io.Serializable {
  ...

  // macro-generated code
  trait Ops[F[_], C] extends scala.AnyRef {
    ...
    def traverse[G[_], B](f : scala.Function1[C, G[B]])(implicit 
      evidence$1 : cats.Applicative[G]
    ) : G[F[B]] = { /* compiled code */ }
    ...
  }

  // macro-generated code
  trait ToTraverseOps extends scala.AnyRef {
    @...
    implicit def toTraverseOps[F[_], C](target : F[C])(implicit 
      tc : cats.Traverse[F]
    ) : Traverse.Ops[F, C] { type TypeClassType = cats.Traverse[F] } = 
    { /* compiled code */ }
  }

  ...

}

Consider the following equivalent ways of invoking traverse

implicitly[Traverse[List]].traverse(List("1", "abc", "3"))(parseIntEither)  // scary
Traverse.apply[List].traverse(List("1", "abc", "3"))(parseIntEither)        // a bit better
Traverse[List].traverse(List("1", "abc", "3"))(parseIntEither)              // much better
List("1", "abc", "3").traverse(parseIntEither)                              // what I ideally want to write

In all cases we need to access the evidence that List satisfies the constraints of Traverse type class before we can call traverse . We could be very explicit about it by using implicitly as in the first case, but ideally we want to just invoke traverse on a value without worrying too much about the type class mechanics as in the last case. The last case makes use of Scala facility called extension methods . Scala 3 has some new syntax which IMO really clarifies the extension concept. In Scala 2 they are implemented using implicit class construct which perhaps is not very informative as a keyword to beginners first encountering the concept. In Scala 3 the concept of extension methods can be tied to the concept of type class directly, perhaps something like so

trait Traverse[F[_]]:
  extension [G[_]: Applicative, A, B](fa: F[A]) def traverse(f: A => G[B]): G[F[B]]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM