LINUX.ORG.RU

эффекты в многоядерном окамле(как это работает?)

 


0

1

Решил посмотреть многоядерный окамль https://github.com/ocamllabs/ocaml-multicore/wiki. В нём добавили новый механизм - эффекты. Код взял отсюда http://kcsrk.info/ocaml/multicore/effects/2015/05/27/more-effects/ и добавил принтов.

Не понятно что в этом коде возвращает (continue k s). Я так понимаю после этого вызова, код выполняется с места вызова эффекта, с полученным значением s, а дальше? Какая функция присваивается ret?

open Printf

module type STATE = sig
  type t
  val put : t -> unit
  val get : unit -> t
  val run : (unit -> 'a) -> init:t -> t * 'a
end

module State (S : sig type t end) : STATE with type t = S.t = struct

  type t = S.t

  effect Put : t -> unit
  let put v = perform (Put v)

  effect Get : t
  let get () = perform Get

  let run f ~init =
    let comp =
        (printf "running comp\n");
      match (f ()) with
      | x -> (printf "handling x  \n" );
              (fun s -> (s, x))
      | effect (Put s_new) k ->
              (printf "handling new \n" );
              (fun s -> 
                  (printf "inside new, before continue \n");
                  let ret = (continue k ()) in
              (printf "inside new, after continue \n");
              (ret s_new))
      | effect Get k -> 
              (printf "handling get \n" );
              fun s -> 
                  (printf "inside get, before continue \n" ); 
                  let ret = (continue k s) in
                  (printf "inside get, after continue \n" ); 
              (ret s)
    in 
    (printf "before (comp) init \n" );
    let some_v = (comp) in
    (printf "before init \n" );
    (some_v init)
end

module IS = State (struct type t = int end)

let foo () : unit =
  printf "running foo \n";
  printf "%d\n" (IS.get ());
  printf "running foo \n";
  IS.put 42;
  printf "running foo \n";
  printf "%d\n" (IS.get ());
  printf "running foo \n";
  IS.put 21

let _ = (IS.run foo 4)

Результат запуска:

running comp
running foo
handling get
before (comp) init
before init
inside get, before continue
4
running foo
handling new
inside get, after continue
inside new, before continue
running foo
handling get
inside new, after continue
inside get, before continue
42
running foo
handling new
inside get, after continue
inside new, before continue
handling x
inside new, after continue

★★

Не понятно что в этом коде возвращает (continue k s)

Там continuation passing style.

val continue: ('a, 'b) continuation -> 'a -> 'b
anonymous
()
Ответ на: комментарий от anonymous

А где функция из обработчика Put передаётся в функцию из Get?

handling new
inside get, after continue <- вот это как получилось
inside new, before continue

Leron ★★
() автор топика
Ответ на: комментарий от Leron

Ну как как. Он значение отдал в (continue k s) и пошёл дальше. На следующем шаге (вот когда он handling new вывел после Put s_new) управление попало уже дальше и вернуло s_new

А в самом-самом конце он через (some_v init) получил наконец 4 и вся эта ерунда раскрутилась.

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

let range a b =
  let rec range' n k =
    match n with
      n when n<a -> k n
    | n -> range' (n-1) (fun x -> n::(k x))
  in
    range' b (fun _ -> [])
;;

# range 1 10
- : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

anonymous
()
Ответ на: комментарий от Leron

В окамле только нет «настоящих» продолжений, типа call/cc. Что как раз эти эффекты (через perform и continue) и делают, как я понял (в исходники особо не лез, только сигнатуру continue посмотрел).

Но принципиальной разницы нет, кроме передачи управления туда-сюда.

Раскрутить можно точно так же. Но мозги иной раз выносит.

anonymous
()
Ответ на: комментарий от anonymous

Спасибо anonymous, тут то понятно:

range' 2 (fun _ -> [])
match 2 with
 |2 when 2 < 1 ->
 |n -> range' (2 - 1) (fun x -> 2::(fun _ -> []))


match 1 with
 | 1 when 1 < 1 ->
 |n -> range' (1 - 1) (fun x -> 1::(fun x -> 2::(fun _ -> [])))


match 0 with
| 0 when 0 < 1 -> (fun x -> 1::(fun x -> 2::(fun _ -> []))) 0


(fun 0 -> 1::(fun x -> 2::(fun _ -> []))) 

1::(fun 0 -> 2::(fun _ -> []))) 

1::2::(fun 0 -> [])

1::2::[]

А как это произошло я так и не понял.

before init                 <-          тут в функцию из Get 4 передалось
inside get, before continue 
---------------------------------
fun 4 ->
(printf "inside get, before continue \n" );
	let ret = (continue k 4) in                  <- переход в foo
(printf "inside get, after continue \n" );
(ret 4)
---------------------------------

4
running foo 						<- переход в match Put
handling new                                
inside get, after continue <- ??? continue не вызвался для перехода в функцию Get,
                           <-  должно было дальше идти к (printf "before init \n" );

Leron ★★
() автор топика
Ответ на: комментарий от anonymous

Там, я так понял хотят добавить корутины, распределяемые между потоками, как в Go, потому multicore.

Leron ★★
() автор топика
Ответ на: комментарий от Bahamut

await/async - жалкая пародия на эффекты.

