Scala compiler does not infer type properly
I am reading Chapter 6 of Functional Programming in Scala . I've approached the last exercise of the Chapter (ie: 6.11) and it makes me doubtful about some line of codes.
Here it is the description of the exercise:
Let's implement a finite state automaton that models a simple candy dispenser. The machine has two inputs: you can insert a coin, or you can dispense candy by turning the knob. It can be in one of two states: locked or unlocked. It also tracks how many candies are left and how many coins it contains.
The method simulateMachine
should operate the machine based on a list of inputs and return the number of coins and candies left in the machine. For instance, if the input Machine has 10 coins and 5 candies, and a total of 4 candies are bought successfully, the output should be (14, 1).
The following is a testable unit of my own implementation.
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)
}
)
)
}
}
My doubt is about the following line in simulateMachine
definition:
val getState: State[Machine, Machine] = get
simulateMachine
implements a desugared version I wrote to better understanding the internals of the original for comprehension I actually wrote to solve the exercise.
My question is:
why I need to explicitly specify the type of currentState
to make the code compiling? Indeed, without that State[Machine, Machine]
type definition the above snippet does not compile.
Why the compiler is not able to infer the actual type? Is that just a matter of type inference limitation or am I missing something here?
Hope my question is clear, thank you!
If you don't supply type arguments to get
and don't specify the type of currentState
explicitly Scala has no way of knowing the type. It would have to infer the type backwards from ((currentMachine.coins, currentMachine.candies), newState)
and val (currentMachine, newState) = currentState.run(s)
but Scala's type inference is not powerful enough to do that.
上一篇: 在Vim中自动完成
下一篇: Scala编译器不能正确推断类型