automatic casts, как в Kotlin
От: Пельмешко Россия blog
Дата: 13.11.11 21:54
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>Кстати, всем кто улыбнулся прочтя твои строки, очень советую не пробовать немерл. Буквально месяц реального использования и от C# начинается натуральная ломка. Причем это даже если не написать ни одного собственного макроса. А на работе, ведь, могут не дать применять то что хочется.


Да я серьёзно, вот фрагмент одной штуки из одного секретного продукта, над которым я работаю:
    private static IForeachStatement IsYieldForeachPattern(IYieldStatement yieldReturn, IType ienumerableType)
    {
      // detect "yield return $0;" where $0 - reference expression
      var yieldExpression = yieldReturn.Expression as IReferenceExpression;
      if (yieldExpression == null) return null;

      // detect if "yield return $0;" is right inside foreach statement
      var foreachStatement = yieldReturn.Parent as IForeachStatement;
      if (foreachStatement == null)
      {
        // ok, we may be inside single statement block
        var block = yieldReturn.Parent as IBlock;
        if (block == null || block.Statements.Count != 1) return null;

        foreachStatement = block.Parent as IForeachStatement;
        if (foreachStatement == null) return null;
      }

      // detect if $0 resolves into foreach statement iteration variable
      if (foreachStatement.IteratorDeclaration == null) return null;
      var target = foreachStatement.IteratorDeclaration.DeclaredElement;
      var resolved = yieldExpression.Reference.Resolve().DeclaredElement;
      if (!DeclaredElementEqualityComparer.ElementComparer.Equals(target, resolved)) return null;

      // detect the type of foreach statement collection expression
      if (foreachStatement.Collection == null) return null;
      var collectionType = foreachStatement.Collection.Type();
      if (collectionType.IsUnknown) return null;

      // detect if foreach statement collection is convertible into IEnumerable<T>
      var conversionRule = foreachStatement.GetTypeConversionRule();
      if (!conversionRule.IsImplicitlyConvertibleTo(collectionType, ienumerableType)) return null;

      return foreachStatement;
    }

Представляешь как этот треш мог бы выглядеть лишь благодаря паттерн-матчингу? Меня уже трясёт от:
var someNode = node as SomeNode;
if (someNode != null) {
   ...
}
и визиторов всяких.

VD>Я, вообще-то, тоже с юмором. Я ж тебя не первый год знаю.


А мне всегда кажется, что ты меня забываешь. Я к тебе на альтнетике даже знакомиться подходил, а ты сказал, что не знаешь такого

p.s. [немного оффтопа] кстати, а можно-ли на макросах "забабахать" automatic casts, как в Kotlin? Типа:
object x = 0;
if (x is int) {
    int y = x + 1;
    Console.WriteLine(y);
}
или типа:
void Foo(int? x) {
    if (x != null) {
        int y = x + 1; // никаких x.Value
        ...
    }
}

То есть макросом поменять тип некоторой локальной переменной внутри некоторого скоупа. Если такое выражается макросом, то круто вообще!

14.11.11 14:32: Ветка выделена из темы Хабрахабр
Автор: Пельмешко
Дата: 13.11.11
— kochetkov.vladimir
14.11.11 14:32: Перенесено модератором из 'О жизни' — kochetkov.vladimir
Re: automatic casts, как в Kotlin
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.11.11 23:07
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Да я серьёзно, вот фрагмент одной штуки из одного секретного продукта, над которым я работаю:...


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

П>...Представляешь как этот треш мог бы выглядеть лишь благодаря паттерн-матчингу? Меня уже трясёт от:

П>
П>var someNode = node as SomeNode;
П>if (someNode != null) {
П>   ...
П>}
П>
и визиторов всяких.


Ну, да. Тут или говногод вглуб, или Посетители и куча болерплэйта в ширь.

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

Потому когда видишь вблизи Nemerle и , то невольно в голове возникает опять эти...
Сори.

