понедельник, 7 ноября 2011 г.

[prog.flame] Ceylon, Kotlin и теперь вот Xtend – кто-нибудь выживет?

Что-то языки под JVM начинают плодиться как грибы. К давно уже заявившей о себе (но не ставшей пока мейнстримом Scala) хотят присоединиться язык Ceylon от RedHat (подробностей о котором совсем не много), Kotlin от JetBrains (пока еще не достигшем стадии стабильного релиза) и совсем свежий Xtend от Eclipse.

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

С другой стороны, мне не верится в то, что кто-то из них сможет серьезно потеснить Java. Сильно сомневаюсь, что какой-то из этих языков станет мейнстримом. Причины я уже когда-то описывал: сила Java в примитивизме. Посему попытки сделать “лучшую Java” посредством устранения примитивизма обречены.

30 комментариев:

CheatEx комментирует...

Не, всё таки, если верить Вашему же посту по ссылке, не в примитивизме а в унификации. В ней реально мало способов сделать одно и то-же, а простые coding conventions сводят это количество к 1. Кроме того не менее важно, на мой взгляд, практически физическое ограничение на нечитабельность говонокода. Какие-бы темные техники не практиковал автор - время разбора кода другим будет более-менее линейно от объёма кода. То есть можно посадить 100 индусов, если совсем (что редкость) не работает посадил 10 русских.

К сожалению, примитивизм пока являеет единственным более-менее надёжным способом добиться вышеозначенных свойств. Очень надеюсь, что только пока является...

Евгений Охотников комментирует...

@CheatEx:

>Не, всё таки, если верить Вашему же посту по ссылке, не в примитивизме а в унификации.

Боюсь, что мы здесь можем вступить в спор о терминах. Чего не хотелось бы, т.к. в главном мы, похоже, сходимся, а именно: Java делает код одного разработчика на код другого разработчика.

>К сожалению, примитивизм пока являеет единственным более-менее надёжным способом добиться вышеозначенных свойств. Очень надеюсь, что только пока является...

А я думаю, что ничего не изменится. Как только язык даст разработчику чуть-чуть больше средств, так сразу начнется "самовыражение". По C++ным программам это хорошо видно.

имя комментирует...

> Как только язык даст разработчику чуть-чуть больше средств, так сразу начнется "самовыражение".

дженерики в яве дают прекрасную возможность "самовыражения", точнее, мутной акробатики (трудно понимаемой другими)

вот смотри:

http://www.linux.org.ru/jump-message.jsp?msgid=6741496&cid=6748281

попробуй понять, что там происходит, особенно с системой типов ;-)

там правда сишарп, но в яве все это можно 1 к 1 написать

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

притом, что выразительные способности у нее меньше, чем у с++

мутная акробатика, кстати, вполне возможна и в скале, и даже сам одерски демонстрировал что-то похожее (уж не ты ли тут давал ссылку) на то, что делается в с++ через шаблоны (емнип конкретно через expression templates)

в общем, не пущать -- это идиотизм

правильно -- это давать хорошо спроектированные инструменты и гайдлайны на их применение

имя комментирует...

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

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

практический пример: есть волк, коза и копуста

дизайнер языка, вместо того, чтобы предоставить волка и козу в намордниках, а капусту в сумке, предоставил их "как есть"

будет ли несколько рейсов в лодке "самовыражением"? уж точно не "само"

имя комментирует...

теперь я расскажу что в том коде происходит в системе типов

spoiler
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:

там акробатически сделана эмуляция зависимых типов

компилятор явы (точнее, его часть, отвечающая за дженерики) знает, что "Type1 = Type3 равносильно Type1=Type2 и Type3=Type4"; это используется для того, чтобы компилятор знал "х+1=у+1 равносильно х=у" путем кодирования целых чисел в виде Cons< Cons< Cons < Nil > > >

