[英]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")
就像arg1
, parseIntEither
就像arg2
。
在貓Traverse
都被注解擬像 宏觀注釋@typeclass
@typeclass trait Traverse[F[_]] extends Functor[F] with Foldable[F] with UnorderedTraverse[F] { ...
並且此注釋為Traverse
生成語法
object traverse extends TraverseSyntax
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.