Re[4]: Написание программ без...
От: Klapaucius  
Дата: 30.11.10 13:34
Оценка:
Здравствуйте, vdimas, Вы писали:

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


Это не так. Паттерн-мэтчинг проверяет не тип, а конструктор. Т.е. не тип, а тэг.
data Type a b = Constructor1 a | Constructor2 b

тип Type — это тип, известный на этапе компиляции. Constructor1, Constructor2 — конструкторы этого типа, известные на этапе компиляции. Эти конструкторы ПМ и проверяет. Так что полиморфизм тут статический.

V>Чем же такой подход отличается от обычных скриптовых нетипизированных языков? Лишь тем, что еще в статике задаются ограничения мн-в типов значений, принимаемых в динамике. Например, подтипом дотнетного System.Object является любой дотнетный тип (кроме unsafe указателей), а подтипом варианта Хаскеля — лишь ограниченное множество типов, перечисленное в виде конструкторов варианта.


Конструктор алгебраического типа не является его подтипом — конструктор алгтд — это вообще не тип. Через саб/супертайпинг алгтд, бывает, реализуют, особенно в языках для ооп-ориентированных виртмашин вроде CLR или JVM, но каких-то общих выводов из этого делать не следует — это особенности некоторых реализаций и не более того.
Да и сабтайпинг — не обязательно динамика. Бывает полностью статический сабтайпинг.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[8]: Написание программ без...
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.12.10 00:08
Оценка:
Здравствуйте, DarkGray, Вы писали:

S>>Максимальная степень сложности: придумать разумную семантику обработки исключений в CPS.


DG>код cps можно представить как дерево, где каждый лист — это yield, а каждая ветка — это блок кода (хвост блока — тоже является отдельным листом)


DG>при выполнении cps происходит обход дерева в глубину,

DG> если блок кода является циклом, то он обходится много раз подряд пока условие цикла верно,
DG> если блок кода является try, то при возникновении исключения происходит переход на хвост блока (для finally — безусловное, для catch — условное), если исключение не обработано, то происходит дальнейший подъем по дереву с заходом в хвостовые узлы блоков try.
Лично мне непонятно, каким образом определить, в хвост которого блока передавать управление.

DG>зы

DG>семантика обработки исключений в CPS — логичная, и имеет одно трактование, и, имхо, реализовать try/catch вместе с yield-ами микрософту не позволило лишь какие-то сугубо реализационные заморочки
А можно ссылку на более подробное описание этой семантики? Я как-то плохо въезжаю после 24-часового перелёта.

DG>ззы

DG>при этом считается, что cps в C# реализуется через yield-ы
Кем считается? Вот, к примеру, малоизвестный парень Эрик считает, что итераторам до CPS далековато.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Написание программ без...
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 01.12.10 01:42
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, DarkGray, Вы писали:


S>>>Максимальная степень сложности: придумать разумную семантику обработки исключений в CPS.


DG>>код cps можно представить как дерево, где каждый лист — это yield, а каждая ветка — это блок кода (хвост блока — тоже является отдельным листом)


DG>>при выполнении cps происходит обход дерева в глубину,

DG>> если блок кода является циклом, то он обходится много раз подряд пока условие цикла верно,
DG>> если блок кода является try, то при возникновении исключения происходит переход на хвост блока (для finally — безусловное, для catch — условное), если исключение не обработано, то происходит дальнейший подъем по дереву с заходом в хвостовые узлы блоков try.

S>Лично мне непонятно, каким образом определить, в хвост которого блока передавать управление.


текущего. какого еще?

у тебя же дерево, и есть текущий выполняемый лист, этот лист входит в какую-то ветку дерева, которая и есть блок — в хвост которого и надо передать управление.

ззы
приводи непонятный пример, давай его разберем.


DG>>зы

DG>>семантика обработки исключений в CPS — логичная, и имеет одно трактование, и, имхо, реализовать try/catch вместе с yield-ами микрософту не позволило лишь какие-то сугубо реализационные заморочки
S>А можно ссылку на более подробное описание этой семантики? Я как-то плохо въезжаю после 24-часового перелёта.

