stdray: (Default)
stdray ([personal profile] stdray) wrote2012-05-11 07:40 am

NEMERLE и поток сознания

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

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


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

Спасибо мне! Сыграл в КО! Однако с языками программирования часто не прокатывает. Осилил %langname% и все, хватит, отвалите. Мне труп страуса нашептал, что у меня в руках ИНСТРУМЕНТ ВЫСОКОГО УРОВНЯ, а потом, прищурившись, добавил: "Полный по Тьюрингу, даааа". Ну теперь вообще ахуеть. Я даже слушать вас не хочу: жаба тормозит, сишка хуже ассемблера, додиез не знаю, зачем придумали, когда у них жаба есть, objc - это просто яблочная илитка, на лиспе искуственный интелект так и не зделали, лямбда-исчисление? исчисление что? Шли бы вы отсюда со своими историями. А потом человеку изменяет сознание и он начинает находить свои любимые костыли даже там, где их никогда и не было. Паттерн ПРИСПОСОБЛЕНЕЦ во все поля.

Я вспоминаю, как начав программировать на C#, испытывал физическую необходимость ОСВОБОДИТЬ ПАМЯТЬ и руками устроить связанный список. Потом-то оно уже проще идет. Знакомые вещи определяются сразу, а новые расширяют кругозор. Интересно узнавать новый язык, задаваясь вопросом: "А как я вообще раньше без этого жил?" Как будто собирается некий пазл, образуя целостную картину, а не "Вот тебе походный ножик. Теперь иди - строй дом".

Казалось бы, причем здесь Nemerle? Просто это язык программирования, который, на мой взгляд, заслуживает внимания.

Nemerle - ЯП общего назначения. Я не знаю, что такое общее назначение. Я так мало знаю о программировании микроконтроллеров и о программировании для суперкомьютеров и еще куче областей , что у меня с парнями, работающими в тех сферах, разная специальность. Я понимаю, что хороший программист должен найти себя в любой области, но у нас _настолько_ разные приоритеты, что придется сильно ломать себя. Я пишу средней толстоты эторпрайз (базворды прилагаются), потому для меня важны:
- Надежность. Ведь ничего падать не должно, а если упадет - перезапуститься. Конечно же, все логировать-логировать и алертить. И желательно все это задублировать.
- Прозрачность. Ведь вон та функция "ОТПРАВИТЬ ДЕНЬГИ НА ПЕЧАТЬ", должна отправлять деньги на печать всегда, а не только в состоянии суперпозиции ста тысяч "Если".
- Читаемость. Ну мы же все знаем, что самодокументируемый код - это миф, не так ли? Правда, мы так же знаем, что документирование всего и вся с последующей актуализацией - тот еще кошмар. Потому лучше писать кратко и информативно.
- Тестируемость. Необходимо проверить исправность каждой детальки еще до момента, когда конструктор будет собран. Мы же не хотим ОТПРАВИТЬ ЯЧЕЙКУ НА БОЕВОЕ ДЕЖУРСТВО, ЧТОБЫ ОНА ТАМ ПРОСТО СИГНАЛ РИСОВАЛА ТАБУРЕТОЧКОЙ, так?
- Совместимость. Хаскель исключительно хорош для новых проектов? А если не новых? Вместо библиотек даете возможность их написать? ВЕРНИТЕ НА МЕСТО МОИ ЛЮБИМЫЕ ВЕЛИКИ! БЫЫЫСТРААА!

Nemerle - статически типизуеммый язык программирования под .NET (есть реализация для Mono, но она вроде сломана) со всеми вытекающими. Язык поддерживает ООП в самом распространенном варианте ее реализации с одиночным наследованием, знакомый по Java и C#. Есть:
- Шаблоны (generics)
- Интерфесы
- Методы расширения
- Частичные (partial) классы
- Модификаторы доступа (internal, protected и тд)
- Весь набор, привычный C# программисту. )

Я не вижу смысла, останавливаться на ООП, потому как все достаточно стандартно. Идем далее. Nemerle реализует концепции функционального программирования:
- Нормальные кортежи
- Алгебраические типы данных
- Иммутабельность из коробки
- Паттерн матчинг
- Функции высшего порядка
- Частичное применение
- Вывод типов
- Замыкания
- Перегрузка и определение операторов с возможностью задания приоритета и ассоциативности
- Итераторы
- List comprehensions (не знаю, как переводится)
- Монады (нет, ввод-вывод не в монаде)
- Computation expressions (опять не знаю устоявшегося перевода)
- Опционально двухмерный синтаксис (синтаксис на отступах) через #pragma indent
- Опционально может в ленивость
- Оптимизация хвостовых вызовов
- События и делегаты (я думаю, эти вещи в этом блоке смотрятся лучше)