кстати, компилятор с++ такого не знает, почему и повторение такого шаблона на плюсах не получается; чтобы обучить компилятор с++ такому, требуется потрудиться (написать несколько шаблонов), а мне все лень

имя комментирует...

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

Евгений Охотников комментирует...

@имя:

>дженерики в яве дают прекрасную возможность "самовыражения", точнее, мутной акробатики (трудно понимаемой другими)

А еще в Java можно извращаться с анотациями. И с рефлекшеном. И даже с манипуляциями над байт-кодом (чем, вроде, всякие AspectJ и занимаются). Так что путей запутать разработчика в Jave всегда было достаточно.

Дело, вероятно, в другом. Java все-таки достаточно нишевая штука. Сильно прижившаяся в разного рода энтерпрайзе, где не требуется больших возможностей от языка. Там сложности другого рода, для решения которых достаточно простых языковых средств. И Java такие средства дает уже давно. И их достаточно.

Там где Java-возможностей не хватает, уже давно используются ее конкуренты (включая C++).

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

Евгений Охотников комментирует...

@имя:

>кстати, компилятор с++ такого не знает, почему и повторение такого шаблона на плюсах не получается; чтобы обучить компилятор с++ такому, требуется потрудиться

Что-то я не могу понять _условие исходно задачи_ из указанного C#-ного кода. Есть впечатление, что на C++ все это без проблем выражается, разве что чуть иначе.

имя комментирует...

блогспот сожрал < Type2 > и < Type4 > в спойлере из-за угловых скобочек, а я это не заметил; переписывать спойлер мне лень, так что пусть так и будет

Евгений Охотников комментирует...

@имя:

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

имя комментирует...

нам нужно статически гарантировать, что два вектора имеют ОДИНАКОВУЮ длину

понятно, что некоторые случаи, когда они и правда имеют одинаковую длину, будут системой типов не пропускаться, но обязательно, чтобы система типов *всегда* ловила случаи разной длины

начало этого всего вот здесь http://www.linux.org.ru/news/opensource/4276462?cid=4295720 (правда там разговор идет в основном про зависимые типы)

имя комментирует...

> Куча строк с двоеточиями в начале, которые не несут никакого смысла.

ыыы!

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

это и называется спойлер

имя комментирует...

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

имя комментирует...

> А еще в Java можно извращаться с анотациями. И с рефлекшеном. И даже с манипуляциями над байт-кодом

мне че-то не известны примеры именно извращений

т.е. к рефлексии есть у меня претензии -- она не статическая

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

т.к. рефлексия -- это как раз прямой доступ к полям, и там извращения/акробатика *в принципе*, похоже, невозможна; вот с аннотациями я уже так уверенно не скажу, а байткод... байткод вообще вещь по-моему сложная, я бы поэтому его побоялся, но относить к акробатике все равно не стал был

имя комментирует...

я неточно выразился;

"покопаться в кишках" тут означает "быстро *самому* пофиксить обнаруженный баг или хотя бы быстро самому сделать workaround"

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

имя комментирует...

тема насчет векторов равной длины достаточно большая и интересная, и я подозреваю, что ты выкатишь свое решение (а может и не одно)

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

Евгений Охотников комментирует...

@имя:

Правильно ли я понимаю вот это:

нам нужно статически гарантировать, что два вектора имеют ОДИНАКОВУЮ длину

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

Или же длина векторов на этапе компиляции известна?

Евгений Охотников комментирует...

@имя:

>понятно, что если рефлексия была бы там обернута еще в какие-то промежуточные слои абстракции, придуманные программистом, то я бы побоялся, но мы вроде говорим о рефлексии в чистом виде

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

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

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

имя комментирует...

насчет равных длин векторов я оказывается недодал еще одну ссылку:

http://www.linux.org.ru/forum/development/4300872

> Есть два вектора, длина которых на этапе компиляции не известна.

конечно же неизвестна (и более того -- лучше всего, чтобы читалась из stdin), иначе задача просто решается на с++

