Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 17.04.10 07:48
Оценка:
Есть тип выражения expr : FixedType. Также есть полиморфный тип comp : TypeVar. Задача состоит в том, что нужно узнать, удовлетворяет ли тип expr типу comp. Все типы получаются динамически во время компиляции.

Сейчас использую проверку expr.TryRequire (comp) по аналогии с реализацией макроса foreach. Но такая проверка у меня невсегда срабатывает.

Например, полиморфный тип comp может выглядеть как void -> option [A]. Тогда если expr есть void -> option [?], то проверка срабатывает. Но если на вход поступает expr с типом void -> option [int-], то проверка обламывается, а не хотелось бы. В чем проблема и как ее решить? Кстати, а что такое int-?
Re: Как определить принадлежность генерику?
От: hardcase Пират http://nemerle.org
Дата: 17.04.10 08:10
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>Кстати, а что такое int-?


Макросы 4 Часть:
Автор(ы): Владислав Юрьевич Чистяков
Дата: 03.09.2009
В данной части статьи рассказывается о том, как работает система вывода типов Nemerle, о том, как с ней могут взаимодействовать макросы Nemerle, и что это дает


Строковое представление переменной типа (выводимое также в отладчике) – это фактически значение свойства Hint, обработанное специальным образом. Свежие переменные обозначаются визуально как вопросительный знак – «?». Если для переменной задана только верхняя граница, то в конце к имени типа дописывается знак минус – «-». Если задана только нижняя граница, то к имени типа дописывается «+». Если же заданы и верхняя граница, и нижняя, то его строковое представлением будет выглядеть как «(L TILL H)», где L – это тип нижней границы, а H – верхней.

/* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 17.04.10 09:25
Оценка:
Здравствуйте, hardcase, Вы писали:

D>>Кстати, а что такое int-?


H>Макросы 4 Часть:
Автор(ы): Владислав Юрьевич Чистяков
Дата: 03.09.2009
В данной части статьи рассказывается о том, как работает система вывода типов Nemerle, о том, как с ней могут взаимодействовать макросы Nemerle, и что это дает


Спасибо. Просмотрел. Все равно для меня остается непонятным, почему для void -> option [int-] не срабатывает TryRequire по более общему типу void -> option [A]. Ведь последний является нижней границей для первого. Или нет? А главное, что поменять, чтобы подобная проверка срабатывала?
Re[3]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 17.04.10 13:46
Оценка:
Вопрос остается окрытым. Тем временем, внутри реализации метода Require класса FixedType (файл MType.n) нашел такие неоднозначные строки:

  match ((this, t)) {
    ...
    | (Fun (f1, t1), Fun (f2, t2)) =>
      // f2.Require (f1) && t1.Require (t2)
      f1.Unify (f2) && t1.Unify (t2)


Как я понимаю, срабатывает именно эта ветка, поскольку имеем функцию void -> option[int-]. Очень сильно смущает закомментированная часть. Что это может значить?
Re[4]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 17.04.10 14:21
Оценка:
Мне кажется, что в этом фрагменте ошибка. Реальное условие должно быть более мягким. Что-нибудь вроде:

  match ((this, t)) {
    ...
    | (Fun (f1, t1), Fun (f2, t2)) =>
      // f2.Require (f1) && t1.Require (t2)
      // f1.Unify (f2) && t1.Unify (t2)
      f1.Unify (f2) && t1.IsGeneralizedBy (t2)


где t1.IsGeneralizedBy (t2) <=> t1 — результат инстанцирования обобщенного t2 или t1 == t2.

Что думаете?
Re[5]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 17.04.10 17:57
Оценка:
Ну, все. Всем спасибо за помощь! Разобрался. Компилятор был прав. Теперь это я понял. Еще понял, что мне нужна была другая постановка задачи. Меня интересует ослабленный тип, где все верхние границы убраны. Пришлось написать проверку вручную. Меня, в общем, интересует, может ли данное выражение хотя бы гипотетически иметь такой-то тип. Если да, тогда применяется совершенно другая генерация кода. Здесь лучше ошибиться в пользу гипотезы.
Re: Как определить принадлежность генерику?
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.04.10 19:43
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>Например, полиморфный тип comp может выглядеть как void -> option [A]. Тогда если expr есть void -> option [?], то проверка срабатывает. Но если на вход поступает expr с типом void -> option [int-], то проверка обламывается, а не хотелось бы. В чем проблема и как ее решить? Кстати, а что такое int-?


Типы A и int не совместимы. Стало быть void -> option[A] и void -> option[int-] тоже.

Тут компилятор совершенно прав.

Лучше опиши задачу более детально.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Как определить принадлежность генерику?
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.04.10 19:45
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>Ну, все. Всем спасибо за помощь! Разобрался. Компилятор был прав. Теперь это я понял. Еще понял, что мне нужна была другая постановка задачи. Меня интересует ослабленный тип, где все верхние границы убраны.


В твоем примере верхние границы не причем. У тебя вложенный параметр типа совершенно не совпадает. Может быть тебе нужно было сравнивать тип с чем-то воде void -> option[?] где "?" — это любой тип? Тогда нужно было сначала сформировать тип где вкачетве параметра типа option[T] задан VarType.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 20.04.10 05:42
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Лучше опиши задачу более детально.


Наверное, забыл упомянуть, что A — параметр типа. Поэтому int должен соответствовать. Но я так понял, что int- означает, что здесь может вывестись int, а может и нет. Никаких гарантий еще нет. Как сложатся звезды. Поэтому int- не будет ни TryRequire, не TryUnify для A. (я правильно понимаю?)

Первоначально мне нужно было хотя бы гипотетическое соответствие. То есть, может ли выражение иметь такой-то тип. В общем, взял код FixedType.Unify и просто переписал под себя. Работает. Но теперь я понял, что и это мне не нужно.

Я все пекусь о тех самых вычислительных выражениях. Еще раз перечитал спеки F# и понял, что "монадность" выражения можно целиком определить по синтаксическому разбору без привлечения вывода типов и без Typer.DelayMacro. Эта монадность возникает только в выражениях let!, do! и use! Все. Если какое-то выражение вне этого шаблона будет действительно иметь монадический тип, то оно будет вычислено, а затем просто проигнорировано, о чем будет выдан соответствующий warning компилятора. Вот, зачем нужен do!. Короче говоря, все можно решить гораздо проще. Но над этим стоит еще подумать.

Кстати, мне не удалось ввести синтаксическую конструкцию def!. Когда делаю так

  public macro @bind (_expr)
  syntax ("def", "!", _expr) { <[ () ]> }


то компилятор уже в другой сборке начинает ругаться при разборе обычных def. Он начинает ожидать "!". Сейчас думаю использовать такие ключевые слова с добавкой comp: defcomp, usingcomp, docomp, returncomp и yieldcomp. Но с восклицательным знаком выглядело бы идиоматичнее и красивее.
Re[3]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 20.04.10 05:57
Оценка:
Да, забыл. Монадность еще возникает в return! и yield! То есть, достаточно одного синтаксического разбора.
Re[3]: Как определить принадлежность генерику?
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.04.10 13:51
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>Наверное, забыл упомянуть, что A — параметр типа. Поэтому int должен соответствовать. Но я так понял, что int- означает, что здесь может вывестись int, а может и нет.


Параметров типов в конкретных типах быть не может. Они заменяются свободными переменными типов (VarType).

D>Никаких гарантий еще нет. Как сложатся звезды. Поэтому int- не будет ни TryRequire, не TryUnify для A. (я правильно понимаю?)


int- — это int или его базовые классы (интерфейсы в данном случае).

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

D>Первоначально мне нужно было хотя бы гипотетическое соответствие. То есть, может ли выражение иметь такой-то тип. В общем, взял код FixedType.Unify и просто переписал под себя. Работает. Но теперь я понял, что и это мне не нужно.


Ну, ты уж разберись, что нужно.

А вообще, если код не закрыт, то лучше дал бы ссылку на него или прислал бы мне преокт, а я бы поглядел и подправил.

D>Я все пекусь о тех самых вычислительных выражениях. Еще раз перечитал спеки F# и понял, что "монадность" выражения можно целиком определить по синтаксическому разбору без привлечения вывода типов и без Typer.DelayMacro.


Это хорошо, так как макро будет проще, а чем проще код тем он надежнее.
В прочем знание типов может расширить возможности или сделать код гибче.

D> Эта монадность возникает только в выражениях let!, do! и use! Все.


Вот и мне так показалось. По этому я был довольно удивлен услышив от тебя о необходимости анализа типов.

D>Кстати, мне не удалось ввести синтаксическую конструкцию def!. Когда делаю так


D>
D>  public macro @bind (_expr)
D>  syntax ("def", "!", _expr) { <[ () ]> }
D>


D>то компилятор уже в другой сборке начинает ругаться при разборе обычных def.


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

D> Он начинает ожидать "!". Сейчас думаю использовать такие ключевые слова с добавкой comp: defcomp, usingcomp, docomp, returncomp и yieldcomp. Но с восклицательным знаком выглядело бы идиоматичнее и красивее.


Можно сделать так:
  public macro @bind (_expr)
  syntax ("!!", "def", _expr) { <[ () ]> }

и соответственно использование:
!!def a = ...;
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 20.04.10 16:45
Оценка: 138 (1)
Здравствуйте, VladD2, Вы писали:

VD>А вообще, если код не закрыт, то лучше дал бы ссылку на него или прислал бы мне преокт, а я бы поглядел и подправил.


Я могу прислать старый, но гораздо интереснее новый, о котором ниже. Кстати, куда его слать? Лицензия BSD как у поляков.

VD>Жаль. Надо будет посмотреть, может можно что-то в компиляторе подправить, чтобы он пропускал такой синтаксис.


Пока остановился на defcomp и тому подобном. Comp можно воспринимать как сокращение от computation или comprehension.

VD>Можно сделать так:

VD>
VD>  public macro @bind (_expr)
VD>  syntax ("!!", "def", _expr) { <[ () ]> }
VD>

VD>и соответственно использование:
VD>
VD>!!def a = ...;
VD>


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

Сейчас у меня обрабатываются конструкции def, defcomp (let! в F#), mutable, return, returncomp (return! в F#), yield, yieldcomp (yield! в F#), if, when, unless и while. Сделал list, array и enumerable comprehension по аналогии с F#. Enumerable значит, что генерится значение типа IEnumerable[T]. В F# это будет seq.

Вот, рабочий пример:

    def coll = 
      comp enumerable
      {
        mutable i = 0;
        while (i < x)
        {
          yield i;
          i ++;
        }
      }


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

List comprehension выглядит как comp list {...}. Array comprehension — comp array {...}. Enumerable — comp enumerable {...}. Общая схема — comp builder {...}, где builder должен быть объектом, реализующим нужные методы как в F#. Генерацию можно переопределить.

Что касается производительности. List comprehension эффективен также как и встроенный в Nemerle. Используется та же самая техника с аккумулятором. Макрос здесь не имеет накладных расходов. Array comprehension эффективен. Тоже используется аккумулятор. Enumerable comprehension сложнее. Там создаются временные подпоследовательности. Но думаю, что скорость терпима.
Re[5]: Как определить принадлежность генерику?
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.04.10 17:32
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>Я могу прислать старый, но гораздо интереснее новый, о котором ниже. Кстати, куда его слать? Лицензия BSD как у поляков.


Если мне, то можно на мыло (в профиле).

Если есть желание сделать это дело доступным для всех, то лучше всего просто расположить код в каталоге snippets. Для того чтобы можно было менять немерловый SVN нужно прислать мне на мыло гуглевый эканут. Я добавлю его в список разработчиков.

D>Пока остановился на defcomp и тому подобном. Comp можно воспринимать как сокращение от computation или comprehension.


Ну, в общем-то это все ерунда. Синтаксис можно потом улучшить. Главное чтобы это все работало как надо.

D>Фу, восклицательные знаки впереди не ком-иль-фо.


А мне нравится! (с)

D>Сейчас у меня обрабатываются конструкции def, defcomp (let! в F#), mutable, return, returncomp (return! в F#), yield, yieldcomp (yield! в F#), if, when, unless и while. Сделал list, array и enumerable comprehension по аналогии с F#. Enumerable значит, что генерится значение типа IEnumerable[T]. В F# это будет seq.


list comprehension и так был, только реализован напрямую в виде макроса:
http://nemerle.googlecode.com/svn/nemerle/trunk/macros/Util.n (смотри module ListComprehensionHelper)

D>Вот, рабочий пример:

D>
D>    def coll = 
D>      comp enumerable
D>      {
D>        mutable i = 0;
D>        while (i < x)
D>        {
D>          yield i;
D>          i ++;
D>        }
D>      }
D>


Прикольно!

Вот видишь, а ты сомневался, что на макросах такое возможно .

Я правильно понимаю, что coll будет иметь тип IEnumerable[int]?

D>Можно использовать и yieldcomp. Тогда будет подставляться вся подпоследовательность.


Вот тут я разницу не уловил.

D>Потом постараюсь найти интересные примеры.


D>List comprehension выглядит как comp list {...}. Array comprehension — comp array {...}. Enumerable — comp enumerable {...}. Общая схема — comp builder {...}, где builder должен быть объектом, реализующим нужные методы как в F#. Генерацию можно переопределить.


D>Что касается производительности. List comprehension эффективен также как и встроенный в Nemerle. Используется та же самая техника с аккумулятором.


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

D> Макрос здесь не имеет накладных расходов. Array comprehension эффективен. Тоже используется аккумулятор. Enumerable comprehension сложнее. Там создаются временные подпоследовательности. Но думаю, что скорость терпима.


Что за подпоследовательности?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 21.04.10 03:51
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Если мне, то можно на мыло (в профиле).


VD>Если есть желание сделать это дело доступным для всех, то лучше всего просто расположить код в каталоге snippets. Для того чтобы можно было менять немерловый SVN нужно прислать мне на мыло гуглевый эканут. Я добавлю его в список разработчиков.


Послал тебе вчера со своего гуглового аккаунта. Поучаствовать не против. Например, я мог бы заменить то, что есть сейчас в каталоге snippets/ComputationExpressions. Там сейчас лежит только набросок.

VD>list comprehension и так был, только реализован напрямую в виде макроса:

VD>http://nemerle.googlecode.com/svn/nemerle/trunk/macros/Util.n (смотри module ListComprehensionHelper)

Да, видел. Похоже на хаскелевский list comprehension. Кстати, у них нотация do есть обобщение list comprehension. В F# пошли несколько другим, но похожим путем.

D>>Вот, рабочий пример:

D>>
D>>    def coll = 
D>>      comp enumerable
D>>      {
D>>        mutable i = 0;
D>>        while (i < x)
D>>        {
D>>          yield i;
D>>          i ++;
D>>        }
D>>      }
D>>


VD>Я правильно понимаю, что coll будет иметь тип IEnumerable[int]?


Да, именно. Кстати, в тех исходниках, что я прислал, не хватает одного Delay при генерации коллекции coll в самом начале, когда оборачивается результат. Добавил. В общем, надо изменить метод EnumerableBuilder.Run:

    
    public Run (expr : PExpr) : PExpr
    {
      <[ EnumerableHelper.Delay (() => $expr) ]>
    }


Сейчас есть зависимость от самой сборки ComputationExpressions.dll. Это из-за вспомогательного класса EnumerableHelper. Потом его вынесу в отдельную dll. Будет маленький и независимый ComputationExpression.Runtime.dll.

D>>Можно использовать и yieldcomp. Тогда будет подставляться вся подпоследовательность.


VD>Вот тут я разницу не уловил.


В прямом смысле подставляется подпоследовательность. Примерно так:

def upTo (n : int) : IEnumerable [int]
{
  comp enumerable
  {
    mutable i = 0;
    while (i < n)
    {
      i ++;
      yield i
    }
  }
}

def manyTimes : IEnumerable [int] =
  comp enumerable
  {
    yieldcomp (upTo (2));  // 1 2
    yield 100;             // 100
    yieldcomp (upTo (3));  // 1 2 3
    yield 100;             // 100
    yieldcomp (upTo (10)); // 1 2 3 .. 10
  }


Иногда yieldcomp — очень полезная штука. Как и returncomp (aka return! в F#). С точки зрения монады yieldcomp и returncomp очень близки.

D>>Что касается производительности. List comprehension эффективен также как и встроенный в Nemerle. Используется та же самая техника с аккумулятором.


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


Я думаю, что такая же эффективность. Эффективнее просто быть не может. Макрос comp практически не создает ничего лишнего в случае list comprehension. Только работа с аккумулятором в тех местах, где прописан yield и yieldcomp. Сначала все складывается в обратном порядке, а потом в конце вызывается Rev ().

D>> Макрос здесь не имеет накладных расходов. Array comprehension эффективен. Тоже используется аккумулятор. Enumerable comprehension сложнее. Там создаются временные подпоследовательности. Но думаю, что скорость терпима.


VD>Что за подпоследовательности?


В вспомогательном классе EnumerableHelper. Yield оборачивается в вызов Singleton. Yieldcomp подставляется как есть. Когда же соединяем две подпоследовательности, то вторая сначала делается отложенной через Delay, а потом обе подпоследовательности соединяются через Append. Cлучай конструкции while для последовательностей прописываем особо в методе While вспомогательного класса.

Фишка в том, что синтаксический разбор всех вычислительных выражений один и тот же, будь то list comprehension или какое-нибудь продолжение. Генерация же кода настраивается под вычислительное выражение.
Re[7]: Как определить принадлежность генерику?
От: WolfHound  
Дата: 21.04.10 09:18
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>Послал тебе вчера со своего гуглового аккаунта. Поучаствовать не против. Например, я мог бы заменить то, что есть сейчас в каталоге snippets/ComputationExpressions. Там сейчас лежит только набросок.

Меняй.

D>В прямом смысле подставляется подпоследовательность. Примерно так:

хъ
D>Иногда yieldcomp — очень полезная штука. Как и returncomp (aka return! в F#). С точки зрения монады yieldcomp и returncomp очень близки.
А вот тут ИМХО можно смотреть на типы.
... << RSDN@Home 1.2.0 alpha 4 rev. 1305>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[8]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 21.04.10 10:00
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Меняй.


OK. Как будет доступ.

D>>Иногда yieldcomp — очень полезная штука. Как и returncomp (aka return! в F#). С точки зрения монады yieldcomp и returncomp очень близки.

WH>А вот тут ИМХО можно смотреть на типы.

Думаю, что можно и без этого. Даже хотелось бы, чтобы сохранить для пользователя возможность переопределять генерацию кода. По идее должен быть тип монады. В случае (1) enumerable comprehension это проверяется. В случае (2) общего билдера все определяется типом задаваемых методов YieldComp и ReturnComp. Если же кастомный билдер сам генерит код (3), то все будет так, как ему вздумается. В случае (4) list и array comprehension на типы забивается. Считается, что идет работа непосредственно с кодом, который меняет аккумулятор. Поэтому как бы тип монады вытекает сам собой. Значение в монаде — это сам код PExpr для list и array comprehension.

Тут выходит интересная вещь с конструкцией for для монадического выражения. Может быть еще немонадический for — там просто тупо подставляется сам for. В общем случае монадический for будет преобразован в foreach. Придется искусственно создавать объект типа IEnumerable[_]. Знаю как это можно сделать через ленивый поток (stream). На стек возвратов это не будет влиять.

В общем, получается довольно интересная вещь, которая до фига рутины автоматизирует. Особенно в случае enumerable comprehension. Я посмотрел рефлектором — там столько лямбд и замыканий создается...
Re[9]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 21.04.10 10:15
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>>>Иногда yieldcomp — очень полезная штука. Как и returncomp (aka return! в F#). С точки зрения монады yieldcomp и returncomp очень близки.

WH>>А вот тут ИМХО можно смотреть на типы.

Немного гоню. Для list и array comprehension тоже проверяется тип по аккумулятору. Он должен быть единым. А вот билдер, генерирующий сам код, может делать, что угодно с yieldcomp и returncomp, также как и с yield и return. Просто эти конструкции фиксирует сам факт наличия монады для всех зависимых выражений, что может влиять на генерацию. Но в ядре макроса comp сами типы не проверяется, кроме, может быть, самой последней стадии — оборачивания значения в конечный результат. Но там особая история.
Re[9]: Как определить принадлежность генерику?
От: WolfHound  
Дата: 21.04.10 10:52
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>В случае (2) общего билдера все определяется типом задаваемых методов YieldComp и ReturnComp.

А зачем вообще нужны YieldComp и ReturnComp?
Ведь мы можем просто использовать перегруженные Yield и Return и таким образом избавиться от лишних ключевых слов.
... << RSDN@Home 1.2.0 alpha 4 rev. 1305>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[10]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 21.04.10 11:31
Оценка:
Здравствуйте, WolfHound, Вы писали:

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


D>>В случае (2) общего билдера все определяется типом задаваемых методов YieldComp и ReturnComp.

WH>А зачем вообще нужны YieldComp и ReturnComp?
WH>Ведь мы можем просто использовать перегруженные Yield и Return и таким образом избавиться от лишних ключевых слов.

Проблема в том, что YieldComp/ReturnComp — получают уже значения в монаде, а Yield/Return только создают монаду по заданному аргументу. По идее ReturnComp (m) = builder.Bind (m, x => builder.Return (x)). Но так делать неразумно. Чаще всего ReturnComp (m) просто превращается в m. В случае вычисления с Yield/YieldComp метод Bind может быть и не определен. Поэтому непонятно как выводить YieldComp из Yield.

К тому же, так сделано в F# Должен сказать, что Дон Сайм очень хорошо все придумал и обдумал детали. Удивительно просто.
Re[10]: Как определить принадлежность генерику?
От: dsorokin Россия  
Дата: 21.04.10 11:35
Оценка:
Здравствуйте, WolfHound, Вы писали:

D>>В случае (2) общего билдера все определяется типом задаваемых методов YieldComp и ReturnComp.

WH>А зачем вообще нужны YieldComp и ReturnComp?
WH>Ведь мы можем просто использовать перегруженные Yield и Return и таким образом избавиться от лишних ключевых слов.

А главное, что YieldComp и ReturnComp обозначают, что в таком то месте есть монада. Это влияет на генерацию кода в целом. И не нужны типы. Все уже просчитано до нас
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.