openlang

Преобразование функций в продолжения для глубоких обработчиков

В примере глубоких обработчиков из документации есть такой абзац:

Вычисление f само может выполнять эффекты. Когда f выполняет Yield, его задача приостанавливается и помещается в очередь готовых задач, а планировщик запускает следующую задачу из очереди. Если эффект — Fork f, текущая задача приостанавливается, а новая задача f сразу выполняется хвостовым вызовом spawn f. Обратите внимание, что запускать новую задачу первой — произвольное решение. Можно было бы вместо этого поместить задачу f в очередь готовых задач и сразу продолжить k.

Возможно, я упускаю что-то простое, но рассмотрим такой пример планировщика:

type _ Effect.t += Fork : (unit -> unit) -> unit t
                 | Yield : unit t

(* Round-robin scheduling with concurrency *)
let run (entry : unit -> unit) : unit =
  let pending : (int * (int, unit) continuation) option ref =
    ref None (* exchanger awaiting a partner *)
  in
  let ready = Queue.create () in (* ready-task queue *)
  let schedule cont arg =
    let resume () = continue cont arg in
    Queue.push
      resume ready
  in
  let next () =
    if Queue.is_empty ready then () (* finished *)
    else begin
      let resume = Queue.pop ready in
      resume ()
    end
  in
  let rec start (work : unit -> unit) : unit =
    match work () with
    | () -> next ()
    | exception err ->
        print_endline (Printexc.to_string err);
        next ()
    | effect Yield, cont -> schedule cont (); next ()
    (* How can work be queued at this point? *)
    | effect (Fork work), cont -> schedule cont (); start work
    | effect (Xchg value), cont ->
        begin match !pending with
        | Some (other, saved) -> pending := None; schedule saved value; continue cont other
        | None -> pending := Some (value, cont); next ()
        end
  in
  start entry

Как поместить work в ready?

Переведено с английскогоПоказать оригинал

Разговор продолжается 3

Следуя доводам
  1. Ссылка на ответ

    Думаю, можно обойти вспомогательную функцию schedule и работать непосредственно с ready.

    Вместо записи

        | effect (Fork work), cont -> schedule cont (); start work

    поменяйте порядок, сделав так:

        | effect (Fork work), cont -> Queue.push (fun () -> start work) ready; continue cont ()
    Переведено с английскогоПоказать оригинал
  2. Ссылка на ответ

    Хотя этой версии, видимо, нужна функция start как обёртка, поскольку при вызове work изнутри обработчика сам этот обработчик её не окружает. Как вы думаете, неизбежно ли это, потому что иначе результат выглядит довольно несимметричным по сравнению с планировщиком из примера? Мне просто интересно, почему в Effect.Deep нет примитива, подобного fiber из Effect.Shallow.

    Переведено с английскогоПоказать оригинал

Другие стороны того же вопроса

  1. Посмотрите, пожалуйста, мою реализацию конвейера на go (ещё изучаю язык)

    Всем привет, Я изучаю go и его средства конкурентного выполнения, а “конвейер” — один из распространённых шаблонов. Вот моя р…

    Ответы 3Последняя активность go