Тестирование монадического кода

Я изучаю Haskell, работая через курс Haskell от Brent Yorgey. Я просто добрался до секции монады, и, хотя я думаю, что (наконец) по-настоящему понимаю, как работать с монадами, я не понимаю, как тестировать код, который их использует.

Упражнение для этого раздела состоит в том, чтобы написать (упрощенное) моделирование рисков, и это требует интенсивного использования монады Rand StdGen. В частности, мы должны написать следующую функцию:

type Army = Int

data Battlefield = Battlefield { attackers :: Army, defenders :: Army }

battle :: Battlefield -> Rand StdGen Battlefield

Он берет начальное поле битвы и запускает симуляцию того, как будет проходить эта битва.

У меня есть реализация для него, но я не понимаю, как ее протестировать. Я не могу "получить" значения внутри Rand StdGen Battlefield, возвращенные battle, поэтому я не могу их распечатать в интерпретаторе GHCI, так как до сих пор я тестировал свой код. Я также не могу понять, как напечатать результат битвы в главной функции Haskell или что-то в этом роде. Как люди проходят тестирование таких функций?

Ответ 1

Вы можете "получить" результат случайного вычисления, используя такие функции, как evalRand и друзей. evalRand принимает значение "start" RandomGen и детерминированно выполняет монадическое вычисление.


Здесь мое ручное, нестрогое объяснение того, что evalRand для:

Одно из различий между монадами и императивным программированием состоит в том, что монада является представлением вычисления, а не самим вычислением. Другими словами, когда Haskell оценивает выражение, подобное a >>= b >>= c (или эквивалентную нотацию do), оно просто кладет кирпичи Lego вместе, так сказать, вычисление не выполняется до тех пор, пока вы не выполните монаду с использованием функции например evalRand.

Для более простого примера, подумайте о том, как создавать функции вместе. . дает вам функцию, которая представляет собой вычисление, выполняемое двумя выполняемыми ею функциями. Вы получаете только возвращаемое значение из этого вычисления, когда вы фактически вызываете функцию с аргументом.

Вот почему многие из монад в стандартной библиотеке (за исключением IO, которые выполняются системой времени выполнения) предоставляют функцию "ловушки", например evalRand. Это то, как вы на самом деле используете вычисление, которое вы только что определили в своем монадическом коде.