во-первых, cps полностью подобен обычному коду.
и основная проблема cps аналогична проблеме обычного кода — это goto (хаотичные перескоки между кусками кода).
если от goto избавиться, то мы имеем:
1. иерархию вложенных в друг друга блоков кода(без наличия частично пересекающихся блоков кода). для простоты можно считать, что понятие блок кода(используемое здесь) эквивалентен блоку с фигурными скобками (if/else,цикл,try/using)
2. последовательное исполнение кода (есть только два исключения: break/return/throw — переход к концу одного из блоков в который вложена текущая позиция, continue — переход к началу одного из блоков в который вложена текущая позиция)
3. cps с такой структурой легко представим в виде yield enumerator-а

DG>>ззы

DG>>при этом считается, что cps в C# реализуется через yield-ы
S>Кем считается? Вот, к примеру, малоизвестный парень Эрик считает, что итераторам до CPS далековато.

он там пишет, что можно было сделать более универсально, но не пишет что далековато.
основная проблема, что yield enumerator неудобно стыкуется с callback-ами, и необходимо использовать разные трюки для этого.
Re[10]: Написание программ без...
От: Sinclair Россия https://github.com/evilguest/
Дата: 02.12.10 00:33
Оценка:
Здравствуйте, DarkGray, Вы писали:


S>>Лично мне непонятно, каким образом определить, в хвост которого блока передавать управление.

DG>текущего. какого еще?
DG>у тебя же дерево, и есть текущий выполняемый лист, этот лист входит в какую-то ветку дерева, которая и есть блок — в хвост которого и надо передать управление.
Я не вижу никакого дерева. В каждый момент у нас есть только исполняемая функция и продолжение, которое нужно вызвать, когда она закончится. При этом стека никакого нет, благодаря tail call optimization.


DG>ззы

DG>приводи непонятный пример, давай его разберем.
Ок. Давай посмотрим на простейший пример из википедии.
(define (pyth x y k)
 (* x x (lambda (x2)
   (* y y (lambda (y2)
     (+ x2 y2 (lambda (x2py2)
       (sqrt x2py2 k))))))))

Предположим, у нас один из * кидает исключение (из-за целого переполнения). Где он будет обработан?

DG>во-первых, cps полностью подобен обычному коду.

Не совсем. В CPS я могу реализовывать произвольные виды call flow, а не только вложение и последовательное исполнение.

DG>и основная проблема cps аналогична проблеме обычного кода — это goto (хаотичные перескоки между кусками кода).

DG>если от goto избавиться, то мы имеем:
DG>1. иерархию вложенных в друг друга блоков кода(без наличия частично пересекающихся блоков кода). для простоты можно считать, что понятие блок кода(используемое здесь) эквивалентен блоку с фигурными скобками (if/else,цикл,try/using)
DG>2. последовательное исполнение кода (есть только два исключения: break/return/throw — переход к концу одного из блоков в который вложена текущая позиция, continue — переход к началу одного из блоков в который вложена текущая позиция)
В CPS нет никаких return. "Выход" из блока не происходит никогда.
DG>3. cps с такой структурой легко представим в виде yield enumerator-а

DG>>>ззы

DG>>>при этом считается, что cps в C# реализуется через yield-ы
S>>Кем считается? Вот, к примеру, малоизвестный парень Эрик считает, что итераторам до CPS далековато.

DG>он там пишет, что можно было сделать более универсально, но не пишет что далековато.

