簡體   English   中英

scala 貓遍歷列表

[英]scala cats traverse for list

我正在嘗試使用此頁面了解列表的遍歷, https://www.scala-exercises.org/cats/traverse

我有一個非常基本的問題(對不起,那些認為它太簡單或太明顯的人)。 只需檢查下面的簽名

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)

您可以看到簽名遍歷需要 2 個參數,第一個參數是 List,第二個參數是 'f'。 那么為什么上面只用 1 個參數調用它呢? 相反,列表變成了調用 traverse 方法的“實例”/“對象”。

我很困惑。

非常感謝。

因為如果你有 (n+1) 參數方法foo

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

您可以定義一個語法(擴展方法,例如具有相同名稱foo ),將調用委托給前一個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)

並在第一個參數上調用foo作為 n 參數方法作為調用者

val a1 = new Arg1
val a2 = new Arg2

a1.foo(a2)

在您的示例中List("1", "abc", "3")就像arg1parseIntEither就像arg2

在貓Traverse都被注解擬像 宏觀注釋@typeclass

遍歷.scala

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

並且此注釋為Traverse生成語法

語法/package.scala#L64

object traverse extends TraverseSyntax

語法/traverse.scala

trait TraverseSyntax extends Traverse.ToTraverseOps

遍歷.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 */ }
  }

  ...

}

考慮以下調用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

在所有情況下,我們都需要訪問List滿足Traverse類型類約束的證據,然后才能調用traverse 我們可以像第一種情況一樣通過implicitly使用來非常明確地說明它,但理想情況下,我們只想調用一個值的traverse ,而不必像最后一種情況那樣擔心類型類機制。 最后一種情況使用稱為擴展方法的 Scala 工具。 Scala 3 有一些新語法,IMO 真正闡明了擴展概念。 在 Scala 2 中,它們是使用implicit class構造來實現的,對於初學者來說,這對於第一次遇到這個概念的關鍵字來說可能不是很有用。 在 Scala 3 中,擴展方法的概念可以直接與類型類的概念聯系起來,也許像這樣

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM