LINUX.ORG.RU

Что нельзя сделать в языке java без метапрограммирования?

 ,


1

2

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

Что нельзя сделать в языке java без метапрограммирования?

метапрограммирования

метапрог

metaprog: triggered

ArkaDOSik ★★
()

измельчали цари совсем

mos ★★☆☆☆
()
Ответ на: комментарий от anonymous

Язык тюринг-полный, а значит можно всё.

тюринг-полный

А должен быть пакман-полный.

kookoo
()

Нормальные люди не пользуются голой Java, а либо используют Spring, либо идут по пути Егора Бугаенко :)

Средств Spring хватит для очень много чего, хоть и будет это зачастую уродливо

stevejobs ★★★★☆
()
Последнее исправление: stevejobs (всего исправлений: 1)

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

Чел занимается херней. Может и можно, но зачем? Во-первых, никто нигде и никогда не использует голою жаву. Во-вторых, никто нигде и никогда не заморачивается повторениями. Если у тебя треть проекта – повторяющийся код, то тогда наверное что-то пошло не так. А так никого не волнует. Сонар по умолчанию включает варнинг с, вроде бы, 5%, и ошибку с 10%. Меньше 5% вообще никто заморачиваться не будет.

morse ★★★★★
()

В Java метапрограммирование называется AOP, aspect oriented programming. Можешь погуглить. Делается действительно почти все

vertexua ★★★★★
()

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

Да, хватит. Более того, в Clojure хватает обычных функций, без макросов, для того, чтобы избавиться от крупных повторяющихся функций. Конкретно в Clojure макросы не рекомендуют использовать без необходимости, потому что, например, макрос ты не можешь передать аргументом другой функции. То, для чего нужны макросы - это создание DSL, который здесь превратил 5 строчеку в одну, там превратил 3 строчки в одну, и в итоге у тебя размер всего кода сократился пополам без усложнения чтения. Но использовать такие приемы нужно по минимуму, предпочитая обычные функции и стандартные макросы:
https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj (искать по defmacro)

Еще раз с другой стороны: в Clojure конечный язык написан на самом языке; в Java конечный язык написан на крестах, то есть, прибит гвоздями намертво и не подлежит изменению. Да, фич в языке жавы фич довольно много для того, чтобы не испытывать острой необходимости, и отвечать «ну да, здесь у жавы в два раза больше строчек, чем могло бы быть с макросами, но ведь код читаем». Но трудоемкость написания (не отладки и не релиза) кода на жаве сравнима с кодом на Си! В ключе этой трудоемкости, на фоне большой избыточности бесполезных объявлений и примитивных структур данных, проблема отсутствия простого метапрограммирования уже не так отсвечивает.

Можно сказать даже так: Kotlin, Clojure, Scala - это и есть метапрограммирование для жавы. То есть, от Java нужно избавиться, чтобы получить какой-то потенциал для метапрограммирования, чтобы изначально не бултыхаться в низкоуровневых конструкциях, а изначально иметь хорошо сочетаемые абстракции - вот в таком виде метапрограмирование действительно экономит уйму времени разработки.

byko3y ★★★★
()
Ответ на: комментарий от stevejobs

Нормальные люди и Spring? Я знал, что ты немного неадекватен, но чтоб настолько.

Средств Spring хватит для очень много чего, хоть и будет это зачастую уродливо

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

cocucka ★★★★☆
()

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

Есть упоротые люди, которые используют Lombok или Immutables для убирания бойлерплейта. Можно ли назвать аннотации метапрограммированием? Ну почему б и нет?

cocucka ★★★★☆
()
Ответ на: комментарий от byko3y

Kotlin, Clojure, Scala - это и есть метапрограммирование для жавы

А lisp - это метарограммирование для С? Хватит уже херню городить, Kotlin, Clojure, Scala - это просто другие языки и ява для них не обязательна.

Liz812
()
Ответ на: комментарий от Nervous

чтобы не делали лишь бы case of не писать (:

anonymous
()
Ответ на: комментарий от Liz812

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

Liz812
()
Ответ на: комментарий от Liz812

Не важно, главное что позволяет вводить в язык новый синтаксис.

Точно, я просто забыл, о чем тред %)

Nervous ★★★★★
()
Ответ на: комментарий от wall_jvm

Достойного для чего? Для того чтоб не писать геттеры-сеттеры и equals? Вон, бери, Lombok, только потом не жалуйся.

Что-то специфичное? Пиши свой annotation processor…

Из достойного для меня могу только Dagger2 вспомнить, который дает compile time dependency injection.

cocucka ★★★★☆
()
Ответ на: комментарий от cocucka

Что-то специфичное?

да, именно

Пиши свой annotation processor

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

wall_jvm
() автор топика
Ответ на: комментарий от Nervous

Чем это отличается от if

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

no-such-file ★★★★★
()

Sorry /пошуткую немного/.

Товарищи всех вас интересует вопрос имеется ли профит в использовании метапрограммирования?  
Прошу всех взглянуть на программу, использующую метаданные.  
Сначала метаданные кажутся маленькие и не нужные.  
Но стоит нам только зайти на ЛОР и почитать посты "невооруженным глазом", мы  
уже видим двойную пользу, тройную пользу, четверную пользу, а с хорошей закуской пятерную пользу.
Полезно ли использование метаданных или нет это науке не известно.  
Наука еще не в курсе дела.  
Хаааа! 
Вот так.  

Лучше всего изучать метаданные «после пятой» и хорошей лизгинке.
Как правильно танцевать лизгинку можно посмотреть здесь:
https://www.youtube.com/watch?v=GdFJ-HW1Quo Метаданные «после пятой»

Владимир

anonymous
()

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

if(f(aaa,bbb) + g(aaa,bbb) == c(aaa,bbb)) x(aaa,bbb)
else if(f(aaa,bbb) - g(aaa,bbb) == c(aaa,bbb)) y(aaa,bbb)
else if(f(aaa,bbb) * g(aaa,bbb) == c(aaa,bbb)) z(aaa,bbb)
else if(f(aaa,bbb) / g(aaa,bbb) == c(aaa,bbb)) k(aaa,bbb)

Через метапрограммирование можно получить что-то вроде

for_op({+,-,*,/}, aaa, bbb, f(..) %% g(..) == c(..), {x,y,z,k})
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

for_op({+,-,*,/}, aaa, bbb, f(..) %% g(..) == c(..), {x,y,z,k})

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

anonymous
()
Ответ на: комментарий от stevejobs

Я тут на этом вашем Spring не могу скандинавские знаки со страницы до базы донести. Кракозябры долетают, прям как в ранних нулевых. Скорее всего проблема в MySQL (собственно, оно и есть одна большая проблема), но таки таблэтка есть?

anonymous
()
Ответ на: комментарий от anonymous

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

anonymous
()
Ответ на: комментарий от Liz812

А lisp - это метарограммирование для С?

Лисп - это язык метапрограммирования для IPL. Си намного позже создан.

Хватит уже херню городить, Kotlin, Clojure, Scala - это просто другие языки и ява для них не обязательна

Как минимум Clojure сделана именно под жаву и имеет структуры данных специально для взаимодействия с жавой, для использования ее стандартной библиотеки вместо создания собственной. Такой код уже не переносим за пределы жавы, как нельзя скомпилировать код большинства утилит никсов при отсутствии стандартной библиотеки Си. Не стоит забывать, что объем кода стандартной библиотеки часто сравним с объемом кода транслятора. Например, компилятор Си в GCC содержит порядка полутора миллионов строк, в то время, как стандартная либа - миллион. В случае жавы хотспот весит где-то полмиллиона, в то время, как базовые классы библиотеки - не меньше полутора миллионов. По этой причине Java - это больше библиотека и трансляторы, а уже к ним прилагается официальный язык, который весьма примитивен, к слову. Да, Clojure, как язык вызова этих классов, отличается по внешней форме от мейнстрим Java - но тебе не кажется, что язык метапрограммирования ожидаемо должен отличаться от базового?

byko3y ★★★★
()
Ответ на: комментарий от anonymous

В этом коментарии больше информации, чем во всем треде :)