Он пишет, что CPS — это более общий случай сопрограмм. А они — более общий случай блоков итераторов.
DG>основная проблема, что yield enumerator неудобно стыкуется с callback-ами, и необходимо использовать разные трюки для этого.
Основная проблема в том, что блоки итераторов реализуют только подмножество CPS. И уже это вызывает неоднозначности в трактовке исключений.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Написание программ без...
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 02.12.10 13:49
Оценка:
S>>>Лично мне непонятно, каким образом определить, в хвост которого блока передавать управление.
DG>>текущего. какого еще?
DG>>у тебя же дерево, и есть текущий выполняемый лист, этот лист входит в какую-то ветку дерева, которая и есть блок — в хвост которого и надо передать управление.
S>Я не вижу никакого дерева. В каждый момент у нас есть только исполняемая функция и продолжение, которое нужно вызвать, когда она закончится. При этом стека никакого нет, благодаря tail call optimization.

CPS-программа (построенная на callback-ах) подобна такой же обычной программе (построенной на goto).
подобие эквивалентное — callback отображается в goto, все остальное остается без изменений.

программу можно представить в виде ориентированного графа: где вершины — это куски кода, а ребра — переходы между кусками кода.

теоретическое отступление (только существенное):

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

и соответственно, если граф можно свести к дереву, то задача здорово упрощается

но орграф можно свести однозначно к дереву только при отсутствие частично перекрывающих циклов
пример: граф A->B->C->D где дополнительно есть ребра C->A и D->B невозможно однозначно свести к дереву.

не образуют частично перекрывающих циклов в коде следующие конструкции: function/call/return плюс блочные конструкции if/then,for/loop/while/break/continue,try/catch/throw, using.
частично перекрывающие циклы образуются только при бесконтрольном применении goto

резюме:
орграф кода можно однозначно свести к дереву при отсутствии goto, образующих частично перекрывающие циклы

следствие 1:
если код представлен в виде дерева — то время жизни (и видимости) переменной от места объявление до выхода из данного узла в вверх

следствие 2:
если код представлен в виде дерева — то можно выделить основную последовательность выполнения кода, который подобен обходу дерева в глубину

следствие 3:
каждый узел дерева после своего выполнения возвращает результат: результат в том числе может быть void(ничего) либо exception(ошибка)