Вообщем-то весь джентльменский набор, необходимый, чтобы назвать язык функциональным. Стоит отметить, что Nemerle разрешает перегрузку функций (в отличии, например, от F#) и вывод типов спокойно разруливает, какую именно следует вызвать. Мне нравится, но наверное хаскелисты негодуют. Правда, Nemerle требует, чтобы на высшем уровне, то есть в классах и модулях (модуль является обычными статическим классом, для методов которого по умолчанию выставлен модификатор public) необходимо явно аннотировать типы. Я не думаю, что кому-то станет плохо от подобного требования, поскольку даже в Haskell, где система типов способна сама все выводить, аннотации типов все равно приводятся, поскольку по типам можно судить о природе процесса, выполняемого функцией. Еще одной особенностью является возможность "открыть" статических класс, импортировав его содержимое в текущее пространство имен. Может мелочь, но меня всегда раздражали все эти Console.WriteLine.

То есть, на первый взгляд, Nemele ничего особенно не представляет. К тому же разработчики старались не сломать мозг своей потенциальной аудитории, потому сделали синтаксис языка по возможности идентичным C#. Разработчики Scala, как я понимаю, пошли тем же путем. А вот ML-подобный синтаксис с непривычки может очень сильно напугать. Проверено. Язык задумывался, как замена C#, то есть каким C# должен был бы быть. И вы знаете, я согласен. Этих возможностей вполне хватает, чтобы комфортно программировать. F# как раз на этой позиции и находится, то есть со вкусом и без изъебств.

Теперь переходим к Nemerle непосредственно, к тому, что делает язык в большой степени уникальным, - макросам. У меня с макросами всегда были проблемы, сначало звучало страшно, потом было непонятно, где и как и применять. Похоже, это не только моя проблема. Иногда, я думаю, что после изобретения LISP'а, значительная часть развития языков программирования является размышлением на тему "Нужны ли макросы? Какими они должны быть тогда?" LISP'еры хоть и не изобрели искустевеный интелект, но очень многое дали миру: gc, графические интерфейсы и конечно макросы. Мне кажется, Ритчи с Томпсоном догадывались, что всего не предусмотришь, потому запили макры. Сишка используется до сих пор, многие даже испытывают к ней теплые чувства. А более другие люди решили, что макры не нужны, а теперь гордятся тьюринг-полным ТЕМПЛЕЙТО-ДОБРОМ. В Скалочку вот добавляют макросы, а я не видел, чтобы кто-то писал, мол Мартин Одерский не особо рубит в языках программирования.

Так с макросами какие проблемы? Во-первых, сразу начинается нытье, мол развалите весь синтаксис со всей семантикой, превратив язык в непонятную ёбу. Я так же ныл (может еще заною, кто его знает). Но это возврат к разговору о возможности прострелить себе ногу. Меня подзаебали эти разговоры, я решил для себя, что прострелить ногу можно везде и всегда. Мутабельность - прострелил. Энергичность - прострелил. Ленивость - иди чини ноги. Иммутабельность - не прочитал книгу по иммутабельным структурам данных? пиздуй в больницу чинить ноги. И так всегда, не бывает серебрянной пули, как не бывает языков общего назначения, как нет искуственого интеллекта. Именно поэтому, программисты существуют, существуют как профессия, как специалисты, а не просто прослойка между креслом и монитором. Хотя надо отдавать себе отчет в собственных возможностях. Это вам не придти и перевести все критические бизнес-процессы на Хаскель из-за того, что кто-то научился Фибоначи считать. И я не один такой, создатель Лифта такой же точки зрения придерживается. То есть в любом случае некоторый опыт необходим.

Во-вторых, макросы сложные. В Nemerle не сложные, то есть при написании макросов используется тот же самый Nemerle и его возможности, никакого специального DSL не требуется. Держим в уме знание, что макрос - функция с типом List[PExpr] -> PExpr, выполняемая во время компиляции, и все будет хорошо. Макросы гигиеничны, то есть при совпадении имен переменной из макроса и контекста, в котором макрос разворачивается, имя из макроса будет автоматически переименовано, хотя это поведение можно отключать, тогда значение будет связано с ближайшим именем в контексте. Таким образом, кстати, работает макра regex match, которую я использовал в своем решении.

Макросы бывают разными
- Макросы выражения: можно сделать printf("%d%f", x, y), который будет на этапе компиляции проверять соответствие количества параметров и их типов строке форматирования; многие компоненты языка такие как if else, while, for и другие реализованы через такие макросы
- Макросы-операторы: собственно через них реализует переопределение и создание новых операторов. Можно задавать приоритет и ассоциативность. Например, с их помощь в Nemerle сделаны pipe-операторы |> и <|
- Мета-атрибуты - видимо, так называются из-за сходства в записили с атрибутами C#. Позволяют изменить класс, метод, свойство и так далее. Для меня самый впечатляющим примером использования является макрос PegGrammar, который по заданной PEG грамматике создает класс-парсер ее разбирающий. Мой игрушечный пример можно увидеть тут. Если игрушки вам не по душе, то можно глянуть на серьезное использование для разбора грамматики C#.
- Лексические макросы: к сожалению, я еще не успел с этим разобраться, но похоже, макра конструирования XML, напоминающая синтаксис для работы с XML в Scala, относится именно к этой категории.

Мне очень хочется, рассказать о макросах Nemerle, кратко и содержательно, чтобы вы могли испытать то же ВАУ, которое испытываю я. Но, по всей видимости, пока в моем мозгу все по полочкам не разложится, я не смогу внятно излагать. Главное, что я хочу донести, что макросы в Nemerle - это очень гибкое средство, которое красиво и лаконично ложится на язык. Синтаксис языка во много сделан на макросах, кодогенерация требующая в C# сторонных тулзовин отлично реализуется, многие хорошие фичи, которые приходят в C#, как новые элементы языка, делаются на макросах. Я думаю, так ожидаемые Type Providers из F# 3.0 делаются на макросах на ура. То есть я пока не вижу, где заканчиваются возможности, но четко представляю, как применять эти вещи в работе: будь-то автоматическая имплементация всяких IDataErrorInfo с INotifyChanged или как присахарить и обезопасить работу с каким-нибудь SqlCommand. При этом я без проблем, могу использовать существующий C# код, могу использовать в C# проектах код на Nemerle (пока не тестировал). Я считаю, что Nemerle - более удобный и качественный инструмент для решения мох задач.

Надесь, было хоть чуть-чуть интересно. Но если вы внезапно дочитали до сюда и считаете все вышенаписаное бредом идиота , хотя бы начинайте псить. Мне приятно будет)