VD>>Я, вообще-то, тоже с юмором. Я ж тебя не первый год знаю.


П>А мне всегда кажется, что ты меня забываешь. Я к тебе на альтнетике даже знакомиться подходил, а ты сказал, что не знаешь такого


Тут еще проблема, что я с трудом сомещаю IRL и ники на форумах. Плюс я тогда еще соображал в одну сторону. Когда "вещаешь" несколько часов мозг тупеет.

Так что еще раз приношу свои извинения.

Для лучшего знакомства нужно где-то собраться и тупо выпить.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: automatic casts, как в Kotlin
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.11.11 23:24
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>[немного оффтопа] кстати, а можно-ли на макросах "забабахать" automatic casts, как в Kotlin? Типа:

П>
П>object x = 0;
П>if (x is int) {
П>    int y = x + 1;
П>    Console.WriteLine(y);
П>}
П>
или типа:

П>
П>void Foo(int? x) {
П>    if (x != null) {
П>        int y = x + 1; // никаких x.Value
П>        ...
П>    }
П>}
П>

П>То есть макросом поменять тип некоторой локальной переменной внутри некоторого скоупа. Если такое выражается макросом, то круто вообще!

Можно.

Чтобы сделать это нужно или доработать стандартные макросы if/when, или ввести свой аналог (например null_guard). Но того же самого можно добиться воспользовавшись оператором match.

В Котлине же сделано нечто большее. Его авторы пытаются сделать язык null-защиденным. Тебе просто не дадут воспользоваться nulable-значением без проверки. Вот это в макросах сделать сложно (хотя и можно, если очень постараться).

На практике, лично мне хватает следующих фишек:
1. Возможности использовать паттерн-матчинг:
match (x)
{
  | Some(x) => // "x" переопределен. Теперь это просто int
    def y = x + 1; // никаких x.Value
    ...

  | None => сообщаем об ошибке
}


2. Оператора ?? позволяющего задать нужное значение по умолчанию.

3. Оператор .? позаимствованный нами из Груви. Он позволяет делать так:
def a = x.?y.?z + 1;

Если переменная x или хотя бы одно из полей будет null, то в результате выражение x.?y.?z вернет 0, и в "a" окажется 1.
Как показала практика — это очень удобно и используется чаще всего.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: automatic casts, как в Kotlin
От: hardcase Пират http://nemerle.org
Дата: 14.11.11 08:43
Оценка: +1 :)
Здравствуйте, VladD2, Вы писали:

VD>3. Оператор .? позаимствованный нами из Груви. Он позволяет делать так:

VD>
VD>def a = x.?y.?z + 1;
VD>

VD>Если переменная x или хотя бы одно из полей будет null, то в результате выражение x.?y.?z вернет 0, и в "a" окажется 1.
VD>Как показала практика — это очень удобно и используется чаще всего.

Ваще-то оператор ?.
/* иЗвиНите зА неРовнЫй поЧерК */
Re: automatic casts, как в Kotlin
От: Jack128  
Дата: 14.11.11 09:13
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Представляешь как этот треш мог бы выглядеть лишь благодаря паттерн-матчингу?


А как он мог бы выглядеть? Вот как "?." здесь поможет упростить код я вижу, а чем пм поможет??
Re[2]: automatic casts, как в Kotlin
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.11.11 12:23
Оценка:
Здравствуйте, Jack128, Вы писали:

П>>Представляешь как этот треш мог бы выглядеть лишь благодаря паттерн-матчингу?


J>А как он мог бы выглядеть? Вот как "?." здесь поможет упростить код я вижу, а чем пм поможет??


Так и поможет. Вместо этой каши будет что-то вроде:

match (yieldReturn.Parent)
{
  | x is IForeachStatement => ...
  | x is IBlock            => ...
  ...
}


Но, если пресмотреться, то это разбор AST. В таких случаях AST описывается на основе вариантов и тогда можно будет распознавать сложные сочетания веток.