следствие 4:
все операции которые изменяют порядок выполнения можно свести к следующему базису:
1. блок function + call
2. блок loop + break(пропуск следующих ветвей на данному уровне и переход на конец блока loop)+continue(пропуск следующих ветвей на данному уровне и переход на начало блока loop
3. блок if/then/else — в зависимости от условия выполняется блок then, но пропускается else, либо наоборот
4. блок try/catch_finally
catch_finally можно представить как блок вида:

catch_finally(результат)//результат - это результат блока кода, который внутри try (см. п. 3)
{
  //безусловный кусок кода (это аналогично finally из main-stream языков)
  bla-bla
  //эти if-ы аналогичны блокам catch из main-stream языков
  if(результат is NullException)
  {
    bla-bla
    return void;//NullException обработан
  }
  if(результат is ArgumentException)
  {
    bla-bla
    return результат; //аналогично throw; внутри catch
  }
  if(результат is exception) //обработка произвольного исключения
    bla-bla
}



следствие 5:
возврат на предыдущую ветку возможен только косвенно с помощью блока цикла и операции continue

следствие 6:
пропуск выполнения веток может быть только с помощью оператора условия

следствие 7:
при возникновении исключения код обходится аналогично п.2, но при этом пропускаются все блоки, кроме блока catch_finally(результат)
или другими словами, все блоки кроме catch_finally выглядят как:
if (current_result is not exception)
{
  bla-bla
}
else
 return current_result;


следствие 8:
при обходе дерева инвариантом хранимого состояния является:
1. текущее положение в дереве (аналогично регистру ip в процессоре)
2. состояние локальных переменных текущего блока(узла) и всех узлов, в который этот узел вложен (аналогичен стеку из обычного описания работы программы)


S>...


зы
конкретный разбор продолжу позже
Re[12]: Написание программ без...
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.12.10 02:00
Оценка:
Здравствуйте, DarkGray, Вы писали:
DG>CPS-программа (построенная на callback-ах) подобна такой же обычной программе (построенной на goto).
DG>подобие эквивалентное — callback отображается в goto, все остальное остается без изменений.
Не совсем так, но в целом они, конечно же, эквивалентны. turing completeness.
DG>программу можно представить в виде ориентированного графа: где вершины — это куски кода, а ребра — переходы между кусками кода.
Можно.
DG>теоретическое отступление (только существенное):
DG>

DG>но далее известно, что граф с точки зрения обработки намного менее удобный, чем дерево.
DG>при любой обработке необходимы такие операции как: обойти всю структуру, разбить структуру на несколько, склеить структуру из нескольких, отобразить одну структуру на другую, выделить инвариант и т.д. и т.п.
DG>все эти операции для дерева решаются намного проще и однозначнее, чем для графа.
DG>для кода, если он представлен в виде дерева, а не графа, то понятнее решаются задачи — время жизни переменных, движение исключения и т.д.
Это всё хорошо. Вот только в общем случае CPS я не вижу никаких гарантий деревообразности.

DG>но орграф можно свести однозначно к дереву только при отсутствие частично перекрывающих циклов
DG>пример: граф A->B->C->D где дополнительно есть ребра C->A и D->B невозможно однозначно свести к дереву.

DG>не образуют частично перекрывающих циклов в коде следующие конструкции: function/call/return плюс блочные конструкции if/then,for/loop/while/break/continue,try/catch/throw, using.
DG>частично перекрывающие циклы образуются только при бесконтрольном применении goto
Ещё раз намекаю: в CPS нет никаких return.

DG>следствие 1:
DG>если код представлен в виде дерева — то время жизни (и видимости) переменной от места объявление до выхода из данного узла в вверх
Повторно намекаю: в CPS направление "вверх" имеет другой смысл.


DG>следствие 3:
DG>каждый узел дерева после своего выполнения возвращает результат: результат в том числе может быть void(ничего) либо exception(ошибка)
Отлично. Только в случае CPS узел не "возвращает" результат. Как раз наоборот: узел передаёт результат дальше.
То есть мы, конечно, можем попробовать развернуть оба call flow. То есть, функция принимает не одно продолжение, а сразу два: на случай "нормального" и "исключительного" развитий событий. Тем не менее, способ сделать catch всё ещё непонятен.

DG>следствие 4:
DG>все операции которые изменяют порядок выполнения можно свести к следующему базису:
DG>1. блок function + call
DG>2. блок loop + break(пропуск следующих ветвей на данному уровне и переход на конец блока loop)+continue(пропуск следующих ветвей на данному уровне и переход на начало блока loop
DG>3. блок if/then/else — в зависимости от условия выполняется блок then, но пропускается else, либо наоборот
DG>4. блок try/catch_finally
DG>catch_finally можно представить как блок вида:
DG>

DG>catch_finally(результат)//результат - это результат блока кода, который внутри try (см. п. 3)
DG>{
DG>  //безусловный кусок кода (это аналогично finally из main-stream языков)
DG>  bla-bla
DG>  //эти if-ы аналогичны блокам catch из main-stream языков
DG>  if(результат is NullException)
DG>  {
DG>    bla-bla
DG>    return void;//NullException обработан
DG>  }
DG>  if(результат is ArgumentException)
DG>  {
DG>    bla-bla
DG>    return результат; //аналогично throw; внутри catch
DG>  }
DG>  if(результат is exception) //обработка произвольного исключения
DG>    bla-bla
DG>}
DG>

Очередной вопрос: о каком именно return идёт речь? В CPS return нету.

DG>следствие 5:
DG>возврат на предыдущую ветку возможен только косвенно с помощью блока цикла и операции continue

DG>следствие 6:
DG>пропуск выполнения веток может быть только с помощью оператора условия

DG>следствие 7:
DG>при возникновении исключения код обходится аналогично п.2, но при этом пропускаются все блоки, кроме блока catch_finally(результат)
DG>или другими словами, все блоки кроме catch_finally выглядят как:
DG>
DG>if (current_result is not exception)
DG>{
DG>  bla-bla
DG>}
DG>else
DG> return current_result;
DG>


DG>следствие 8:
DG>при обходе дерева инвариантом хранимого состояния является:
DG>1. текущее положение в дереве (аналогично регистру ip в процессоре)
DG>2. состояние локальных переменных текущего блока(узла) и всех узлов, в который этот узел вложен (аналогичен стеку из обычного описания работы программы)

Что имеется в виду под "вложением" в случае CPS? На всякий случай ещё раз напомню: в CPS всякий раз, когда тебе хочется написать return a; ты делаешь вместо этого continue(a).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: Написание программ без...
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 03.12.10 11:27
Оценка:
S>Это всё хорошо. Вот только в общем случае CPS я не вижу никаких гарантий деревообразности.

а в обычном коде видешь?
вот, например, в этом:
void main()
{
  int x = 7;
  int q = 4;
  int z = 7;
  lab2:
  switch (x)
  {
     case 0:
       z = 3;
     case 15:
       printf ("%d", x);
       x = 17;
       goto lab3;
     case 16:
       x -= 40;
       if (x < -100)
        goto lab7;
       z -= 2;
       break;
    case 17:
       z = 5;
       x+=2;
       break;
    case 18:
       z--;
       break;
    lab7:
      q = 0;
    case 19:
       z++;
       q = 10;
       break;
    lab4:
    default:
      while(x > 4 && q > 0)
      {       
        x -= 15;
       lab3:
        q--;
      }
      break;
    case 37:
      return;
  }
  goto lab2;
}


если в коде (что в обычном, что в cps) нет основной последовательности выполнения — значит это спагетти-код, и такой код вместе с разработчиком необходимо оставлять за дверью.



S>Ещё раз намекаю: в CPS нет никаких return.


есть, семантика return — это выход из именованого блока,
после этого передается управление следующему блоку (и эта семантика не зависит — код обычный или cps):
в обычном коде — это называется return, и реализуется через pop указателя со стандартного stack-а и переходе по нему,
в cps — это называется continue, часто реализуется через вызов функции, может реализовываться через переход по указателю, может реализовываться через снятие указателя со _своего_ stack-а и переходе на него.

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

DG>>следствие 1:

DG>>если код представлен в виде дерева — то время жизни (и видимости) переменной от места объявление до выхода из данного узла в вверх
S>Повторно намекаю: в CPS направление "вверх" имеет другой смысл.

ты о семантике, или опять о названиях?

DG>>следствие 3:

DG>>каждый узел дерева после своего выполнения возвращает результат: результат в том числе может быть void(ничего) либо exception(ошибка)
S>Отлично. Только в случае CPS узел не "возвращает" результат. Как раз наоборот: узел передаёт результат дальше.
S>То есть мы, конечно, можем попробовать развернуть оба call flow. То есть, функция принимает не одно продолжение, а сразу два: на случай "нормального" и "исключительного" развитий событий. Тем не менее, способ сделать catch всё ещё непонятен.

и какая разница — как назвать? что блок возвращает или передает результат?
байты что ли от этого по другому в коде лягут?

S>Очередной вопрос: о каком именно return идёт речь? В CPS return нету.


о том самом который делает выход из текущего именноваго блока, и не важно как он при этом называется в том или ином языке


S>Что имеется в виду под "вложением" в случае CPS? На всякий случай ещё раз напомню: в CPS всякий раз, когда тебе хочется написать return a; ты делаешь вместо этого continue(a).


cps можно представить в виде дерева, а в дереве все узлы куда-то вложены.

зы
кстати стоит зафиксировать — какое ключевое отличие CPS от обычного кода.
какой у тебя кстати критерий для выделения cps?
Re[14]: Написание программ без...
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.12.10 13:46
Оценка:
Здравствуйте, DarkGray, Вы писали:

S>>Это всё хорошо. Вот только в общем случае CPS я не вижу никаких гарантий деревообразности.


DG>а в обычном коде видешь?

Давай прекратим рассуждать об обычном коде. DG>если в коде (что в обычном, что в cps) нет основной последовательности выполнения — значит это спагетти-код, и такой код вместе с разработчиком необходимо оставлять за дверью.



S>>Ещё раз намекаю: в CPS нет никаких return.


DG>я вижу что ты хочешь поговорить о кошерном именовании действий в зависимости от контекста?

DG>какой ты хочешь добиться цели, когда говоришь что у кого-то где-то это называют по другому?
Хочу, чтобы мы обсуждали CPS.

DG>ты о семантике, или опять о названиях?

О семантике, конечно. Времена жизни переменных в CPS устроены несколько по-другому, чем в обычном случае.

DG>и какая разница — как назвать? что блок возвращает или передает результат?

DG>байты что ли от этого по другому в коде лягут?
Давай не будем пока углубляться в байты. Байты управляются бэк-эндом; нас пока интересует семантика.

DG>о том самом который делает выход из текущего именноваго блока, и не важно как он при этом называется в том или ином языке



S>>Что имеется в виду под "вложением" в случае CPS? На всякий случай ещё раз напомню: в CPS всякий раз, когда тебе хочется написать return a; ты делаешь вместо этого continue(a).


DG>cps можно представить в виде дерева, а в дереве все узлы куда-то вложены.

Не надо повторять утверждения. Надо обосновывать.
DG>зы
DG>кстати стоит зафиксировать — какое ключевое отличие CPS от обычного кода.
DG>какой у тебя кстати критерий для выделения cps?
Вместо return выполняется вызов переданного в функцию continuation. В очередной раз намекну: в обычном стиле можно вернуться только в одно место — то, откуда вызвали. В CPS я могу делать "возврат" в произвольное количество мест. Именно поэтому CPS более выразителен, чем классический call flow.

Ты почему-то продолжаешь упорно рассматривать обычный код, отказываясь обсуждать CPS. В обычном коде семантика исключений чётко прописана и понятна — это я написал с самого начала. В CPS лично мне она непонятна. Я задал вопрос с примером кода. Ответа нет.
Ок, давай я попробую придумать более близкий пример на C#.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Написание программ без...
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 03.12.10 14:56
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ты почему-то продолжаешь упорно рассматривать обычный код, отказываясь обсуждать CPS. В обычном коде семантика исключений чётко прописана и понятна — это я написал с самого начала. В CPS лично мне она непонятна. Я задал вопрос с примером кода. Ответа нет.


А ты понимаешь, как с помощью продолжений выразить выброс/перехват исключения?
Re[15]: Написание программ без...
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 03.12.10 15:18
Оценка:
S> В CPS я могу делать "возврат" в произвольное количество мест. Именно поэтому CPS более выразителен, чем классический call flow.

так это зло, а не польза (фактически — это будет аналог longjmp-а). в итоге получается write-only-логика работы программы, которую потом через полгода (а тем более если это другой чел) и с литром водки не разобрать.

основная полезность CPS — это возможность "снаружи" прерывать поток управления на время для выполнения какого-то другого действия.
и эта фича обычно используется для того, чтобы в одном hardward-ом потоке иметь возможность крутить несколько логических потоков управления.

например:
static IEnumerable<object> Task(string text)
{
  foreach (var ch in text)
  {
    Console.WriteLine(ch);
    yield return null; 
  }
}
static void Main()
{
  var task1 = Task("Hello").GetEnumerator();
  var task2 = Task("World").GetEnumerator();
  for (;;)
  {
    if(!task1.MoveNext() & !task2.MoveNext())
      break;    
  }
}


прерывание выполнение потока управления очень полезно когда делается долгое внешнее действие: чтение файла, обращение по сети, получение данных из базы, и на cps удобно класть асинхронные куски, записывая при этом их в нормальном виде, а не в виде спагетти, когда одна фигня вызывает другую фигню через callback.

например, вот следующий код (особенно если почистить и половину служебного кода убрать в библиотеку) нагляден и понятен в отличие от спагетти на callback-ах
    static IEnumerable<F> Task(string text)
    {
      foreach (var ch in text)
      {
        Console.WriteLine("{0}: {1}", watch.Elapsed, ch);
        yield return null;
      }
    }
    static IEnumerable<F> Task2(AutoResetEvent wait, string host, int port)
    {
      using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
      {
        var f = new F { wait = wait };
        f.result = socket.BeginConnect(host, port, f.OnEnd, null);
        yield return f;
        try
        {
          socket.EndConnect(f.result);
          Console.WriteLine("{0}: connection to {1}, {2}", watch.Elapsed, host, port);
        }
        catch (Exception exc)
        {
          Console.WriteLine("{0}: not connected to {1}, {2}", watch.Elapsed, host, port);
          //yield return exc
        }
      }
    }
    static void Main()
    {
      var wait = new AutoResetEvent(false);
      watch.Start();

      var task1 = Task("Hello, world");
      var task2 = Task2(wait, "ya.ru", 80);
      var task3 = Task2(wait, "200.0.0.1", 80);

      Run(wait, 1000, task1, task2, task3);

    }

    static void Run(WaitHandle wait, int idletimeout, params IEnumerable<F>[] tasks)
    {
      var _tasks = tasks.Select(task => task.GetEnumerator()).ToArray();
      for (; ; )
      {
        var isRun = false;
        foreach (var task in _tasks)
        {
          if (task.Current == null || task.Current.IsEnd)
          {
            if (task.MoveNext())
              isRun = true;
          }
          else
            isRun = true;
        }
        if (!isRun)
          break;

        wait.WaitOne(1000);
      }

    }

    static System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
    class F
    {
      public AutoResetEvent wait;
      public void OnEnd(IAsyncResult result)
      {
        this.result = result;
        Console.WriteLine("{0}: OnEnd", watch.Elapsed);
        if (wait != null)
          wait.Set();
      }
      public bool IsEnd { get { return result != null && result.IsCompleted; } }
      public IAsyncResult result;
    }

и вот для этого кода имеет смысл рассуждать как должны обрабатываться exception-ы
Re[16]: Написание программ без...
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.12.10 21:35
Оценка:
Здравствуйте, DarkGray, Вы писали:

DG>так это зло, а не польза (фактически — это будет аналог longjmp-а). в итоге получается write-only-логика работы программы, которую потом через полгода (а тем более если это другой чел) и с литром водки не разобрать.

(Вздыхает). Остальное человечество почему-то думает наоборот:

This, along with a restrictive style prohibiting a variety of constructs usually available, is used to expose the semantics of programs, making them easier to analyze. This style also makes it easy to express unusual control structures, like catch/throw or other non-local transfers of control.


DG>основная полезность CPS — это возможность "снаружи" прерывать поток управления на время для выполнения какого-то другого действия.

Основная полезность CPS — возможность строить свои управляющие конструкции в дополнение к тем, которые уже есть в языке.
Возможность асинхронного выполнения — не более чем побочный эффект, связанный с отсутствием стека. Поясняю: "аппаратный" поток вынужден хранить своё состояние (регистры+стек) во время "сна". В CPS стека нет, есть только указатель в текущую функцию. Именно поэтому можно иметь миллионы "логических" потоков управления, не исчерпывая адресное пространство.


DG>и эта фича обычно используется для того, чтобы в одном hardward-ом потоке иметь возможность крутить несколько логических потоков управления.


DG>прерывание выполнение потока управления очень полезно когда делается долгое внешнее действие: чтение файла, обращение по сети, получение данных из базы, и на cps удобно класть асинхронные куски, записывая при этом их в нормальном виде, а не в виде спагетти, когда одна фигня вызывает другую фигню через callback.


DG>например, вот следующий код (особенно если почистить и половину служебного кода убрать в библиотеку) нагляден и понятен в отличие от спагетти на callback-ах

DG>и вот для этого кода имеет смысл рассуждать как должны обрабатываться exception-ы
Для этого — не имеет. Для него уже вопросы обработки исключений тщательно разобраны. Я про них, кстати, упоминал.
А вообще — вы изобретаете poor man's asynchrony.
Тут сразу стоит обсуждать исключения в C#5 с await/async.

Про спагетти на коллбэках — вот например чрезвычайно трудноанализируемый код:

void buttonHandler() {
    // This is executing in the Swing UI thread.
    // We can access UI widgets here to get query parameters.
    final int parameter = getField();
 
    new Thread(new Runnable() {
        public void run() {
            // Now we're in a separate thread.
            // We can do things like hit a database or access
            // a blocking resource like the network to get data.
            final int result = lookup(parameter);
 
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    // Now we're back in the UI thread and can use
                    // the fetched data to fill in UI widgets.
                    setField(result);
                }
            });
        }
    }).start();