Update: Как-то получилось, что обсуждение велось параллельно в двух местах: тут и еще одна ветка обсуждения.

[identity profile] Влад Чистяков (from livejournal.com) 2012-05-11 09:18 pm (UTC)(link)
Это не проблема. Макрос парсит исходник. Проблема будет вот так, например:
$"... $(\!) "
так как это не разрешенная эскейп-последовательность.

Но для подхода N2 и это не проблема, потому как там нет парсинга строк вообще. Макрос сможет тупо парсить нужную ему грамматику. Берешь грамматику строки и расширяешь ее как тебе нравится.

[identity profile] Влад Чистяков (from livejournal.com) 2012-05-11 09:20 pm (UTC)(link)
Вот тут http://www.rsdn.ru/forum/nemerle/4732878.flat.aspx есть общее описание.

[identity profile] hls-1141.livejournal.com 2012-05-11 09:24 pm (UTC)(link)
схоронил.

[identity profile] thesz.livejournal.com 2012-05-11 09:53 pm (UTC)(link)
А вот это очень интересно. ;)

STG, "виртуальная машина" ghc, одно время компилировала практически кодированный по Чёрчу код. То есть, замыкание типа Q на входе принимало столько аргументов, сколько конструкторов было у типа Q.

То есть, люди прочитали, поняли и применили. Сравните с подходом Лиспа, в который анономные функции попали чисто случайно, а правила видимости стали совпадать с лямбда-исчислением только в 1983 году.

[identity profile] thesz.livejournal.com 2012-05-11 10:42 pm (UTC)(link)
Вкратце, Хаскель представляет собой сравнительно немного синтаксического сахара над лямбда-исчислением с нормальным порядком упрощения. Среди сахара - удобное использование констант, например, символов или целых, создание типов, case, if...then...else и именование констант.

Мой пример демонстрировал, что сахар из Хаскеля вполне можно убрать. Я убрал чуточку, можно убрать больше. Вплоть до того, что оставить практически чистое лямбда-исчисление.