имя комментирует...

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

да

например, если у нас что-то зависит от *названия* метода (которое доступно через рефлексию), то при невинном его переименовании мы можем нафиг порушить программу

но если через рефлексию спрашивать только "есть ли у метода аннотации? и какие?", то вроде как все хорошо

Евгений Охотников комментирует...

@имя:

>конечно же неизвестна (и более того -- лучше всего, чтобы читалась из stdin), иначе задача просто решается на с++

Тогда я не понимаю, как это гарантируется в упоминавшихся примерах на Haskell и на C# (фиг с ним, с Haskell-ем, я его не знаю), но в C# откуда гарантии компилятора?

имя комментирует...

вот на жабке http://www.linux.org.ru/forum/development/4300872?cid=4305706

сам компилятор гарантии не дает, а вот акробатика похоже дает

т.е. если не менять код ScalarProduct, Cons и Nil (и не делать от них производных классов), то компилятор будет бить по рукам (см. например закоменченную строку, где пытаются сделать разную длину)

если делать производные классы, то мне кажется можно поломать, но х.з.; в жабке нет нужного модификатора

имя комментирует...

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

Евгений Охотников комментирует...

@имя:

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

Возможна, но до плюсовых высот ей далеко ;)

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

return _main(n-1, i+1, new Cons<A>(0, null), new Cons<A>(i*i, second));

И получаем удачную компиляцию.

имя комментирует...

ну вот, пришел поручик ржевский и все опошлил :-)

этот код написан, кстати, по мотивам хаскеля, а в хаскеле нет null-а -- но зато там есть жопа _|_ так что надо подумать, не поломает ли она всю конструкцию там

имя комментирует...

> Возможна, но до плюсовых высот ей далеко ;)

пусть написана целая ява-библиотека примерно в таком стиле для парсинга в стиле boost::spirit

подозреваю, что ты от нее откажешься, как и от плюсовой

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

имя комментирует...

null, кстати, должен рассматриваться именно как объект класса, производного по отношению ко всем классам явы

Евгений Охотников комментирует...

@имя:

>ну вот, пришел поручик ржевский и все опошлил :-)

Ну а то! :)
Там изначально было видно, что заявленные цели не достигаются. Только у меня C#-компилятора нет. А на Java сразу стало возможно было проверить.

>этот код написан, кстати, по мотивам хаскеля, а в хаскеле нет null-а -- но зато там есть жопа _|_ так что надо подумать, не поломает ли она всю конструкцию там

Я думаю, что и с Хаскелем там не все однозначно. Срача там на пять страниц, читать все влом. Но, надеюсь, и в Хаскелевом варианте должны были найти грабли. Поскольку я не представляю себе как на уровне системы типов с заранее неизвестным n доказать, что A[n] != A[m]. Где m так же неизвестна на этапе компиляции.

Евгений Охотников комментирует...

@имя:

>пусть написана целая ява-библиотека примерно в таком стиле для парсинга в стиле boost::spirit

подозреваю, что ты от нее откажешься, как и от плюсовой


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

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

Честно скажу, что Java-вские дженерики никогда не мог вкурить. После беглого взгляда на C#-ные дженерики я понял больше, чем от тчательного выкуривания мануала по дженерикам Java. Правда давно это было. Году в 2004.

Евгений Охотников комментирует...

@имя:

Попытался поиграться с Хаскелевым вариантом. Поскольку Хаскеля я не знаю, то сломать его так просто не удается. Похоже, он работает за счет фокуса с выводом типов для main'. Там получается что в варианте с n!=0 аргументы для нового вызова main' должны быть одинакового типа. Что возможно только, если они конструируются одинаковым способом (т.е. либо Cons(_ Nil), либо Cons(_ a)). Если конструировать их по-разному, то Haskell считает, что у агументов as и bs типы получаются разными. Потому и ошибка компиляции.