Это при слове Java чтоле тут начали набегать немощные умишком?

anonymous
()
Ответ на: комментарий от anonymous

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

Я не использую hibernate. jdbc template наше все.

anonymous
()
Ответ на: комментарий от anonymous

В этом коментарии больше информации, чем во всем треде :)

Это была шутка /просьба не воспринимать ее, что метаданные - «горе от ума»/.
Они действительно могут полезны если их не использовать в качестве «микроскопа для забивания гвоздей».

PS: 1С === «База метаданных» + «Объектно-ориентированная база» + «Язык программирования», позволяющий на их основе вести разработку прикладного кода.

1C является великолепной технологией, использующей метаданные.

Но не "серебрянная пуля"

Владимир

anonymous
()

А использование рефлексии считается метапрограммированием в твоём определении или нет? А кодогенерация?

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

А использование рефлексии считается метапрограммированием в твоём определении или нет? А кодогенерация?

Конечно же считается. Но я должен заметить, что эта ниша у жавы очень узкая.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Если считается, то легко примеров можно накидать. Самый простой вариант уже выше написали, например сериализация произвольного объекта в JSON. Руками писать это маразм, а с рефлексией пишется один раз и работает для всех объектов.

Пожалуй единственная задача, в которой мне не хватает средств Java, это логгирование. Хочется писать log(x + y) и получать на печати «x + y = 12», например. Это тривиально делается на макросах C но практически никак не делается в Java.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от Legioner

Конечно, это всё относится к способам метапрограммирования

"1. Компилируемые техники 1.1. Шаблоны, макросы и параметрический полиморфизмом. Для одного блока кода на этапе прекомпиляции генерируется несколько вариантов для разных типов параметров, это самый древний тип матапрограммирования.

1.2. Оптимизирующая прекомпиляция. Вычисление и оптимизация всех выражений и целых функций, которые не содержат переменных, на этапе компиляции или прекомпиляции.

1.3. Генераторы исходного кода для компилируемых языков. Перед компиляцией, на этапе сборки проекта, пакетным образом, по метаданным или без их, генерируются файлы, функции, классы, формы и т.д.

1.4. Написание своего предметно-ориентированного компилируемого языка (под определенный круг задач) — DSL (Domain-specific Programming Language).

  1. Интерпретируемые техники 2.1. Эвалуациея кода (eval) из строковых переменных в интерпретируемых языках или языках, поддерживающих позднюю компиляцию в байт-код во время исполнения. 2.2. Написание своего специализированного интерпретируемого императивного языка под задачу (LOP — Language Oriented Programming). 2.3. Создание или использование декларативного языка, формата сериализации, специального синтаксиса или подмножества таких синтаксисов как XML, JSON, XAML и др. 2.4. Динамическое формирование и исполнение кода на языках запросов, например: SQL, XQuery, LINQ и др.

  2. Гибридные техники 3.1. Интроспекция — предоставление доступа к внутренним структурам языка, типам данных, классам, функциям и т.д. Получение метаданных о структурах и обход их в цикле, или получение параметров функции как массива с возможностью анализировать класс каждого. 3.2. Динамическая интерпретация метамоделей. Специальная терминология для этого типа еще не разработана, точнее, она не устоялась, поэтому я опишу его подробнее ниже. А тут же дам приведу основную особенность: метамодель (модель предметной области на мета-языке) содержит как императивные так и декларативные компоненты влияющие друг на друга, а приложение становится «виртуальной машиной» для запуска метамодели. 3.3. Распределенная информационная система с динамическим связыванием на базе интерпретации метамоделей. Это применение пункта (3.2) к клиент-серверному или межсистемному взаимодействию."

wall_jvm
() автор топика
Ответ на: комментарий от anonymous

LOR на спринг:

