【问题标题】:Scala compiler does not infer type properlyScala 编译器无法正确推断类型
【发布时间】:2017-11-19 17:08:50
【问题描述】:

我正在阅读 Scala 中的函数式编程 的第 6 章。我已经接近本章的最后一个练习(即:6.11),它使 我怀疑某些代码行。

这是练习的描述:

让我们实现一个模拟简单糖果的有限状态自动机 自动售货机。机器有两个输入:你可以插入硬币,或者你 可以通过转动旋钮分配糖果。它可以是两个之一 状态:锁定或解锁。它还跟踪剩余的糖果数量 以及它包含多少硬币。

  • 如果还剩下糖果,将硬币插入上锁的机器会解锁。
  • 转动未锁定机器上的旋钮将使其分配一颗糖果并返回锁定状态。
  • 转动锁定机器上的旋钮或将硬币插入未锁定机器没有效果。
  • 没有糖果的机器会忽略任何类型的输入。

simulateMachine 方法应该基于一个 输入列表并返回剩余的硬币和糖果的数量 机器。例如,如果输入 Machine 有 10 个硬币和 5 个 糖果,一共买了4颗糖果成功,输出 应该是 (14, 1)。

以下是我自己实现的可测试单元。

object Test extends App {

  class Comparison(value: Int) {
    def shouldBe(other: Int): Boolean = value == other
  }

  implicit def enrichIntegers(value: Int) = new Comparison(value)

  val machine = Machine(locked  = true, candies = 5, coins = 10)
  val input = List(Coin, Turn, Coin, Turn, Coin, Turn, Coin, Turn)
  val result = StateChange.simulateMachine(input)
  val tuple: ((Int, Int), Machine) = result.run(machine)
  val ((coins, candies), _) = tuple

  (coins shouldBe 14) && (candies shouldBe 1) match {
    case true => println("Ok")
    case false => println("Ko")
  }
}

sealed trait Input
case object Coin extends Input
case object Turn extends Input
case class Machine(locked: Boolean, candies: Int, coins: Int)

case class State[S,+A](run: S => (A, S)) {
  def map[B](f: A => B): State[S, B] = State(
    s => {
      val (a, newState) = this.run(s)
      (f(a), newState)
    }
  )
  def map2[B,C](sb: State[S, B])(f: (A, B) => C): State[S, C] = State(
    s => {
      val (a, newState) = this.run(s)
      val (b, finalState) = sb.run(newState)
      (f(a, b), finalState)
    }
  )

  def flatMap[B](f: A => State[S, B]): State[S, B] = State(
    s => {
      val (a, state): (A, S) = this.run(s)
      f(a).run(state)
    }
  )
}

object State {

  def get[S]: State[S, S] = State(s => (s, s))
  def set[S](s: S): State[S, Unit] = State(_ => ((), s))

  def modify[S](f: S => S): State[S, Unit] = {
    for {
      s <- get
      _ <- set(f(s))
    } yield ()
  }

  def unit[S, A](a: A): State[S, A] = State(s => (a,s))

  def sequence[S, A](fs: List[State[S,A]]): State[S, List[A]] = {
    fs.foldRight[State[S, List[A]]](State.unit(Nil))((elem, acc) => elem.map2(acc)((a,b)=>a::b))
  }
}

object StateChange {
  def update(i: Input)(s: Machine) = {
    (i,s) match {
      case (_, Machine(_,0,_)) => s
      case (Coin, Machine(false,_,_)) => s
      case (Turn, Machine(true,_,_)) => s
      case (Coin, Machine(true,candies,coins)) => Machine(false,candies,coins+1)
      case (Turn, Machine(false,candies,coins)) => Machine(true,candies-1,coins)
    }
  }

  def simulateMachine(inputs: List[Input]): State[Machine, (Int, Int)] = {
    import State._
    val list: State[Machine, List[Unit]] = sequence(inputs.map(input => modify(update(input))))
    list.flatMap((_: List[Unit]) =>
       State(
         s => {
           val currentState: State[Machine, Machine] = get   // <= my doubt is here
           val (currentMachine, newState) = currentState.run(s)
           ((currentMachine.coins, currentMachine.candies), newState)
         }
       )
     )
  }
}

我怀疑simulateMachine定义中的以下行:

val getState: State[Machine, Machine] = get

simulateMachine 实现了我编写的一个脱糖版本,以便更好地理解原件的内部结构,以便理解我实际上是为解决练习而编写的。

我的问题是:

为什么我需要明确指定currentState 的类型来编译代码?事实上,如果没有 State[Machine, Machine] 类型定义,上面的 sn-p 就无法编译。

为什么编译器不能推断出实际类型?这只是类型推断限制的问题还是我在这里遗漏了什么?

希望我的问题很清楚,谢谢!

【问题讨论】:

    标签: scala functional-programming type-inference


    【解决方案1】:

    如果你没有为get 提供类型参数并且没有明确指定currentState 的类型,Scala 无法知道类型。它必须从 ((currentMachine.coins, currentMachine.candies), newState)val (currentMachine, newState) = currentState.run(s) 向后推断类型,但 Scala 的类型推断功能不足以做到这一点。

    【讨论】:

    • 谢谢,如果我这样做:for { _ &lt;- list s &lt;- get } yield(s.coins, s.candies) 类型推断效果更好?
    • @P3trur0 我不认为它在没有显式类型的情况下总是能工作,但它确实有更好的机会,因为它是一个单一的表达式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-16
    • 2018-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多