Монада обратного состояния в OCaml

Как бы вы реализовали монаду обратного состояния в OCaml? (Поскольку он сильно зависит от лени, я думаю, нужно использовать модуль Lazy из стандартной библиотеки).


person Bob    schedule 24.07.2014    source источник
comment
Как насчет монады Tardis, слишком? Отлично подходит для подсчета очков боулинга, среди прочего .   -  person Boyd Stephen Smith Jr.    schedule 25.07.2014
comment
@BoydStephenSmithJr.: Вы задаете еще вопрос?   -  person Bob    schedule 25.07.2014
comment
Я полагаю, да. Но я подожду ответа на этот вопрос, прежде чем опубликовать свой.   -  person Boyd Stephen Smith Jr.    schedule 25.07.2014
comment
@ Боб, что ты пробовал? Я могу сказать вам, что реализация этого с помощью модуля Lazy работает почти так, как вы ожидаете. Вам нужно только подтверждение существования или у вас есть более конкретный вопрос?   -  person Lambdageek    schedule 25.07.2014
comment
@Lambdageek: мой вопрос: как это сделать без зацикливания в OCaml? Куда бы я ни положил лень, я всегда получаю зацикливание рекурсии. Было бы здорово, если бы вы написали ответ.   -  person Bob    schedule 25.07.2014


Ответы (1)


Я выложил суть решения.

Сложный момент:

type 's l = 's Lazy.t
type ('a, 's) st = 's -> ('a * 's l)

let bind (mx : ('a, 's) st) (f : ('a -> ('b, 's) st)) (s : 's l) : ('b * 's l) =
  (* conceptually we want

         let rec (lazy (y, s'')) = lazy (mx s')
             and (lazy (z, s')) = lazy (f y s)
         in (force z, s'')

     but that's not legal Caml.

     So instead we get back lazy pairs of type ('a * 's l) l and we
     lazily project out the pieces that we need.
   *)
  let rec ys'' = lazy (mx (LazyUtils.join (LazyUtils.snd zs')))
    and (zs' : ('b * 's l) l) = lazy (f (Lazy.force (LazyUtils.fst ys'')) s)
  in (Lazy.force (LazyUtils.fst zs'), LazyUtils.join (LazyUtils.snd ys''))

Как я уже упоминал в комментарии, несколько хитрость заключается в том, что вы не хотите случайно принудительно вычислить состояние слишком рано. К сожалению, чтобы получить правильную взаимную рекурсию, вы также вынуждены временно сделать ответы вычислений (которые текут вперед) ленивыми. Итак, основные правила игры таковы:

  1. Делайте то, что типы говорят вам делать.
  2. Никогда не форсируйте состояние, кроме как в конструкции lazy e.

В частности, LazyUtils.join : 'a Lazy.t Lazy.t -> 'a Lazy.t не может быть:

let join xll = Lazy.force xll

Потому что это заставит внешний преобразователь слишком рано. Вместо этого должно быть:

let join xll = lazy (Lazy.force (Lazy.force xll))

Что выглядит как заикание, но на самом деле корректно задерживает все вычисления.

person Lambdageek    schedule 24.07.2014
comment
Большое спасибо за этот отличный ответ. Я смог заново открыть для себя ваш ответ после изучения ваших уловок. Затем я провел остаток дня, пытаясь обобщить это на случай монады обратного состояния transformer. Но я застрял: я не могу определить привязку. Единственное решение, которое я могу смутно представить, будет использовать mfix монады в параметре, но это само по себе проблематично определить, потому что это приводит к зацикливанию рекурсии. Вот почему я задаю этот другой вопрос. Есть идеи? - person Bob; 25.07.2014