Следующий шаг — лфитинг и введение DSL-я для представления языка. Тогда работа будет вестись уже на совершенно другом уровне.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: automatic casts, как в Kotlin
От: YF Германия  
Дата: 14.11.11 12:40
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Но, если пресмотреться, то это разбор AST. В таких случаях AST описывается на основе вариантов и тогда можно будет распознавать сложные сочетания веток.

VD>Следующий шаг — лфитинг и введение DSL-я для представления языка. Тогда работа будет вестись уже на совершенно другом уровне.

А что такое лифтинг?
Re[3]: automatic casts, как в Kotlin
От: Jack128  
Дата: 14.11.11 13:58
Оценка:
Здравствуйте, VladD2, Вы писали:

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


П>>>Представляешь как этот треш мог бы выглядеть лишь благодаря паттерн-матчингу?


J>>А как он мог бы выглядеть? Вот как "?." здесь поможет упростить код я вижу, а чем пм поможет??


VD>Так и поможет. Вместо этой каши будет что-то вроде:


VD>
VD>match (yieldReturn.Parent)
VD>{
VD>  | x is IForeachStatement => ...
VD>  | x is IBlock            => ...
VD>  ...
VD>}
VD>


ну ты полный код опиши ? На Nemerle.

Если апи полностью на вариантах/АТД будет — тогда возможно проще будет, но в текущем варианте — линейный код будет заменен цепочной вложенных match'ей. Понятности это не прибавит.
Re[4]: automatic casts, как в Kotlin
От: hardcase Пират http://nemerle.org
Дата: 14.11.11 14:36
Оценка:
Здравствуйте, YF, Вы писали:

VD>>Следующий шаг — лфитинг и введение DSL-я для представления языка. Тогда работа будет вестись уже на совершенно другом уровне.


YF>А что такое лифтинг?


Превращение структуры данных в код, котрый порождает эту структуру данных.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[5]: automatic casts, как в Kotlin
От: YF Германия  
Дата: 14.11.11 15:20
Оценка:
Здравствуйте, hardcase, Вы писали:

YF>>А что такое лифтинг?

H>Превращение структуры данных в код, котрый порождает эту структуру данных.

я правильно понимаю, что речь идет о макросе, который генерирует эту структуру данных?
Re[6]: automatic casts, как в Kotlin
От: hardcase Пират http://nemerle.org
Дата: 14.11.11 15:28
Оценка:
Здравствуйте, YF, Вы писали:

YF>я правильно понимаю, что речь идет о макросе, который генерирует эту структуру данных?


Нет. Речь идет о квазицитатах. Квазицитаты превращаются в код, который порождает AST, записанное в цитате.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[7]: automatic casts, как в Kotlin
От: hardcase Пират http://nemerle.org
Дата: 14.11.11 15:30
Оценка:
Здравствуйте, hardcase, Вы писали:

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


YF>>я правильно понимаю, что речь идет о макросе, который генерирует эту структуру данных?


H>Нет. Речь идет о квазицитатах. Квазицитаты превращаются в код, который порождает AST, записанное в цитате.


Квазицитаты во вхождениях PM поднимаются (lift) в обычные шаблоны для вариант (алгебраических типов).
/* иЗвиНите зА неРовнЫй поЧерК */
Re[4]: automatic casts, как в Kotlin
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.11.11 17:17
Оценка:
Здравствуйте, YF, Вы писали:

YF>А что такое лифтинг?


С LINQ-ом знаком? Представляешь как выглядит дерево выражений? Вот в шарпе захардкожен лифтинг для подъема кода в деревья выражений. Но тоже самое можно делать с любым языком. А лифтинг — это, грубо говор, поднятие кода в его АСТ.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: automatic casts, как в Kotlin
От: Пельмешко Россия blog
Дата: 14.11.11 20:06
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Здравствуйте, Пельмешко, Вы писали:


П>>Представляешь как этот треш мог бы выглядеть лишь благодаря паттерн-матчингу?


