推断类型为理解

时间:2017-11-14 14:37:01

标签: scala scalaz

设定:

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 ()
}
  1. 为什么要编译?
  2. x的类型是什么? (IDEA说它是type $_1或类似的东西 - 我认为它不是可表示的类型)
  3. 修改

    如果我将yield ()更改为yield println(x)并打印出for for comprehension的输出,我会得到:

    Map(1 -> a)
    Set(2)
    (Foo(Map(1 -> a),Set(2)),List((), ()))
    

1 个答案:

答案 0 :(得分:0)

问题是if表达式的类型

if (bar.cond)
  `foo.map` += 1 -> "a"
else
  `foo.set` += 2 

State[Foo, Serializable]或类似的(scala可以统一MapSet类型的公共基本类型);但是runTraverseS[Foo, Unit]期望State[Foo, Unit](声明为Unit),并且这些类型无法统一。

解决方案很简单,只需将结果映射到“void”,就像这样:

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]