scala猫中为什么需要函子
我刚刚开始学习Scala猫的框架。我正在读《函子》。我了解它的特点,但不了解它的用法。如果scala猫中为什么需要函子,scala,functor,scala-cats,Scala,Functor,Scala Cats,我刚刚开始学习Scala猫的框架。我正在读《函子》。我了解它的特点,但不了解它的用法。如果Functors中已有映射方法,如列表,选项等,我为什么要使用它 例如, val list = List(1, 2, 3) Functor[List].map(list)(x => x * 2) 但同样的道理也可以通过 list.map(x => x * 2) 当我们在Functortrait中提取方法map时会得到什么。 有人能解释一下它的用法吗。这个问题类似于为什么在OOP中有人需要一个
Functors
中已有映射
方法,如列表
,选项
等,我为什么要使用它
例如,
val list = List(1, 2, 3)
Functor[List].map(list)(x => x * 2)
但同样的道理也可以通过
list.map(x => x * 2)
当我们在Functor
trait中提取方法map
时会得到什么。
有人能解释一下它的用法吗。这个问题类似于为什么在OOP中有人需要一个接口(trait),而它的实现有相同的方法 接口是一种抽象。抽象是有用的。在编程时,我们更喜欢关注重要的事情,而忽略当前不重要的细节。此外,使用接口编程有助于解耦实体,从而创建更好的体系结构 接口(如
compariable
,Iterable
,Serializable
等)是描述OOP中行为的方式。类型类(如Functor
、Monad
、Show
、Read
等)是描述FP中行为的方式
如果只想在列表(或
选项
)上映射(或平面映射
),则不需要函子
。如果你想处理所有你需要的可映射的东西,如果你想处理所有你需要的可映射的东西,如果你想处理所有的可映射的东西,你需要单子等等。这个问题类似于为什么在OOP中有人需要一个接口(trait),而它的实现有相同的方法
接口是一种抽象。抽象是有用的。在编程时,我们更喜欢关注重要的事情,而忽略当前不重要的细节。此外,使用接口编程有助于解耦实体,从而创建更好的体系结构
接口(如compariable
,Iterable
,Serializable
等)是描述OOP中行为的方式。类型类(如Functor
、Monad
、Show
、Read
等)是描述FP中行为的方式
如果只想在列表(或
选项
)上映射(或平面映射
),则不需要函子
。如果你想处理所有你需要的可映射的东西Functor
,如果你想处理所有你需要的可映射的东西flatMap
Monad等等。当你知道这个对象有这个方法,并且这个方法是这样调用的时候,你可以在对象上调用.map
。若您知道对象的确切类型,那个么编译器可以检查是否确实如此。但是如果你不知道对象的类型呢?如果不想使用运行时反射呢
想象一下这样的情况:
def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
fInt.map(_ * 2)
def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
Functor[F].map(fInt)(_ * 2)
在这里,我们不知道F
的类型-它可能是列表
,选项
,未来
,IO
,或者[String,*]
。然而,我们能够.map
而不使用反射-我们使用函子[F]
来增强扩展方法。我们也可以不用这样的扩展方法:
def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
fInt.map(_ * 2)
def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
Functor[F].map(fInt)(_ * 2)
它将起作用(只要我们有权在其范围内):
在我们知道F=列表、选项等的情况下,我们没有理由使用它。但是如果F是动态的,我们有所有的理由使用它
为什么我们要让这个F成为动态的呢?在库中使用它,例如,可以将通过类型类提供的多个功能组合在一起
例如,如果您有F[\u]
和g[\u]
,并且您有travel[F]
和Applicative[g]
(更强大的函子F[A]
可以将F[A]
转换为g[A]
:
val listOption: List[Option] = List(Some(1), Some(2))
listOption.sequence // Some(List(1, 2))
val listFuture: List[Option] = List(Future(1), Future(2))
listFuture.sequence // Future(List(1, 2))
实际上,Cats生态系统中的所有库都使用这个概念(称为TypeClass)来实现功能,而不必假设您选择与它们相同的数据结构和IO组件。只要您能够提供类型类实例,证明它们可以安全地在您的类型上使用某些方法,它们就可以实现该功能(例如,Cats效果使用一些类型类扩展Cats,Doobie、FS2、Http4s等在这些基础上构建,而不必假设您使用什么来运行计算)
长话短说-在像您这样的情况下,使用Functor
是没有意义的,但一般来说,它们允许您在不那么简单且没有硬编码类型的情况下仍然使用.map
。您可以在对象上调用.map
,当您知道此对象具有此方法时,这个方法就是这样叫的。若您知道对象的确切类型,那个么编译器可以检查是否确实如此。但是如果你不知道对象的类型呢?如果不想使用运行时反射呢
想象一下这样的情况:
def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
fInt.map(_ * 2)
def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
Functor[F].map(fInt)(_ * 2)
在这里,我们不知道F
的类型-它可能是列表
,选项
,未来
,IO
,或者[String,*]
。然而,我们能够.map
而不使用反射-我们使用函子[F]
来增强扩展方法。我们也可以不用这样的扩展方法:
def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
fInt.map(_ * 2)
def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
Functor[F].map(fInt)(_ * 2)
它将起作用(只要我们有权在其范围内):
在我们知道F=列表、选项等的情况下,我们没有理由使用它。但是如果F是动态的,我们有所有的理由使用它
为什么我们要让这个F成为动态的呢?在库中使用它,例如,可以将通过类型类提供的多个功能组合在一起
例如,如果您有F[\u]
和g[\u]
,并且您有遍历[F]
和应用[g]
(更强大的函子
)