[API] Наш ответ рослину?
От: catbert  
Дата: 19.10.11 22:12
Оценка: 18 (1)
http://blogs.msdn.com/b/kirillosenkov/archive/2011/10/18/roslyn-ctp.aspx

Вот с чего и надо копировать API
Re: [API] Наш ответ рослину?
От: Кирилл Осенков Украина
Дата: 20.10.11 01:12
Оценка:
У нашего API ещё далеко не всё гладко. Вот например код для изменения узла в дереве:

        [TestMethod]
        public void UpdateNode()
        {
            string text = "class C { void M() { } }";
            var tree = SyntaxTree.ParseCompilationUnit(text);
            var root = (CompilationUnitSyntax)tree.Root;
            MethodDeclarationSyntax method = root
                .DescendentNodes()
                .OfType<MethodDeclarationSyntax>()
                .First();
            var newMethod = method.Update(
                method.Attributes,
                method.Modifiers,
                method.ReturnType,
                method.ExplicitInterfaceSpecifierOpt,
                Syntax.Identifier("NewMethodName"),
                method.TypeParameterListOpt,
                method.ParameterList,
                method.ConstraintClauses,
                method.BodyOpt,
                method.SemicolonTokenOpt);

            root = root.ReplaceNode(method, newMethod);
            tree = SyntaxTree.Create(tree.FileName, root, tree.Options);
            Assert.AreEqual("class C { void NewMethodName() { } }", tree.Text.GetText());
        }


Очень уж громоздко. Вот думаем, как изменить Update() чтобы не передавать все параметры. Если сделать optional, тогда как присвоить свойству null? Т.е. непонятно, если передавать null, это очистка или "оставь как было"?

Вот такие интересные проблемы возникают с immutable деревьями.
Re: [API] Наш ответ рослину?
От: Ziaw Россия  
Дата: 20.10.11 01:38
Оценка:
Здравствуйте, catbert, Вы писали:

C>http://blogs.msdn.com/b/kirillosenkov/archive/2011/10/18/roslyn-ctp.aspx


C>Вот с чего и надо копировать API


Теперь есть хороший бэкенд, ответом должен быть хороший фронтэнд. N2 можно стартовать.
Re[2]: [API] Наш ответ рослину?
От: Ziaw Россия  
Дата: 20.10.11 02:45
Оценка: 9 (1)
Здравствуйте, Кирилл Осенков, Вы писали:

КО>Очень уж громоздко. Вот думаем, как изменить Update() чтобы не передавать все параметры. Если сделать optional, тогда как присвоить свойству null? Т.е. непонятно, если передавать null, это очистка или "оставь как было"?


Можно сделать хелпер на экспрешенах, по аналогии с bltoolkit linq Update. Будет не оптимально быстро, но для внешнего пользователя подойдет. Он все равно компиляторы не пишет.
Re[2]: [API] Наш ответ рослину?
От: WolfHound  
Дата: 20.10.11 04:39
Оценка: 20 (2)
Здравствуйте, Кирилл Осенков, Вы писали:

КО>Вот такие интересные проблемы возникают с immutable деревьями.

В функциональном программировании есть свои паттерны.
И чтобы не было мучительно больно их нужно изучить, прежде чем делать неизменяемые деревья.
В данном случае zipper вас спасет.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: [API] Наш ответ рослину?
От: Кирилл Осенков Украина
Дата: 20.10.11 05:42
Оценка:
WH>В функциональном программировании есть свои паттерны.
WH>И чтобы не было мучительно больно их нужно изучить, прежде чем делать неизменяемые деревья.
WH>В данном случае zipper вас спасет.

Спасибо, постараюсь разобраться. К своему стыду, абсолютно с этим не знаком.

У каждого паттерна есть область применимости. Т.е. проблема которую он решает, ситуация (или условия), в которой этот паттерн хорошо работает. Просто так тулить паттерны на проблему не глядя смысла не имеет. Будет очень интересно, если бы ты смог рассказать поподробнее, когда нужно и когда не нужно использовать zipper. В идеале если бы был пример эффективной реализации в управляемой среде типа CLR или Java. И чтобы память жрало по минимуму.

У нас (в примере, который я привёл) проблема не с реализацией, а с дизайном API. Собственно реализацию мы сделали и вроде она нашим требованиям удовлетворяет. Мы оптимизировали реализацию на производительность, исходя из GC allocations, memory traffic и пр. особенности .NET платформы. Заменили в нужных местах классы на структуры, сделали внутренние деревья без ссылок на родителей (для переиспользования) и пр.