ᚾᛟᛏᛖ ᛏᚺᚨᛏ ᛏᚺᛖ ᛈᚱᛖᛋᛖᚾᛏ ᚲᛟᚾᚡᛖᚱᛏᛖᚱ ᚹᛟᚱᚴᛋ ᚹᛁᛏᚺ ᛗᛟᛞᛖᚱᚾ ᛖᚾᚷᛚᛁᛋᚺ ᛟᚾᛚᚤ. ᛚᛖᛏᛏᛖᚱᛋ ᚹᛁᛏᚺ ᛟᛚᛞ ᚾᛟᚱᛋᛖ ᛟᚱ ᚨᚾᚤ ᛟᛏᚺᛖᚱ ᛞᛁᚨᚲᚱᛁᛏᛁᚲᛋ ᚹᛁᛚᛚ ᚾᛟᛏ ᛒᛖ ᚲᛟᚾᚡᛖᚱᛏᛖᛞ ᛁᚾᛏᛟ ᚱᚢᚾᛖᛋ.

EXL ★★★★★
()
Ответ на: комментарий от anonymous

лизгинке

Это в честь оккупировавшего Development своим тупняком @Liz812?

t184256 ★★★★★
()

Почему

Вместо того, чтобы кодить, и решать практические задачи, новички задают всякие извращённые вопросы о предмете?

menangen ★★★★★
()
Ответ на: комментарий от menangen

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

Legioner ★★★★★
()
Ответ на: Почему от menangen

Вместо того, чтобы кодить, и решать практические задачи, новички задают всякие извращённые вопросы о предмете?

Зачем интересоваться миром, если можно всю жизнь махать лопатой. Согласен.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Лисп - это язык метапрограммирования для IPL. Си намного позже создан.

Докажи. Докажи то, что list до C существовал с теми свойствами, которые ты приписываешь к нему.

Например, компилятор Си в GCC содержит порядка полутора миллионов строк

В гцц нету никакого отдельного «компилятора си».

в то время, как стандартная либа - миллион.

Как и в stdlib нету никаких «миллион».

xiomar_georgios
()
Ответ на: комментарий от wall_jvm