anonymous
()
Ответ на: комментарий от Leron

должно было дальше идти к (printf «before init \n» );

с какой радости-то? вообще ни разу.

Hint: comp это не лямбда, возможно, если её сделать лямбдой, то вывод будет чуть понятнее:

before (comp) init
running comp
running foo
 >>> тут perform Get
handling get   <-- тут мы создаём лямбду
before init      <-- а тут посылаем ей 4
inside get, before continue  <-- а тут она эту четверку "забирает" и возвращает в принтф
>>> тут возврат из perform Get
4
running foo
>> тут perform Put 42
handling new    <-- тут создаём лямбду
inside get, after continue  <-- которая сначала попадёт сюда
inside new, before continue  <-- а тут "заберёт" 42, это "вторая часть" лямбды, после продолжения
running foo
...
anonymous
()
Ответ на: комментарий от anonymous

а тут «заберёт» 42

в смысле его заберет Get на следующем шаге:

printf "%d\n" (IS.get())
>> perform Get
handling get
inside new, after continue
inside get, before continue <-- вот тут она "заберёт" 42, которую ей "оставил" perform Put 42
>> конец perform Get
42
anonymous
()
Ответ на: комментарий от Leron

Там, я так понял хотят добавить корутины, распределяемые между потоками, как в Go, потому multicore.

Но есть же Concurrent ML с каналами, почему бы не сделать так же, вместо этой лапши с продолжениями?

korvin_ ★★★★★
()
Ответ на: комментарий от korvin_

вместо этой лапши с продолжениями?

Был же JoCaml, как я понимаю его бросили. Печаль.

Крышеснос с продолжениями тоже никак не могу понять. Что за любовь к извращениям. Это же понять сложно, даже если сам писал, через какое-то время, не говоря о том, чтобы отлаживать.

anonymous
()
Ответ на: комментарий от anonymous

Крышеснос с продолжениями

Это всё какой-то сиерровский квест напоминает. В самом начале пропустил какую-то мелочь - и всё, в конце выясняется, что игру пройти нельзя.

anonymous
()
Ответ на: комментарий от anonymous

Вот эта строчка ничего не делает:

let some_v = (comp) in

Вернее она делает some_v = comp, Так что если заменить (some_v init) на (comp init) то вывод программы вообще не меняется.

comp это похоже что лямбда, которая в match определяется (а что тогда?)

Если нет, то откуда такой порядок вывода:

handling get                                <- тут лямбда определилась
before (comp) init
before init                                    <- тут лямбда вызвалась
inside get, before continue         <- работает

Я имею ввиду, из обработчика Get лямбда дальше вызвалась(после «before init»).
В коде мне не видно почему тоже самое не произошло с лямбдой из обработчика Put.

Leron ★★
() автор топика
Ответ на: комментарий от korvin_

Но есть же Concurrent ML с каналами, почему бы не сделать так же, вместо этой лапши с продолжениями?

Чтобы не копировать данные при пересылке через каналы.

Leron ★★
() автор топика

Вы как-то сильно усложнили код, когда добавили печать. Я полистал оригинальную статью, вроде там в одном абзаце всё рассказано.

Вообще, наверное, стоит начать с более простого примера:

https://github.com/kayceesrk/effects-examples/blob/master/memo.ml

Kakadu
()
Ответ на: комментарий от anonymous

Крышеснос с продолжениями тоже никак не могу понять.

Тоже мне проблема! Все как раз очень легко понять, если аккуратненько завернуть в монаду (это я безотносительно окамля).

Можно и несколько продолжений завернуть в одно монадическое вычисление при необходимости, а такое бывает: одно продолжение - для главной ветви вычисления, второе - для обработки исключительных ситуаций, третье - для экстренной отмены. Создается базовый интерфейс, а дальше просто используется. Все довольно несложно, и уже было придумано до нас!)

А что нагородили в окамле - хз

dave ★★★★★
()
Ответ на: комментарий от Leron

Ну я тогда не знаю, как подробнее объяснить. Вроде тут всё очевидно.

comp это похоже что лямбда

да, я просто предложил ей дополнительную стрелку добавить, чтобы немного пологичнее вывод был. Тут правильно заметили, такой подробный вывод только отвлекает.

которая в match определяется

Похоже, в мультикоре на match дополнительную семантику навесили, он тут больше на try with в цикле похож. Пока юнит не получит (handling x) не успокоится.

anonymous
()
Ответ на: комментарий от dave

аккуратненько завернуть в монаду

Главное не переусердствовать, а вот тут:

А что нагородили в окамле - хз

Похоже на такой случай. Зачем надо совершать подвиг, удаляя аппендицит через ноздрю - вот уж точно х.з.

anonymous
()
Ответ на: комментарий от e1nste1n

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

P.S. Да, эффекты тоже в монаду обёрнуты.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от Bahamut

Ой, кажется теперь окамль заимствует фичи из шарпика

Чо, только заметил? Давно у Гейтса тырят: циклы из бейсика, сокеты из WinNT...

anonymous
()
Ответ на: комментарий от Oxdeadbeef

Ну да. Это ж не мертвые кресты.

anonymous
()
Ответ на: комментарий от anonymous

циклы из бейсика

Не понял, ГЕЙтс изобрел бейсик? Чо?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.