Т.е. проблема у нас в данном случае — как сделать юзабельный API surface. Какой наименее лаконичный и discoverable код юзер должен написать, чтобы изменить данное свойство данного неизменяемого узла? Один из вариантов, который мы рассматривали, это нагенерировать кучу методов MethodDeclarationSyntax.SetAttributes(..), MethodDeclarationSyntax.ClearAttributes(..), MethodDeclarationSyntax.SetModifiers(..) и пр. которые возвращают новый экземпляр MethodDeclarationSyntax.

Ну Fluent API то есть.

Хотелось сделать, чтобы был один метод с optional parameters (указывать аргументы только для тех параметров, которые хочется изменить). Но всё упирается в то, что в CLR нету nullable reference типов. Кстати, ещё один вариант рассматривали — сделать Option<T> (ну типа Maybe), но у него с очевидностью не очень.

Вот. Будет интересно послушать любые соображения. Повторюсь, я от функциональных паттернов не отнекиваюсь. За первые 10 минут я не разобрался, как zipper применить для нашей проблемы. Буду разбираться дальше, но любая дополнительная помощь очень приветствуется. На первый взгляд кажется что нам хранить "текущий элемент" (дырку, или как они говорят, фокус) не нужно. Т.к. заранее неизвестно какое свойство узла пользователь будет менять.
Re[2]: [API] Наш ответ рослину?
От: dotneter  
Дата: 20.10.11 06:17
Оценка:
Здравствуйте, Кирилл Осенков, Вы писали:

Вопрос в сторону, посмотрел документацию то так и не понял, можно ли с помощью Roslyn сделать подобие макроатрибутов?
... << RSDN@Home 1.2.0 alpha 5 rev. 1536>>
Talk is cheap. Show me the code.
Re[2]: [API] Наш ответ рослину?
От: catbert  
Дата: 20.10.11 06:43
Оценка: +1
Здравствуйте, Кирилл Осенков, Вы писали:

КО>Очень уж громоздко. Вот думаем, как изменить Update() чтобы не передавать все параметры. Если сделать optional, тогда как присвоить свойству null? Т.е. непонятно, если передавать null, это очистка или "оставь как было"?


Я бы добавил методы .WithAttributes, .WithName, .WithXxx, которые возвращают новый объект, но с измененным свойством.
Re[3]: [API] Наш ответ рослину?
От: Ziaw Россия  
Дата: 20.10.11 06:49
Оценка:
Здравствуйте, dotneter, Вы писали:

D>Здравствуйте, Кирилл Осенков, Вы писали:


D>Вопрос в сторону, посмотрел документацию то так и не понял, можно ли с помощью Roslyn сделать подобие макроатрибутов?


А какие проблемы? Код переписывать можно. Берем обычные атрибуты, ищем их в коде и меняем дерево так как нужно.

Надо просто сделать Roslyn.Nemerle, который предоставит нормальный DSL для всего этого (квазицитаты и PM).
Re[3]: [API] Наш ответ рослину?
От: Кирилл Осенков Украина
Дата: 20.10.11 07:14
Оценка:
Z>Можно сделать хелпер на экспрешенах, по аналогии с bltoolkit linq Update. Будет не оптимально быстро, но для внешнего пользователя подойдет. Он все равно компиляторы не пишет.

Интересно, но перформанс нужен... Этот способ слишком расточительный, да и по API usability не очень.
Re[3]: [API] Наш ответ рослину?
От: Кирилл Осенков Украина
Дата: 20.10.11 07:16
Оценка:
C>Я бы добавил методы .WithAttributes, .WithName, .WithXxx, которые возвращают новый объект, но с измененным свойством.
Да, это похоже самый лучший вариант.
Re[4]: [API] Наш ответ рослину?
От: dotneter  
Дата: 20.10.11 07:17
Оценка:
Здравствуйте, Ziaw, Вы писали:

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


D>>Здравствуйте, Кирилл Осенков, Вы писали:


D>>Вопрос в сторону, посмотрел документацию то так и не понял, можно ли с помощью Roslyn сделать подобие макроатрибутов?


Z>А какие проблемы? Код переписывать можно. Берем обычные атрибуты, ищем их в коде и меняем дерево так как нужно.

