Реализация flatMap() для перехода состояния

Упражнение 6.8, Кьюзано и Бьярнасон, Функциональное программирование в Scala, с. 87 спрашивает, как можно реализовать flatMap() для следующего трейта:

trait RNG {
  def nextInt: (Int, RNG)
}

type Rand[+A] = RNG => (A, RNG)

Ключ ответа дает следующее решение:

def flatMap[A,B](f: Rand[A])(g: A => Rand[B]): Rand[B] =
rng => {
  val (a, r1) = f(rng)
  g(a)(r1) // We pass the new state along
}

Stackoverflow дает много ответов на вопросы о flatMap()/monad, но ни один из них не ответил мне на мои вопросы относительно предпоследней строки кода.

Я не понимаю синтаксис строки

g(a)(r1)

(1) Я не понимаю, как вычисляется g(a)(r1). Какую синтаксическую функцию выполняет (r1)? Строка не иллюстрирует каррирование, я не верю, так как g принимает только один аргумент: A. (2) Если g(a) уже вернет тип Rand[B], то почему строка не заканчивается здесь? (3) какова связь между Rand[B], возвращаемым функцией g(a), и вторым набором скобок: (r1)? (4) если возвращаемый тип этой реализации flatMap() — Rand[B], что равно RNG => (A, RNG), как генерируются закрывающие круглые скобки справа от стрелки? Если бы мне пришлось угадывать, я бы сказал, что они генерируются оценкой (r1), но я не очень понимаю этот код, учитывая мои вопросы с 1 по 3.


person Ben Weaver    schedule 06.06.2017    source источник


Ответы (1)


Помните, что мы вызываем f и g внутри новой анонимной функции, на что указывает строка

rng => { ... }

g возвращает Rand[B] при задании A, поэтому g(a) оценивается как функция от RNG до (A, RNG).

Таким образом, g(a) возвращает функцию, ожидающую RNG в качестве аргумента, затем мы можем вызвать ее с нашим r1, который имеет тип RNG.

Результат вызова (B, RNG). Теперь, поскольку подпись flatMap предполагает, что вы вернете Rand[B], который совпадает с RNG => (B, RNG), а мы возвращаем (B, RNG) внутри нашей функции, она точно соответствует подписи.

person Luka Jacobowitz    schedule 06.06.2017
comment
Спасибо, Лука, это здорово. Имеет смысл. Ключевым знанием для меня является то, что (r1) действительно представляет собой вызов функции, возвращаемой g(a). Я предполагаю, что это похоже на синтаксис карри: f[A,B,C](a)(b): a =› (b =› c), наоборот. - person Ben Weaver; 06.06.2017