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 个参数,第一个参数是列表,第二个参数是 'f'。
那么为什么上面只用 1 个参数调用它?相反,列表变成了调用遍历方法的 'instance'/'object'。
我很纳闷
非常感谢。
因为如果你有(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
被注释为 Simulacrum macro-annotation @typeclass
@typeclass trait Traverse[F[_]] extends Functor[F] with Foldable[F] with UnorderedTraverse[F] { ...
并且此注释生成 Traverse
的语法
object traverse extends TraverseSyntax
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 : 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
类型 class 约束的证据,然后才能调用 traverse
。我们可以像第一种情况那样使用 implicitly
来非常明确地说明它,但理想情况下我们只想对一个值调用 traverse
而不必过多担心类型 class 机制,如最后一个案例。最后一个案例使用了名为 扩展方法 的 Scala 工具。 Scala 3 有一些 new syntax,IMO 真正阐明了扩展概念。在 Scala 2 中,它们是使用 implicit class
结构实现的,对于初次接触该概念的初学者来说,作为关键字可能不是很有信息量。在 Scala 3 中,扩展方法的概念可以直接与类型 class 的概念相关联,也许类似于 so
trait Traverse[F[_]]:
extension [G[_]: Applicative, A, B](fa: F[A]) def traverse(f: A => G[B]): G[F[B]]
我正在尝试使用此页面了解列表的遍历, 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 个参数,第一个参数是列表,第二个参数是 'f'。 那么为什么上面只用 1 个参数调用它?相反,列表变成了调用遍历方法的 'instance'/'object'。
我很纳闷
非常感谢。
因为如果你有(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
被注释为 Simulacrum macro-annotation @typeclass
@typeclass trait Traverse[F[_]] extends Functor[F] with Foldable[F] with UnorderedTraverse[F] { ...
并且此注释生成 Traverse
object traverse extends TraverseSyntax
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 : 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
类型 class 约束的证据,然后才能调用 traverse
。我们可以像第一种情况那样使用 implicitly
来非常明确地说明它,但理想情况下我们只想对一个值调用 traverse
而不必过多担心类型 class 机制,如最后一个案例。最后一个案例使用了名为 扩展方法 的 Scala 工具。 Scala 3 有一些 new syntax,IMO 真正阐明了扩展概念。在 Scala 2 中,它们是使用 implicit class
结构实现的,对于初次接触该概念的初学者来说,作为关键字可能不是很有信息量。在 Scala 3 中,扩展方法的概念可以直接与类型 class 的概念相关联,也许类似于 so
trait Traverse[F[_]]:
extension [G[_]: Applicative, A, B](fa: F[A]) def traverse(f: A => G[B]): G[F[B]]