Scala具有Iterable[A]定义的特征
def flatMap[B](f: (A) ? GenTraversableOnce[B]): Iterable[B]
Run Code Online (Sandbox Code Playgroud)
这肯定看起来像monad上的绑定函数,并且文档暗示它是monad,但有两个异议,一个是minor和一个major:
GenTraversableOnce.我认为这只是一种在评判monad时可以忽略的便利.这些问题会破坏收藏品的单一性吗?
lmm*_*lmm 19
"主要"问题更容易回答:不,它没有,因为这不是它的意思.monad不需要具有任何特定的"值"或没有,只是以特定方式组合功能.
对于"次要的",你是关心类型的权利.正确地说,monad是一个monoid(有一些额外的约束),这意味着它是一个具有某些操作的集合.据我所知,这个集合的元素是类型的东西A => M[B](在scalaz这个类型被称为Kleisli); flatMap是|+|幺半群的操作.
Scala 中所有可能 的集合是否A => Iterable[B]构成了与此操作相关的monoid(以及合适的标识选择)?不,非常不是,因为有很多可能A => Iterable[B]违反了monad法律.对于一个简单的例子,{a: A => throw new RuntimeException()}.一个更严重的例子是,例如,如果a Set存在于flatMap链中,这可能会破坏关联性:假设我们有:
f: String => Iterable[String] = {s => List(s)}
g: String => Iterable[String] = {s => Set(s)}
h: String => Iterable[String] = {s => List("hi", "hi")}
Run Code Online (Sandbox Code Playgroud)
然后
((f |+| g) |+| h).apply("hi") = List("hi") flatMap h = List("hi", "hi")
Run Code Online (Sandbox Code Playgroud)
但
(f |+| (g |+| h)).apply("hi") = List("hi") flatMap {s => Set("hi")} = List("hi")
Run Code Online (Sandbox Code Playgroud)
令人沮丧的是,因为幺半群的全部意义在于我们可以写作f |+| g |+| h而不用担心我们评估它的方式.回到monads,重点是我们应该能够写作
for {
a <- f("hi")
b <- g(a)
c <- h(b)
} yield c
Run Code Online (Sandbox Code Playgroud)
而不用担心其责令flatMaps的组成.但为f,g并h从上面,你希望上面的代码,得到的答案吗?(我知道答案,但这很令人惊讶).使用真正的monad,除了作为scala编译器实现细节之外,问题不会出现,因为答案在任何一种方式都是相同的.
另一方面,可能的特定子集A => M[B],例如" scalazzi scalazzi安全子集中A => List[B]实现的所有集合 ",是否构成了关于该定义的monad flatMap?是(至少对于两个scala函数相等时的普遍接受的定义).并且有几个子集适用于此.但我认为scala Iterables 一般形成一个monad 并不完全正确flatMap.
Apo*_*isp 13
你的标题问题的答案是否定的.一个集合flatMap不足以成为monad.如果它满足一些其他条件,它可能是一个单子.
你的"次要"问题肯定打破了monadicity(正确的"monad-ness")Iterable.这是因为许多亚型Iterable和GenTraversableOnce是不是单子.因此,Iterable不是monad.
你的"主要"问题根本不是问题.例如,Listmonad的函数参数一次flatMap接收一个元素List.列表的每个元素都会生成一个完整的结果列表,这些列表最后都会连接在一起.
幸运的是,判断某些东西是否是monad非常容易!我们只需要知道monad的精确定义.
F[_]一个类型参数的类型构造函数.例如,F可以是List,Function0,Option,等.A的值并生成类型的值F[A].A => F[B]函数和类型函数的函数,B => F[C]并生成一个类型的复合函数A => F[C].(还有其他方法可以说明这一点,但我发现这个公式很容易解释)
考虑这些Iterable.它绝对需要一种类型的参数.它在功能上有各种单位Iterable(_).虽然它的flatMap操作并不严格遵守,但我们当然可以写:
def unit[A](a: A): Iterable[A] = Iterable(a)
def compose[A,B,C](f: A => Iterable[B],
g: B => Iterable[C]): A => Iterable[C] =
a => f(a).flatMap(g)
Run Code Online (Sandbox Code Playgroud)
但这并不能使它成为一个单子,因为monad还必须满足某些定律:
compose(compose(f, g), h)=compose(f, compose(g, h))compose(unit, f)= f=compose(f, unit)正如lmm已经指出的那样,打破这些法律的一个简单方法就是混合Set和List作为Iterable这些表达式.
虽然只有flatMap(而不是unit)的类型构造不是monad,但它可能形成所谓的Kleisli半群.要求与monad相同,除非没有unit操作和没有身份法的情况.
(关于术语的注释:monad形成Kleisli类,和半群是没有身份的类别.)
Scala的for-内涵技术上有甚至更少比semigroupoids(只要求map和flatMap操作服从没有法律).但是将它们与至少不是半群的东西一起使用会产生非常奇怪和令人惊讶的效果.例如,这意味着您无法在for-comprehension中内联定义.如果你有
val p = for {
x <- foo
y <- bar
} yield x + y
Run Code Online (Sandbox Code Playgroud)
而定义foo是
val foo = for {
a <- baz
b <- qux
} yield a * b
Run Code Online (Sandbox Code Playgroud)
除非相关性法律成立,否则我们不能依赖于将其重写为:
val p = for {
a <- baz
b <- qux
y <- bar
} yield a * b + y
Run Code Online (Sandbox Code Playgroud)
不能做这种替换是非常违反直觉的.因此,大多数时候,当我们处理for-comprehension时,我们假设我们在monad中工作(可能即使我们没有意识到这一点),或者至少是Kleisli半群.
但请注意,这种替换通常不适用于Iterable:
scala> val bar: Iterable[Int] = List(1,2,3)
bar: Iterable[Int] = List(1, 2, 3)
scala> val baz: Iterable[Int] = Set(1,2,3)
baz: Iterable[Int] = Set(1, 2, 3)
scala> val qux: Iterable[Int] = List(1,1)
qux: Iterable[Int] = List(1, 1)
scala> val foo = for {
| x <- bar
| y <- baz
| } yield x * y
foo: Iterable[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9)
scala> for {
| x <- foo
| y <- qux
| } yield x + y
res0: Iterable[Int] = List(2, 2, 3, 3, 4, 4, 3, 3, 5, 5, 7, 7, 4, 4, 7, 7, 10, 10)
scala> for {
| x <- bar
| y <- baz
| z <- qux
| } yield x * y + z
res1: Iterable[Int] = List(2, 3, 4, 3, 5, 7, 4, 7, 10)
Run Code Online (Sandbox Code Playgroud)
有关Scala中monad的更多内容,包括它的含义以及我们应该关注的原因,我建议您查看本书的第11章.
| 归档时间: |
|
| 查看次数: |
4933 次 |
| 最近记录: |