J>А как он мог бы выглядеть? Вот как "?." здесь поможет упростить код я вижу, а чем пм поможет??


Поможет, если API задизайнено под него будет + куча active patterns (не помню как оно в немерлях называется).
Даже одинх automatic casts хватило бы чтобы заметно облегчить эту C#-лапшу...
Re[4]: automatic casts, как в Kotlin
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.11.11 21:46
Оценка:
Здравствуйте, Jack128, Вы писали:

J>ну ты полный код опиши ? На Nemerle.


А смысл? Если решать эту задачу на Немерле, то код буде совсем другой. Всех этих интерфейсов не будет в помине.

Если очень нужно легаси упростить, то тоже есть множество решений.

Например, лобовое:
def isYieldForeachPattern(stmt : IStatment)
{
  | IStatment where(Parent=foreachStatement is IForeachStatement)
  | IStatment where(Parent=IBlock where(Statements=st, Parent=foreachStatement is IForeachStatement)) when st.Count == 1 =>
    def target = foreachStatement.IteratorDeclaration.DeclaredElement;
    def resolved = yieldExpression.Reference.Resolve().DeclaredElement;
    unless (DeclaredElementEqualityComparer.ElementComparer.Equals(target, resolved))
      return null;
    unless (foreachStatement?.Collection.Type().IsValid)
      return null;

    // detect if foreach statement collection is convertible into IEnumerable<T>
    def conversionRule = foreachStatement.GetTypeConversionRule();
    if (conversionRule.IsImplicitlyConvertibleTo(collectionType, ienumerableType)) foreachStatement else null

  | _ => null
}

При этом можно создать ExtensionPattern-Ы, которые будут выглядеть как паттерн-матчинг по вариантным типам, но на самом деле будет раскрываться в приведенные выше паттерны. Это позволит упростить код лобового решения и сделать его читабельным.

Естественно, можно написать макрос, который превращает три строки в одну.

Далее можно воспользоваться ComputationExpressions и создать билдер для монды Maby. В этом случае код будет выглядеть так:
  private static IForeachStatement IsYieldForeachPattern(IYieldStatement yieldReturn, IType ienumerableType)
  {
    comp Maybe
    {
      // detect "yield return $0;" where $0 - reference expression
      defcomp yieldExpression = yieldReturn.Expression maybe IReferenceExpression;

      // detect if "yield return $0;" is right inside foreach statement
      defcomp foreachStatement = yieldReturn.Parent maybe IForeachStatement
      ...
    }
  }


В общем, вариантов масса. Было бы желание.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: automatic casts, как в Kotlin
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.11.11 21:56
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>...куча active patterns (не помню как оно в немерлях называется).


Есть и аналог active patterns из F#. Но они тормоза (в F#-е тоже) по своей природе. Но есть еще ExtensionPattern. Вот это то что доктор прописал! Они отлично подходят для сокрытия пушистости МП по ОО-типам.

Погляди пример МП по LINQ-овским деревьям выражений.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: automatic casts, как в Kotlin
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.11.11 22:12
Оценка: 7 (2)
Здравствуйте, Jack128, Вы писали:

J>но в текущем варианте — линейный код будет заменен цепочной вложенных match'ей. Понятности это не прибавит.


Ты просто не понимаешь всей мощи ПМ. Весь кайф ПМ в его рекурсивности. К примеру, вот такую кучу строк:
      var yieldExpression = yieldReturn.Expression as IReferenceExpression;
      if (yieldExpression == null) return null;

      // detect if "yield return $0;" is right inside foreach statement
      var foreachStatement = yieldReturn.Parent as IForeachStatement;
      if (foreachStatement == null)
      {
        // ok, we may be inside single statement block
        var block = yieldReturn.Parent as IBlock;
        if (block == null || block.Statements.Count != 1) return null;

        foreachStatement = block.Parent as IForeachStatement;
        if (foreachStatement == null) return null;
      }
      ...