По-моему, можно разобраться и без бутылки и даже без комментариев. И это ещё убогий джавасинтаксис с кучей мусора.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: Написание программ без...
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.12.10 21:44
Оценка:
Здравствуйте, lomeo, Вы писали:

L>А ты понимаешь, как с помощью продолжений выразить выброс/перехват исключения?

http://blogs.msdn.com/b/ericlippert/archive/2010/10/22/continuation-passing-style-revisited-part-two-handwaving-about-control-flow.aspx
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: Написание программ без...
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 03.12.10 22:10
Оценка:
S>Основная полезность CPS — возможность строить свои управляющие конструкции в дополнение к тем, которые уже есть в языке.

пример такой полезной управляющей структуры?
Re[17]: Написание программ без...
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 03.12.10 22:24
Оценка:
S> В CPS стека нет, есть только указатель в текущую функцию. Именно поэтому можно иметь миллионы "логических" потоков управления, не исчерпывая адресное пространство.

и та же зачистка dispose-ов делается в ручную?
Re[18]: Написание программ без...
От: Sinclair Россия https://github.com/evilguest/
Дата: 04.12.10 02:47
Оценка:
Здравствуйте, DarkGray, Вы писали:

S>> В CPS стека нет, есть только указатель в текущую функцию. Именно поэтому можно иметь миллионы "логических" потоков управления, не исчерпывая адресное пространство.