За долгие годы наработал я следующие варианты метапрограммирования.

  1. Самый мерзкий: кодогенерация в рантайме (в т.ч. спринг). Синтаксис идиотский, хрен увидишь что сгенерил, хрен отладишь. Разве что с точки зрения понимания работы стековой машины JVM забавно, если юзать низкоуровневые инструменты (я юзал https://asm.ow2.io/ и какую-то обёртку поверх него, автоматом расчитывающую размер фрейма под локальные переменные – вроде бы она называлась bytebucket, но чёт не гуглится).

// UPD1: Опять же, какого чёрта делать в рантайме то, что надо делать в билдтайме; из-за всей этой дичи JEE- и спринг-приложения и стартуют так долго.

Далее все варианты – про работу твоего кода в buildtime, что и является метапрограммированием здорового человека. В случае жавы, под каждую задачу рисуется отдельный maven-плагин – модуль приложения; этот модуль-плагин может собираться и тут же собранный запускаться при сборке другого модуля – т.е. всё в рамках одного запуска команды mvn.

  1. Самый тупой: кодогенерация. Парсишь что-то из src (и/или ещё откуда-нибудь), генеришь что-то в target/generated-sources. Под это дело даже отдельные фазы у maven есть. Этим кто только не занимается: например JPA и QueryDSL генерят свои мета-классы, сканируя и парся entity-классы. Я люблю классы сущностей по структуре базы генерить – здесь я, во-первых, не сорцы парсю, а лезу в information_schema (локальной временной базы, на которой предварительно прогоняю миграции другим maven-плагином); а во-вторых, я люблю кодогенерировать в src, а не в target/generated-sources: так удобнее и изменения отслеживать (в какой момент генератор поломали), и не только. Для парсинга категорически рекомендую JavaParser.org: редко я могу что-нибудь искренне похвалить, а манипуляция AST вообще занятие крайне занудное – но эта либа отличная (с учётом того, что в жаве нету pattern matching, с помощью которого в той же scala работать с AST на порядки проще).

// UPD2: «JPA … генерят свои мета-классы» – в смысле, Criteria API.

  1. Развитием идеи кодогенерации целого исходника с нуля является замена кусков исходника: ищем метки ///something-begin/// и ///something-end/// и генерируем код между ними. Тут опять же модифицируемый файл лежит в src. Класс, ищущий такие метки, разбивающий по ним исходник, предоставляющий API типа set(«something», generatedSource) и собирающий исходник из кусков обратно – тривиален.

  2. Ещё круче (хотя и гораздо гиморнее) парсить не метки (что имеет довольно ограниченное применение), а натравить JavaParser.org на исходник, подкрутить распарсенное AST и сериализовать его тем же JavaParser-ом назад в исходник. Слегка поломаются пустые строки и отступы, но терпимо (раньше по крайней мере ломались; например, каменты прижимаются к коду под ними). Я таким образом перегенерировал классы сущностей, сохраняя добавленные вручную программистом методы, не являющиеся вырожденными геттерами-сеттерами (которые добавлялись/удалялись плагином вместе с приватными полями класса при изменении структуры таблицы).

Всеми этими безобразиями с генерацией сорцов / изменениями их по месту можно сделать очень много, но не всё: нельзя нормально разворачивать DSL. Например, если в сорце есть статически-типизированный SQL-запрос, его нельзя удалить и заменить на сгенерированный по нему raw SQL, а генерить этот raw SQL в отдельный файл – настолько уродство, что даже не хочется думать, как до этого сгенерированного в отдельном файле запроса добраться из точки, где вставлен исходный DSL.

Для таких случаев:

  1. Ну и наконец, есть lombok. Это самое «честное» метапрограммирование на AST-макросах: сорцы не меняются, манипуляция AST выполняется в процессе работы javac, результаты манипуляций идут в class-файл. Посмотреть что нагенерилось можно, открыв этот class-файл в IDE (с автоматическим дизассемблированием). Ставить точки останова на сгенерированном коде у меня не выходило (не помню, пытался ли на class-файле), делал stepOver / stepInto вслепую + view variables на каждом шаге.

Самая большая проблема у lombok: чтобы добавить в него свой код, надо его форкать. Форков у него на одном только гитхабе – полторы тыщи, и всем пофиг. И авторы lombok как-то не чешутся делать поддержку расширяемости, и кстати авторы javac тоже не чешутся сделать классы AST public – сейчас lombok инстанциирует их через рефлексию (ну там с кешированием конструкторов, так что всё довольно шустро). Короче, на метапрограммирование в жаве всем насрать. С тоской вспоминаю скалу, где метапрограммирование встроенное, хотя AST в ней в разы более сложное, чем у жавы.

Собирается этот lombok отдельно от проекта, с помощью ant. Там у него свои заморочки.

Ну и кто с AST не работал, тем предстоит познать геморрой. Там ничего не делается в лоб. Простой пример – локальная переменная с инициализатором:

// boolean myVar = true;
mk.VarDef(
    mk.Modifiers(0),
    node.toName("myVar"),
    mk.TypeIdent(CTC_BOOLEAN),
    mk.Literal(CTC_BOOLEAN, 1)
)

В скале, помнится, константа задавалась то ли как Constant(Literal(true)), то ли как Literal(Constant(true)). Но там хотя бы квазицитирование есть, в отличие от. На каждом новом языке, на каждом новом компиляторе – свои заморочки. Хочешь сгенерировать try-catch – пишешь его в коде, навешиваешь на этот код lombok-аннотацию, при старте maven ставишь точку останова внутри lombok и когда до неё доходит дело, смотришь какое AST сгенерил компилятор; потом такое же делаешь в своём плагине, раза со 2-го … 5-го получится. Помнится, я пытался mk.Literal(CTC_BOOLEAN, true) в последней строчке – и оно не работало.

Что там с типизацией, не помню. У JavaParser вроде есть какие-то API для семантического анализа, но я с ними не разбирался. В lombok приезжает вроде уже типизированное дерево, но это не точно.

Можно подумать, что все варианты кроме lombok – для бедных. Но на lombok ты можешь работать только в пределах сорца (ну может, ещё анализировать импортированные типы), а с тупой кодогенерацией я и List<Migration> по списку файлов migration/*.java генерю, и, повторюсь, классы по базе. В общем, всё это можно совмещать.

dimgel ★★★★★
()
Последнее исправление: dimgel (всего исправлений: 5)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.