when map over a function 当你有 andThen 时很有用
When map over a function is useful when you have andThen
使用 Scalaz,一个函数可以映射到另一个函数上。我什么时候想使用 map
而不是 andThen
?使用 map
有明显的优势吗?谢谢
例如,
val f: Int => Int = (a) => a + 10
val g: Int => Int = (a) => a * 100
(f map g map {_*3})(10) == (f andThen g andThen {_*3})(10) // true
暂时搁置实现细节,map
是 andThen
函数(在A => ?
的仿函数实例下),以及如果我们专门讨论功能而不是更高层次的抽象,那么谈论偏爱一个而不是另一个真的没有多大意义。
像 map
这样的方法(以及类型 类 像 Functor
更普遍)允许我们做的是对特定类型或类型构造函数的抽象。例如,假设我们要编写一个适用于 A => Int
或 Kleisli[Option, A, Int]
的 incrementResult
方法。这些类型在继承方面没有任何共同点(缺少 AnyRef
,这是无用的),但是 A => ?
和 Kleisli[Option, A, ?]
都是函子,所以我们可以这样写:
import scalaz._, Scalaz._
def incrementResult[F[_]: Functor](f: F[Int]): F[Int] = f.map(_ + 1)
然后像这样使用它(注意我使用 kind-projector 来稍微简化类型语法):
scala> val plainOldFuncTriple: Int => Int = _ * 3
plainOldFuncTriple: Int => Int = <function1>
scala> val optionKleisliTriple: Kleisli[Option, Int, Int] = Kleisli(i => Some(i * 3))
optionKleisliTriple: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>)
scala> val f = incrementResult[Int => ?](plainOldFuncTriple)
f: Int => Int = <function1>
scala> val k = incrementResult[Kleisli[Option, Int, ?]](optionKleisliTriple)
k: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>)
scala> f(10)
res0: Int = 31
scala> k(10)
res1: Option[Int] = Some(31)
在这种情况下,具体来说,有更好的方法来实现这个操作,但它显示了一般的想法——我们无法使用 andThen
编写一个既适用于普通函数又适用于 Kleisli 箭头的方法,但是我们可以使用 map
给我们的额外抽象级别。
所以回答你的问题——如果你想抽象所有具有仿函数实例的类型构造函数,你会使用 map
,但如果你专门使用函数,map
是 andThen
,并且——只要我们仍然搁置实现细节——你选择哪个并不重要。
脚注:Scalaz 的 syntax
包为具有仿函数实例的类型的值提供的 map
是作为扩展方法实现的,因此有一点点开销(都在编译时)和运行时)涉及在函数上使用 map
而不是 andThen
。如果您只使用函数而不需要额外的抽象,那么,您不妨使用 andThen
.
使用 Scalaz,一个函数可以映射到另一个函数上。我什么时候想使用 map
而不是 andThen
?使用 map
有明显的优势吗?谢谢
例如,
val f: Int => Int = (a) => a + 10
val g: Int => Int = (a) => a * 100
(f map g map {_*3})(10) == (f andThen g andThen {_*3})(10) // true
暂时搁置实现细节,map
是 andThen
函数(在A => ?
的仿函数实例下),以及如果我们专门讨论功能而不是更高层次的抽象,那么谈论偏爱一个而不是另一个真的没有多大意义。
像 map
这样的方法(以及类型 类 像 Functor
更普遍)允许我们做的是对特定类型或类型构造函数的抽象。例如,假设我们要编写一个适用于 A => Int
或 Kleisli[Option, A, Int]
的 incrementResult
方法。这些类型在继承方面没有任何共同点(缺少 AnyRef
,这是无用的),但是 A => ?
和 Kleisli[Option, A, ?]
都是函子,所以我们可以这样写:
import scalaz._, Scalaz._
def incrementResult[F[_]: Functor](f: F[Int]): F[Int] = f.map(_ + 1)
然后像这样使用它(注意我使用 kind-projector 来稍微简化类型语法):
scala> val plainOldFuncTriple: Int => Int = _ * 3
plainOldFuncTriple: Int => Int = <function1>
scala> val optionKleisliTriple: Kleisli[Option, Int, Int] = Kleisli(i => Some(i * 3))
optionKleisliTriple: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>)
scala> val f = incrementResult[Int => ?](plainOldFuncTriple)
f: Int => Int = <function1>
scala> val k = incrementResult[Kleisli[Option, Int, ?]](optionKleisliTriple)
k: scalaz.Kleisli[Option,Int,Int] = Kleisli(<function1>)
scala> f(10)
res0: Int = 31
scala> k(10)
res1: Option[Int] = Some(31)
在这种情况下,具体来说,有更好的方法来实现这个操作,但它显示了一般的想法——我们无法使用 andThen
编写一个既适用于普通函数又适用于 Kleisli 箭头的方法,但是我们可以使用 map
给我们的额外抽象级别。
所以回答你的问题——如果你想抽象所有具有仿函数实例的类型构造函数,你会使用 map
,但如果你专门使用函数,map
是 andThen
,并且——只要我们仍然搁置实现细节——你选择哪个并不重要。
脚注:Scalaz 的 syntax
包为具有仿函数实例的类型的值提供的 map
是作为扩展方法实现的,因此有一点点开销(都在编译时)和运行时)涉及在函数上使用 map
而不是 andThen
。如果您只使用函数而不需要额外的抽象,那么,您不妨使用 andThen
.