Вот я не увидел, где можно взять код допустим метода и переписать, везде работают со строками.
... << RSDN@Home 1.2.0 alpha 5 rev. 1536>>
Talk is cheap. Show me the code.
Re[2]: [API] Наш ответ рослину?
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.10.11 11:34
Оценка:
Здравствуйте, Кирилл Осенков, Вы писали:

КО>Очень уж громоздко. Вот думаем, как изменить Update() чтобы не передавать все параметры. Если сделать optional, тогда как присвоить свойству null? Т.е. непонятно, если передавать null, это очистка или "оставь как было"?


Дык, вообще-то, не надо было null за допустимое значение принимать. Сделайте кучу dummy-объектов и назовите их None.

А вообще, тут по-уму нужно язык изменять. Во-первых, было бы разумно ввести в язык возможность здания в качестве значения по умолчанию любых выражений. Просто сериалиппзовать дерево разбора этого выражения и в сериализованной форме подставлять в атрибут хранящий значение по умолчанию. В Скале это реализовано. Я давно хотел для немерла это сделать, но времени не хватает.

Во-вторых, можно было бы сделать фичу аналогичную F#-овской. Во многих МЛ-языках (в том числе и в F#) есть синтаксис для изменения неизменяемых структур. Для шарпа его можно было бы адаптировать как-то так:
MethodDeclarationSyntax  method = ...;
MethodDeclarationSyntax  newMethod  = new method {  Name = Syntax.Identifier("NewMethodName"); }


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

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

Вообще, то что я увидел — очень похоже на то что я пытался создать в R# много лет назад. Бросил я это занятие именно потому, что понял, что работать на таком низком уровне очень тяжело. Правда, времена изменились и у вас есть линк, и типизированное представление (которой иногда очень нужно). Но все же код серьезных преобразований будет жутким.

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

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

Кстати, чтобы показать вашему руководству полезность МП можно было бы сделать нужные вам расширения языка как препроцессор (на рослине же). Например, вот такая вот конструкция:
MethodDeclarationSyntax  newMethod  = new method {  Name = Syntax.Identifier("NewMethodName"); }

совершенно корректна с точки зрения синтаксиса шарпа, но не с точки зрения его семантики, так как за new должен идти тип.
Но что стоит сделать легкий препроцессор который бы заменял бы эту конструкцию на то что ты привел в своем сообщении?
Правильно — ничего. Все что нужно знать — это то что method не является типом. А это как раз не сложно вычислить (при вашей то семантической системе).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: [API] Наш ответ рослину?
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.10.11 11:34
Оценка:
Здравствуйте, Кирилл Осенков, Вы писали:

КО>Хотелось сделать, чтобы был один метод с optional parameters (указывать аргументы только для тех параметров, которые хочется изменить). Но всё упирается в то, что в CLR нету nullable reference типов. Кстати, ещё один вариант рассматривали — сделать Option<T> (ну типа Maybe), но у него с очевидностью не очень.


С очевидностью у него все ОК. Вот с производительностью проблемы. Если только структурой делать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: [API] Наш ответ рослину?
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.10.11 11:44
Оценка:
Здравствуйте, catbert, Вы писали:

C>http://blogs.msdn.com/b/kirillosenkov/archive/2011/10/18/roslyn-ctp.aspx


C>Вот с чего и надо копировать API


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

Средства конструирования новых сущностей и декомпозиция там тоже не на высоте.

Есть так же вещи которые для меня выглядят загадочно. АПИ их вроде как имьютабельный, но под капотом тварится какая-то химия. Реальные данный АСТ-веток получаются из слотов. Что за Green Red я вообще не понял. Подозреваю, что это какая-то оптимизация удешевления неизменяемости.

Кстати, хотелось бы услышать от Киралиа, что это за химия и зачем она нужна.

Но кое-чему поучиться можно. Одна только смелость с которой они практически все (включая Solution) сделали неизменяемым заслуживает уважения!

Качество и проработка тоже на высоте.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: [API] Наш ответ рослину?
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.10.11 11:45
Оценка:
Здравствуйте, Ziaw, Вы писали:

D>>Вопрос в сторону, посмотрел документацию то так и не понял, можно ли с помощью Roslyn сделать подобие макроатрибутов?


Z>А какие проблемы?


Отсутствие точки входа. Ты же не будешь свои проекты компилировать собственной версией компилятора? И уж точно не захочешь, чтобы они не могли работать в IDE (ведь движок интеграции о твоих расширения знать не будет).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: [API] Наш ответ рослину?
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.10.11 11:47
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Надо просто сделать Roslyn.Nemerle, который предоставит нормальный DSL для всего этого (квазицитаты и PM).


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

