设置:
import scalaz._; import Scalaz._
case class Foo(map: Map[Int, String] = Map.empty, set: Set[Int] = Set.empty)
val `foo.map`: Lens[Foo, Map[Int, String]] = Lens.lensu((f, m) => f.copy(map = m), _.map)
val `foo.set`: Lens[Foo, Set[Int]] = Lens.lensu((f, s) => f.copy(set = s), _.set)
case class Bar(cond: Boolean)
val listOfBars = List(Bar(true), Bar(false))
现在这不编译
listOfBars.runTraverseS[Foo, Unit](Foo()) { bar =>
if (bar.cond)
`foo.map` += 1 -> "a"
else
`foo.set` += 2
}
这个问题是 Lens[Foo, Map[K, V]].+=
返回一个 State[Foo, Map[K, V]]
而Lens[Foo, Set[A]].+=
返回一个 State[Foo, Set[A]]
。回想一下,State[S, A]
在 A
然而,这确实编译:
listOfBars.runTraverseS[Foo, Unit](Foo()) { bar =>
for {
_ <- State.init[Foo]
x <- {
if (bar.cond)
`foo.map` += 1 -> "a"
else
`foo.set` += 2
}
} yield ()
}
- 为什么会编译?
x
的类型是什么? (IDEA 说它是type $_1
或类似的东西 - 我假设它不是可表示的类型)
编辑
如果我将 yield ()
更改为 yield println(x)
并打印 for comprehension 的输出,我会得到:
Map(1 -> a)
Set(2)
(Foo(Map(1 -> a),Set(2)),List((), ()))
最佳答案
问题是if表达式的类型
if (bar.cond)
`foo.map` += 1 -> "a"
else
`foo.set` += 2
是State[Foo, Serializable]
或类似的(scala可以统一Map
和Set
类型的通用基类型);但是 runTraverseS[Foo, Unit]
需要一个 State[Foo, Unit]
(因为声明了 Unit
),这些类型不能统一。
解决方案很简单,只需映射结果并像这样“作废”它:
val unit = ()
listOfBars.runTraverseS[Foo, Unit](Foo()) { bar =>
(if (bar.cond)
`foo.map` += 1 -> "a"
else
`foo.set` += 2
).map(Function.const(unit))
}
这实际上是代码的“for”版本所做的(它只是丢弃值 x
,其类型类似于 State[Foo, Serializable]
。
关于scala - 推断类型以供理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47288392/