можно заменить одним составным паттерном:
| IStatment where(Parent=foreachStatement is IForeachStatement)
| IStatment where(Parent=IBlock where(Statements=st, Parent=foreachStatement is IForeachStatement)) when st.Count == 1 => ...

При этом проверки на null просто не нужны. Паттерн описывает объект. И если объект не соответствует из-за того, что где-то там в нем null-ы, то это не проблема, так как алгоритм построения дерева решений это учитывает автоматом.

Если паттерны кажутся длинными, то можно воспользоваться ExtensionPattern-ом и описать, например, такие расширения:
[assembly: ExtensionPattern(IStatment,
  ParentForeach(foreachStatement)
  = IStatment where(Parent=foreachStatement is IForeachStatement))]

[assembly: ExtensionPattern(IStatment,
  ParentForeachBlock(foreachStatement, statements)
  = IStatment where(Parent=IBlock where(Statements=statements, Parent=foreachStatement is IForeachStatement)))]

тогда код паттернов будет таким:
| ParentForeach(foreachStatement) | ParentForeachBlock(foreachStatement, st) when st.Count == 1 => ...

то есть одна понятная строка, вместо 14 строк-макарон.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: automatic casts, как в Kotlin
От: Ziaw Россия  
Дата: 15.11.11 01:42
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>На практике, лично мне хватает следующих фишек:

VD>1. Возможности использовать паттерн-матчинг:

Было бы очень круто, если бы ПМ выражения можно было использовать в виде обычных булевых. При этом образцы сопоставления были бы видны в блоках if и when.

when (x is IDisposable) // некоторые паттерны уже работают как булевы, осталось доработать if/when
  x.Dispose()

when (x matches [arg1, arg2]) // для остальных можно ввести оператор
{
  f(arg1, arg2)
}
Re[3]: automatic casts, как в Kotlin
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.11.11 11:20
Оценка: 27 (1)
Здравствуйте, Ziaw, Вы писали:

Z>Было бы очень круто, если бы ПМ выражения можно было использовать в виде обычных булевых. При этом образцы сопоставления были бы видны в блоках if и when.


Z>when (x is IDisposable) // некоторые паттерны уже работают как булевы, осталось доработать if/when

Z> x.Dispose()

Вот так как раз не работает, так как тип переменной не изменяется. Так что проверку можно сделать, но тип "х" останется прежним. Можно попробовать подправить.

Потенциально можно переделать макрос when и сделать так, чтобы он вводил для подобных случаев локальную неизменяемую переменную. Но это предотвратит возможность использовать исходную переменную внутри when. Меж тем как минимум одно такое применение есть в компиляторе:
mutable retTy = ty1;

when (!args1.IsEmpty && argsTys.Length == args1.Length)
{
  NList.Iter2(args1, argsTys, trySetType);

  when (retTy is PExpr.Wildcard)
    retTy = PExpr.TypedType(ret);
}


Можно сделать поддержку для другого специального случая — использования оператора as. Тогда описанный тобой код будет выглядеть так:
when ((x is IDisposable) as x)
  x.Dispose()

или даже:
when (x is IDisposable as x)
  x.Dispose()

Ну, или можно отдельный оператор придумать.
Короче — технически это решается легко. Вопрос только в том, что нужно найти красивое синтаксическое решение.

Z>when (x matches [arg1, arg2]) // для остальных можно ввести оператор
Z>{
Z>  f(arg1, arg2)
Z>}


А так, как раз можно. Только вместо устаревшего matches, нужно использовать is. В прочем, вариант со списком не захотел работать. Но я доработал макру when напильником, и теперь это работает.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: automatic casts, как в Kotlin
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.11.11 12:24
Оценка:
Здравствуйте, hardcase, Вы писали:

YF>>я правильно понимаю, что речь идет о макросе, который генерирует эту структуру данных?


H>Нет. Речь идет о квазицитатах.


На самом деле макросы могут квази-цитаты для хитрых языков реализовывать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.