В Лиспе это тоже можно. Всего лишь надо сменить порядок вычислений лиспа. Ибо ifTheElse с true и false, определённые выше, уже будут работать в Хаскеле, а в Лиспе - нет.

ifThenElse c t e = c t e
true t e = e
false t e = e

test = ifThenElse true (putStrLn "haha!") (putStrLn "hoho!")

Аргументы функции в лиспе энергичные, будут вычислены обе ветви ifThenElse в тесте выше - получите "haha!\nhoho!\n" вместо одной строки. Или придётся имитировать call-by-name/need через замыкания. Но это будет уже не столь просто, как в Хаскеле. То есть, да - можно. Но сложнее. Проще выдумать макросы и специальные формы.

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

[identity profile] xeno-by.livejournal.com 2012-05-11 10:57 pm (UTC)(link)
Есть вопросы.

1) В гайде ты упоминал, что инструкции, написанные внутри блока scope {}, прозрачно применяются, когда мы входим в блок, и отменяются, когда мы из него выходим. Прозрачно отменяются? Это как?

2) Можно ли механизмом публикации символов реализовать области видимости для параметров по умолчанию, а именно что-то типа:

def foo(x: Int, y: Int = x * x) = ??? // компилируется
def bar(x: Int, y: Int = z * z, z: Int = 42) = ??? // ошибка

3) Можно ли в текущей модели символов сделать имплиситы, а именно:

class Foo
class Bar { def bar = ??? }
implicit def foo2bar(foo: Foo) = new Bar
new Foo().<...>

<...> должен увидеть bar

4) Можно ли в текущей модели типов реализовать higher-kinded полиморфизм? Экзистенциальные типы? Зависимые типы? Пересечения и объединения? null-safety цейлоновской мощности?

5) Можно ли реализовать erasure, например, для юзкейса, описанного в треде выше?

6) Аналогичный вопрос про специализацию полиморфных типов для ситуаций, когда рантайм-специализация неприемлема или недоступна.

7) Как планируется реализовывать интеграцию в IDE? Есть ли на эту тему информация? Особенно интересует скорость отклика.

[identity profile] potan.livejournal.com 2012-05-12 11:21 am (UTC)(link)
Суд Oracle-Google показал, что доступность жабьей инфраструктуры скорее минус, чем плюс. Опять-таки требование совместимости с jvm не дает хвостовые вызовы оптимизировать.

[identity profile] thedeemon.livejournal.com 2012-05-12 05:00 pm (UTC)(link)
Вот в Руби (и Перле, вроде) есть другой if:
do_something(x) if condition
Такое насколько легко запилить на ленивых функциях?

Пример с if хрестоматийный, но сильно далеко в этом направлении не уедешь. Макросы же дают свободу в синтаксисе, а ленивость - не очень-то. Вот если бы в хаскеле не было do-нотации, можно ли ее добавить чисто ленивостью? Макросами можно. XML-литералы насколько легко добавить в хаскель одной ленивостью? В язык с макросами легко. Манипуляции с типами функций можно сделать одной ленивостью? Макросами можно. И т.д.

[identity profile] thesz.livejournal.com 2012-05-12 08:15 pm (UTC)(link)
do_something if condition - можно сделать с уже готовым when из Control.Monad:

when' = flip when

f = do
do_something `when'` condition

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

В чём смысл XML-литералов? В "наглядности", это раз (в кавычках из-за плохой читаемости XML), и в "скорости", это два (ибо всё равно строки соединять). Так вот может важнее правильность этого XML, чтобы вам компилятор быстрее сказал об ошибке в проверке по схеме, чем эти два сомнительных преимущества?

Ознакомьтесь с реализацией Agda2 и семейств типов Хаскеля. Обе реализации ленивые. Это касательно манипуляции с типами функций.

[identity profile] Влад Чистяков (from livejournal.com) 2012-05-12 08:23 pm (UTC)(link)
http://www.rsdn.ru/forum/nemerle/4735764.1.aspx

[identity profile] thedeemon.livejournal.com 2012-05-12 08:26 pm (UTC)(link)
Я вот на Clean'e пописал немного и прочувствовал недостаток do-нотации. Ленивость есть, но ее одной мало, синтаксис тоже немаловажен.

Правильность XML тоже важна, и ее макрос тоже может проверить, это не преимущество ленивости.

[identity profile] thesz.livejournal.com 2012-05-13 09:52 am (UTC)(link)
Я объясню. Я за инструменты, которые уменьшают количество труда по всему фронту работ.