Было бы куда лучше, если бы они сразу заложили нужные точки входа. Например, в виде событий.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: [API] Наш ответ рослину?
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.10.11 11:49
Оценка:
Здравствуйте, dotneter, Вы писали:

D>Вот я не увидел, где можно взять код допустим метода и переписать, везде работают со строками.


Переписать то как раз можно. Это они сделали. Вот как получить доступ к АСТ во время компиляции, пока не ясно. Боюсь, что ни как.

Еще одна проблема — они полностью закрыли АПИ типизации. Создать объекты семантической модели можно только изнутри компилятора (они все интерналом помечены). А было бы куда лучше, если бы это апи так же было доступно для прямого конструирования. Тогда мы бы без труда навернули немрл (точнее уже свой язык) поверх рослина.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: [API] Наш ответ рослину?
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.10.11 11:50
Оценка:
Здравствуйте, Кирилл Осенков, Вы писали:

C>>Я бы добавил методы .WithAttributes, .WithName, .WithXxx, которые возвращают новый объект, но с измененным свойством.

КО>Да, это похоже самый лучший вариант.

От перегрузок будет рябить в глазах. Лучше чуть расширить язык.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: [API] Наш ответ рослину?
От: WolfHound  
Дата: 20.10.11 11:55
Оценка:
Здравствуйте, Кирилл Осенков, Вы писали:

КО>У каждого паттерна есть область применимости. Т.е. проблема которую он решает, ситуация (или условия), в которой этот паттерн хорошо работает. Просто так тулить паттерны на проблему не глядя смысла не имеет. Будет очень интересно, если бы ты смог рассказать поподробнее, когда нужно и когда не нужно использовать zipper.

Этот паттерн используется для навигационного доступа и "редактирования" неизменяемых структур.

КО>В идеале если бы был пример эффективной реализации в управляемой среде типа CLR или Java. И чтобы память жрало по минимуму.

Устройство функциональных языков не так уж и отличается от CLR и Java.
Так что я думаю, что можно брать любой пример на строгом языке. Nemerle, OCaml, F#,...
Ленивые языки типа хаскеля это отдельная история. Примеры на них вам не подойдут. Разве что на интерфейс посмотреть.
Вот простенький пример на F#: http://www.lshift.net/blog/2010/12/30/f-zipper-with-pipe-forward
Вот еще интересная статья на эту тему: http://www.cs.tufts.edu/~nr/pubs/zipcfg-abstract.html
Там народ сделал оптимизатор С-- на зипперах.

КО>У нас (в примере, который я привёл) проблема не с реализацией, а с дизайном API.

Это я понял.

КО>Собственно реализацию мы сделали и вроде она нашим требованиям удовлетворяет. Мы оптимизировали реализацию на производительность, исходя из GC allocations, memory traffic и пр. особенности .NET платформы. Заменили в нужных местах классы на структуры, сделали внутренние деревья без ссылок на родителей (для переиспользования) и пр.

Короче занимались тем, чем занимаются функциональщики не первое десятилетие.

КО>Т.е. проблема у нас в данном случае — как сделать юзабельный API surface. Какой наименее лаконичный и discoverable код юзер должен написать, чтобы изменить данное свойство данного неизменяемого узла? Один из вариантов, который мы рассматривали, это нагенерировать кучу методов MethodDeclarationSyntax.SetAttributes(..), MethodDeclarationSyntax.ClearAttributes(..), MethodDeclarationSyntax.SetModifiers(..) и пр. которые возвращают новый экземпляр MethodDeclarationSyntax.

Это тоже вариант.
Но он работает только для текущей ветки.
И ее потом придется, каким то образом засунуть обратно в дерево.
Вот это выглядит весьма сомнительно. Особенно если дерево глубокое и нужно поменять несколько методов.
root = root.ReplaceNode(method, newMethod);


КО>Вот. Будет интересно послушать любые соображения. Повторюсь, я от функциональных паттернов не отнекиваюсь. За первые 10 минут я не разобрался, как zipper применить для нашей проблемы. Буду разбираться дальше, но любая дополнительная помощь очень приветствуется. На первый взгляд кажется что нам хранить "текущий элемент" (дырку, или как они говорят, фокус) не нужно. Т.к. заранее неизвестно какое свойство узла пользователь будет менять.

Фокус нужно хранить на тот элемент свойства, которого хочешь поменять.
А у этого фокуса уже сделать методы типа SetModifiers.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.