DG>и та же зачистка dispose-ов делается в ручную?

Не знаю. CPS в дотнете, как такового, нет. В схеме нет IDisposable. Поэтому не очень понятно, что и как делается.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: Написание программ без...
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 04.12.10 09:46
Оценка:
Здравствуйте, Sinclair, Вы писали:

L>>А ты понимаешь, как с помощью продолжений выразить выброс/перехват исключения?

S>http://blogs.msdn.com/b/ericlippert/archive/2010/10/22/continuation-passing-style-revisited-part-two-handwaving-about-control-flow.aspx

Хм... На лиспе всё это выглядело гораздо симпатичнее.
Ну раз понимаешь, тогда в чём пойнт вот этого высказывания?

В обычном коде семантика исключений чётко прописана и понятна — это я написал с самого начала. В CPS лично мне она непонятна.

Ведь это просто один из паттернов использования самих продолжений.
Re[19]: Написание программ без...
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 04.12.10 12:36
Оценка:
S>Не знаю. CPS в дотнете, как такового, нет. В схеме нет IDisposable. Поэтому не очень понятно, что и как делается.

в том-то и дело, что cps в общем виде без генерализованного потока управления — очень сложен для обозримости, понимания, модификации, объединения с другими концепциями и т.д.

и соответственно, такой cps может быть полезен для реализации каких-то сложных библиотек/framework-ов, но в конечном коде — такой cps подобен обезьяне с гранатой (тут можно провести аналогию, что "cps с произвольными переходами" vs "cps с генерализованным потоком управления" аналогичен "память с произвольным доступом по указателю" vs "память с защищенными доступом").
первая фича — конечно намного мощнее и выразительнее, но намного хуже поддается хоть какому-нибудь контролю и предсказуемости.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.