Нормальный порядок вычислений именно таков: уменьшает количество труда по всему фронту работ.

Типы данных тоже.

И макросы тоже, если рассудить.

Я перечислил инструменты в порядке понимаемой мной важности.

90% применений макросов может быть убрано нормальным порядком вычислений. См. Алана Кея: http://kazimirmajorinc.blogspot.com/2010/02/alan-kay-on-fexprs.html

Ещё 90% оставшихся 10% - типами (правильность XML).

И вот где уж совсем никак, надо применять макросы. 1% применений макросов действительно оправдан, а не вызван недостатками семантики или системы типов языка.

[identity profile] thesz.livejournal.com 2012-05-13 09:53 am (UTC)(link)
>Я вот на Clean'e пописал немного и прочувствовал недостаток do-нотации

В чём же недостаток?

[identity profile] thedeemon.livejournal.com 2012-05-13 02:53 pm (UTC)(link)
В смысле, ее там недостает. Нет в клине do-нотации, а хотелось бы.

[identity profile] thedeemon.livejournal.com 2012-05-13 02:55 pm (UTC)(link)
Я с рассуждениями в целом согласен, только проценты другие представлял. Но у меня нет четких данных на этот счет, только интуиция.

[identity profile] sassa-nf.livejournal.com 2012-05-13 05:54 pm (UTC)(link)
ого! интересно, если increment можно через Succ выразить, то как же с умножением? :)

[identity profile] sassa-nf.livejournal.com 2012-05-13 05:56 pm (UTC)(link)
а, вот так понятно. Спасибо.
petropavel: (Default)

[personal profile] petropavel 2012-05-15 01:10 pm (UTC)(link)
Не подскажете ли, что у меня не так?
contest.n:10:17:10:31: error : unbound name `ReadLines' in `File'
(Nemerle только что скачал, ничего в нём не понимаю))
petropavel: (Default)

[personal profile] petropavel 2012-05-15 01:16 pm (UTC)(link)
как я понял, ReadLines появился в .net 4,0
microsoft.com/en-us/download/confirmation.aspx?id=17718 - тут был,
Microsoft .NET Framework 4 (Standalone Installer) - оно поставлено.
Вижуал студии у меня нет, джава (извините) основной язык у нас.

[identity profile] stdray.livejournal.com 2012-05-15 01:23 pm (UTC)(link)
Пространство имен открыто? То есть
using System.IO;
сделали?
Если да, то придется выложить куда-то код, на pastebin или еще куда.
Edited 2012-05-15 13:23 (UTC)
petropavel: (Default)

[personal profile] petropavel 2012-05-15 01:24 pm (UTC)(link)
Спасибо, скачал nightly версию net-4.0, с ней собралось.

Код компилирую - Ваш, с конкурса)

upd: лаконичность решения сразила, захотелось "пощупать" руками )
Edited 2012-05-15 13:38 (UTC)

[identity profile] stdray.livejournal.com 2012-05-15 02:00 pm (UTC)(link)
Такое можно во многих яыках сделать. Прелесть Nemerle в том, что в отличии от аналогов, не требуется встроенных в язык средств, их можно сделать самому. Хотя, как видно из комментариев выше, на счет эффективности подобных механизмов существуют разные, небезосновательные мнения. Я не думаю, что у вас возникнут какие-то прблемы с Nemerle, но мне кажется комфортней все же в студии. Взять какую-нибудь бесплатную вроде http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-csharp-express и пользовать.
petropavel: (Default)

[personal profile] petropavel 2012-05-15 02:08 pm (UTC)(link)
Спасибо за советы, воспользуюсь.

На самом деле, в нашей команде сейчас волевым решением начальства внедряется Erlang, отсюда интерес к "функциональщине" в целом )

(Anonymous) 2012-05-15 03:03 pm (UTC)(link)
Нормальный порядок вычислений покрывает хорошо если 1% юзкейсов макросов, так что не знаю, где уж тут "на порядок". А вот зависимые типы, если есть макросы, не нужны, так как любая система типов выражается через макросы. Вообще, если упорядочить системы типов по возрастанию выразительности, то макросы - это точная верхняя грань полученного множества. Причем это формально строго утверждение, безо всяких преувеличений :)

(Anonymous) 2012-05-15 03:07 pm (UTC)(link)
В энергичном языке спец-формой является begin, а в энергичном - if. Так что разницы никакой нет.

Page 3 of 5