LINUX.ORG.RU

clojure после common lisp - насколько омерзительно и в чём именно?

 , ,


3

2

Всё ещё думаю о возможности заняться разработкой на clojure. Но ява же тормозная - меня бесит скорость сборки. Вот боюсь, как бы кложа не оказалась слишком уж тормозной.

Расскажите о своих ощущениях (из серии «собираюсь жениться - отговорите»).

★★★★★
Ответ на: комментарий от alienclaster

Фреймворки по определению неудобны.

Это по какому такому определению?

По определению фреймворка. Где вся архитектура продумана за тебя, и чаще всего не соответствует твоим задачам. Людям нужны инструменты, а не политика. Политику уж как-то сами замутят.

не изобретать кривые велосипеды

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

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

Да-да, представьте себе, в языках того времени не было циклов и не было функций — вместо этого применялись goto и глобальные переменные.

Эээ… Это в каких? До Алгола был Фортран, там были цикл DO (аналог for) и DO WHILE, подпрограммы (SUBROUTINE) и функции (FUNCTION). Вот IF был не блочный (добавили только в FORTRAN 77), поэтому GOTO таки был нужен.

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

Так, я, оказывается, даже не совсем туда полез. Актуальная документация вот тут:

https://github.com/embox/embox-docs/tree/master/

Там есть разделы en и ru, и простое их сравнение показывает, что обновлялись они примерно в одно время (половина файлов 2 года назад, часть 9 месяцев назад, и quick start — как русский, так и английский - 2 месяца назад). Ноздря в ноздрю.

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

Скажи, а у тебя по каким соображениям Маркс, Энгельс и Советский Союз написаны с маленькой буквы, а Lexus и Маск — с большой?

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

До Алгола был Фортран, там были цикл DO (аналог for) и DO WHILE

В Fortran IV DO было сахаром над GOTO:

http://www.bitsavers.org/www.computer.museum.uq.edu.au/pdf/DEC-10-AFDO-D decs...

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

подпрограммы (SUBROUTINE) и функции (FUNCTION)

Да, были, и это тоже был сахар над GOTO.

Вот IF был не блочный (добавили только в FORTRAN 77), поэтому GOTO таки был нужен

Блоков там формально не существовало, END, RETURN, STOP можно было записывать где попало, хоть грамотные программисты и стремились записывать код именно в блочной форме. Первыми блоки и локальные переменные появились именно в Algol 60.

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

Скажи, а у тебя по каким соображениям Маркс, Энгельс и Советский Союз написаны с маленькой буквы, а Lexus и Маск — с большой?

По соображениям лени.

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

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

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

Вот Линусу Торвальдсу, как он сам говорил, пофиг на людей, ему главное технологии.

Ты про это?

Фигасе у них там... свобода слова. Меня радует, как этот концлагерь с усиленной кормежкой кто-то там называет свободой, а халявное жилье и путевки на море — тоталитаризмом.

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

Там ещё интересный абзац был:

Этика сообщества, которым он руководит, до недавнего времени была описана в документе с говорящим названием Code of Conflict. Его краткое содержание: делай свою работу хорошо; это главное; а если тебя сильно лично обидели, пиши, разберемся. Кто-то увидит в этих принципах описание эффективной команды, нацеленной на достижение результата. Кто-то — токсичную атмосферу.

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

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

Ну давай, расскажи мне, что у лисперов нет склонностей к написанию write-only кода.

Я Common Lisp изучал практически по исходникам. Причём не испытывал никаких трудностей при этом и желания поискать документацию.

Аналогичная попытка почитать C++ Boost закончилась через полчаса.

Проще чем на Common Lisp, наверное, принято писать разве что на Haskell.

У лисперов есть не нравящаяся некоторым привычка делать DSL чуть ли не для каждой задачи. То есть перед чтением исходников приходится сначала прочитать макросы DSL, осознать семантику и только после этого читать сами исходники.

Если к лисперам относить Racket, то там исходник читать чуть сложнее, потому что документацию в нём принято делать отдельно (в scribble) и, как следствие, гораздо меньше комментариев в самом коде.

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

Где вся архитектура продумана за тебя, и чаще всего не соответствует твоим задачам.

Если архитектура не соответствует твоим задачам, значит просто выбран неправильный фреймворк. Думаю, уже под любую мыслимую архитектуру написано по паре фреймворков.

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

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

Проще чем на Common Lisp, наверное, принято писать разве что на Haskell

М-м-м, у лисперов какие-то странные критерии простоты. Там нынче в хаскеле в моду входит тот же MTL, где даже простое последовательное выполнение двух инструкций превращено в трехэтажные конструкции из монад с reader/writer-ами — и всё это тупо ради того, чтобы получить одинаковый интерфейс для чистых и грязных функций.

Аналогичная попытка почитать C++ Boost закончилась через полчаса

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

У лисперов есть не нравящаяся некоторым привычка делать DSL чуть ли не для каждой задачи. То есть перед чтением исходников приходится сначала прочитать макросы DSL, осознать семантику и только после этого читать сами исходники

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

Знаешь, у меня недавно горел пукан, когда я работал с JS либой текстового процессора. Разрабы ставили перед собой ясную цель: сделал архитектуру модель-вид, где пользовательский ввод влияет на модель, а модель уже односторонне преобразовывается в вид; при этом текстовый процессор сохраняет несколько версий модели в виде персистентных структур для простого отката версий. То есть, что-то очень похожее на ClojureScript.

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

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

И самая вишенка на торте: как со всей этой системой работать, если у тебя уже есть фреймворк для построения SPA, вроде React/Vue/Svelte? Если бы текстовой процессор опирался на модель, близкую к реакту, то есть, тупо функции render везде, то хуже бы натягивался на Vue, или Svelte, или Angular. Но когда текстовой процессор представляет собой что-то отдельно стоящее, то он не натягивается ни на один SPA фреймворк.

Так вот, по поводу лиспа. Не было бы никакой проблемы, если бы DSL-и были ограниченными и согласованными. Но каждый уважающий себя биполярщик-лиспер нахерячит DSL «как его увидел», не будет его поддерживать, мол «я потратил время на написание — теперь ты потрать время на чтение», а потом придет следующий лиспер и переделает DSL заново.

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

Лучше спроси https://t.me/clojure_ru

Там же сидит автор https://github.com/serioga/webapp-clojure-2020 шаблона с которого посоветую начать

Для веба, особенно если абстрагироваться от фронта, бек-мейнстрим, имо, последние пару лет находится тут https://github.com/metosin

Не ищи «clojure web framework», тут все opt-in.

Начти с fn прямо для ring (аналог wsgi/rack), хочешь - вставь роутер и middleware stack, хочешь - в роутер вставь схему и авто-сваггером, тайпкастом и валидацией по схеме/контракту.

Можно начать тупо сделав глобальную переменную с db-коннектом и дергать jdbc с sql-строчками, хочешь - возьми hash->sql билдер (honeysql), хочешь - заведи все в DI систему.

С этой т.з. webapp-clojure-2020 как раз похож на нафаршированный пример где, возможно, много лишнего. Но спринга или рельс тут, может и к лучшему, нет

Инкрементальность всех шагов included, кроме разве добавления зависимости - можно и на лету, но имо проще подождать рестарт репла

p.s. Babashka вместо новых шелл-скриптов работает норм

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

Там нынче в хаскеле в моду входит тот же MTL

Тем не менее, его достаточно один раз понять и код с его использованием читается не сложнее, чем C++ iostream с арифметическим сдвигом в качество ввода/вывода. Исходники у MTL вообще лаконичные.

И это все-таки далеко не стандартная крестовая либа — лучше бы сравнивал с STL.

iostream тоже написан так, что без внешней документации тяжело понять, что именно надо использовать. Я про basic_*stream и basic_ios.

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

Если речь идёт про DSL библиотек, то это проблема любых фреймворков. Использовать в одном C++ проекте Qt и VTK тоже удовольствия мало.

Если же в рамках одного проекта, то DSL, как правило, согласованы, так как стараются придерживать единого стиля. Вот, например, https://github.com/hu-dwim : несколько десятков библиотек с десятком единообразных DSL.

Не было бы никакой проблемы, если бы DSL-и были ограниченными и согласованными.

Они вполне ограниченные. Нет никакой проблемы в одном проекте смешивать cl-iterate, cl-defstar и cl-sql, например.

я потратил время на написание — теперь ты потрать время на чтение

В Common Lisp лёгкость чтения кода приводит к тому, что DSL мало документируются. Из кода макроса и пары примеров ведь всё очевидно…

а потом придет следующий лиспер и переделает DSL заново.

NIH синдром от языка не зависит.

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

Тем не менее, его достаточно один раз понять и код с его использованием читается не сложнее, чем C++ iostream с арифметическим сдвигом в качество ввода/вывода. Исходники у MTL вообще лаконичные

Еще раз: эта библиотека нужна для последовательного выполнения команд. Не для машинного обучения, не для асинхронного ввода-вывода, не для вычислений на GPU. И это даже не интерпретатор DSL. Это всего-лишь библиотека для записи простых конструкций языка в другой форме. Ее в принципе не должно существовать, она не выполняет никакой полезной функции. И по этой причине она крайне сложна для библиотеки, которая ничего не делает.

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

У меня пердак погорел еще когда я пытался понять, что же такое монада. Такое ощущение, будто это было какой-то международное соревнование «напиши самую непонятную статью про последовательное выполнение команд». Да, я понимаю, что монады умеют не только просто последовательно выполнять команды, но, например, засунуть в монаду списка семантику concat-fmap — это довольно сомнительный в плане удобства прием. Особенно если учесть, что в итоге в язык добавили list comprehension, который является третьим способом сделать то же самое, причем, теперь уже прибитым гвоздями к компилятору.

iostream тоже написан так, что без внешней документации тяжело понять, что именно надо использовать. Я про basic_*stream и basic_ios

Сишное stdio тоже очень тяжелое, из-за буферизации. Ты уверен, что ты сможешь разобраться в хаскелевом вводе-выводе, который написан на C/C--? Он, между прочим, прибит гвоздями намертво к RTS и написан ни разу не на хаскеле. Как я довольно давно писал тут: хаскель представляет собой высокоуровневый язык для описания правил дерганья сишный функций. И ключевым сишным фундаментом являются сами потоки выполнения, исключения, сборка мусора.

Если же в рамках одного проекта, то DSL, как правило, согласованы, так как стараются придерживать единого стиля. Вот, например, https://github.com/hu-dwim : несколько десятков библиотек с десятком единообразных DSL

Это очень замечательно. В Clojure это звучит просто: не пишите макросы, пока вы можете не писать макросы. И это работает. Потому что DSL — зло, которое лишь изредка может быть оправдано большей пользой.

В Common Lisp лёгкость чтения кода приводит к тому, что DSL мало документируются. Из кода макроса и пары примеров ведь всё очевидно

Если ты отлаживаешь большую систему, где взаимодействует большое число модулей, написанных разными людьми, то ты проклянешь все эти DSL-и, потому что за одну отладку тебе придется «легко прочитать» десятки онных. Потому что один легко, второй легко, пятый легко — а десять-двадцать уже чот не легко, и работа не сделана, а я всё сижу да «легко читаю».

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

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

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

Ну почему же? Удобные комбинаторы монад. Так-то и монаду не нужна, можно IO гвоздями прибить как в Clean, а все остальные вручную функциями делать.

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

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

Да, я понимаю, что монады умеют не только просто последовательно выполнять команды, но, например, засунуть в монаду списка семантику concat-fmap — это довольно сомнительный в плане удобства прием.

Это нормальный подход математиков. Например, для работы с функциями придумывают бесконечномерное пространство, где функции являются векторами и можно посчитать скалярное произведение. Так и монады: это просто удобная математическая абстракция произвольного контейнера (последовательное выполнение команд заключается в записи последовательности команд в контейнер IO).

Ты уверен, что ты сможешь разобраться в хаскелевом вводе-выводе, который написан на C/C–?

Скорее всего, нет. Я про то, как принято писать код на самом хаскеле.

Как я довольно давно писал тут: хаскель представляет собой высокоуровневый язык для описания правил дерганья сишный функций.

Это можно сказать про любой высокоуровневый язык.

Потому что DSL — зло, которое лишь изредка может быть оправдано большей пользой.

Религиозное утверждение. Хотя в Racket также.

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

macroexpand спасает. Хотя в менее мощном отладчике всё грустно.

но наклепать кучу DSL-ей — это как раз один из этих пособов сделать программу нечитаемой.

Большая часть DSL’ей наоборот повышают читабельность.

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

Если архитектура не соответствует твоим задачам, значит просто выбран неправильный фреймворк. Думаю, уже под любую мыслимую архитектуру написано по паре фреймворков.

А как он может быть правильно выбран, если выбирают по популярности в конкретный момент времени. И то же касается языков программирования. Вот выбрали питон. Почему? Да он очень популярный, много батареек, много кодеров на рынке, блаблабла. А пишут какое-нибудь GUI. И так же фреймворки. Если уже выбрали ЯП, то варианты резко сокращаются чаще всего до 1 штуки.

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

Вот я твою перепалку с @abondarev читаю

Никакой перепалки с моей стороны не было. Я попросил не делать хамских и бездоказательных высказываний, о том, что кто то по мнению автора, пользуется какими то ресурсами ВУЗа.

Ну да, не на русском и не русифицированном ЯП, а на принятом в мире Си. Ты мог бы с ними поговорить, например, «а как насчёт вести вики на русском языке». Но нет, ты им начинаешь угрожать юридическими проверками.

Как я уже когда то сказал автору язык не имеет значение (это мое мнение никому не навязываю) важна функциональность. То есть та польза которую приносит проект его пользователям. Документации и материалов на русском у нас больше чем на английском. У нас все еще большинство разработчиков русскоговорящие. Есть вполня вменяемые средства коммуникации в том числе и на русском . Текст дипломов и курсовых обычно использует русский. Для русских ВУЗов конечно. Но например как минимум названия должны быть переведены и аннотации тоже. Это требование федеральных ВУЗов, а не наше.

В общем если человеку хочется заняться подачей в суд или еще чем то там. То я ему не мешаю. Пусть и он не мешает.

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

А как он может быть правильно выбран, если выбирают по популярности в конкретный момент времени.

Странные люди. А средство транспорта они тоже по популярности выбирают? «Нынче популярны электросамокаты, в отпуск на море поеду на нём…».

Вот выбрали питон. Почему? Да он очень популярный, много батареек, много кодеров на рынке, блаблабла. А пишут какое-нибудь GUI.

PyQt не худший вариант для какого-нибудь GUI. Но в целом выбор языка и фреймворка идёт после выбора архитектуры. Иначе архитектуру надо выбирать диктуемую фреймворком, а не произвольную.

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

Ты уверен, что ты сможешь разобраться в хаскелевом вводе-выводе, который написан на C/C–?

Посмотрел putStr / hPutStr / writeBlocks / writeCharBuffer / flushWriteBuffer / IODevice.write / c_write. Всё на нормальном Haskell’е кроме c_write, который тривиален.

Исходники тоже очень читабельные, включая буферизацию.

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

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

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

Тогда какие претензии к фреймворкам? С таким же успехом можно возмущаться, что внедорожник в коридоре поставить нельзя.

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

Спасибо, добавлю твой комментарий в закладки :)

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

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

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

Но например как минимум названия должны быть переведены и аннотации тоже. Это требование федеральных ВУЗов, а не наше.

Ну вот, оказывается, всё ещё хуже. Латынь нам сверху спускают, из самого Ватикана.

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

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

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

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

Я не знаю, что такое современная наука. Во времена Галилео Галилея наука была частью политики и ВПК. Почитай историю про открытие спутников Юпитера или про Гюйгенса и хронометры. Если люди с тех пор стали другими, то и наука стала другой.

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

den73 ★★★★★
() автор топика

А собственно, по теме. В общелиспе я могу переопределить defclass и defstruct. Правда, классы тормозные, а переопределять структуру иногда чревато, но в целом это работоспособно. А как в clojure с этим?

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

defrecord работает хорошо - можно добавить поле, при этом, код, вызывающий по-позиционный конструктор, не падает.

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

Короче, по мощности динамической замены выглядит как годнота. Хотя конечно, двухсекундный затуп при запуске любого инструмента - это треш.

А нет ли способа как-то сделать в openjdk AOT компиляцию и закешировать? Или это так там не работает?

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

Посмотрел putStr / hPutStr / writeBlocks / writeCharBuffer / flushWriteBuffer / IODevice.write / c_write. Всё на нормальном Haskell’е кроме c_write, который тривиален

Поздравляю, ты запутался в трех соснах. Справедливости ради, я сам бы сразу не догадался, если бы не знал заранее работу IO в GHC. Дело в том, что упомянутое тобой c_write (которое тупо обертка над write) вызывается либо из prodServiceThread менеджера ввода-вывода, либо hPutBufNonBlocking. Последнее, в свою очередь, уже ниоткуда не вызывается из стандартной либы, потому что делает неблокирующую операцию, которая непонятно когда закончится и непонятно что делать с ее буфером — оно оставлено для собственных реализаций асинхронного ввода-вывода. Штатные же функции используют hPutBuf, в котором стэк вызова примерно такой:

hPutBuf =>
    hPutBuf' h ptr count True => -- canblock = true
        bufWrite =>
            writeChunk =>
                writeRawBuffer =>
                    write_rawBuffer -- Сначала пишется в буфер до заполнения
                    threadWaitWrite =>
                        waitForWriteEvent

Дальше уже работает менеджер ввода-вывода из GHC.Conc. Справедливости ради, таки в RTS оно зашито только под форточками — это rts/win32/AsyncIO.c и rts/win32/IOManager.c. Хз почему я запомнил, что остальное тоже на сишке.

Ну вот сколько я потратил сейчас на разбор цепочки? Где-то час. Сколько у меня ушло на разбор ввода-вывода libc? Где-то столько же.

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

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

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

Я диплом писал в 1993, это, конечно, уже был не СССР, но сомневаюсь, что новые власти это протащили в неповоротливую вузовскую систему за два года. Помимо прочего, у новорусских властей начала 90-х других забот хватало, они драчкой между собой занимались, ну и при(х)ватизацией заодно.

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

Ну вот сколько я потратил сейчас на разбор цепочки? Где-то час. Сколько у меня ушло на разбор ввода-вывода libc? Где-то столько же.

Не знаю, здесь каждый элемент цепочки в дюжину строк. В glibc каждая функция – на несколько экранов. И как можно сравнивать легко читающийся Haskell с стопкой макросов типа

#define _IO_sputn(__fp, __s, __n) _IO_XSPUTN (__fp, __s, __n)
    //...
    #define _IO_XSPUTN(FP, DATA, N) JUMP2 (__xsputn, FP, DATA, N)
    //...
    #define JUMP2(FUNC, THIS, X1, X2) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1, X2)
    //...
    # define _IO_JUMPS_FUNC(THIS) \
      (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS ((struct _IO_FILE_plus *) (THIS)) + (THIS)->_vtable_offset))
    //...
    #define _IO_JUMPS(THIS) (THIS)->vtable

которая разворачивается в

((*(struct _IO_jump_t **) ((void *) &((struct _IO_FILE_plus *) (((_IO_FILE*)(&_IO_2_1_stdout_)) ) )->vtable+(((_IO_FILE*)(&_IO_2_1_stdout_)) )->_vtable_offset))->__xsputn ) (((_IO_FILE*)(&_IO_2_1_stdout_)), str, len)
monk ★★★★★
()
Ответ на: комментарий от bread

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

Только если задача не ложится целиком на фреймворк. Надо тебе сделать складской учёт – будешь склеивать полдюжины небольших библиотек для работы с БД, пользовательским интерфейсом, генерацией отчётов, журналированием, … или возьмёшь готовый 1С или Axapta и за час получишь результат?

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

Можешь предложить написание сайтов дешевле без всяких рельсов и прочих фреймворков?

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

Штатные же функции используют hPutBuf, в котором стэк вызова примерно такой:

???

putStr s        =  hPutStr stdout s

hPutStr handle str = hPutStr' handle str False

hPutStr' =
...
    case buffer_mode of
       (NoBuffering, _) -> do
            hPutChars handle str        -- v. slow, but we don't care
            when add_nl $ hPutChar handle '\n'
       (LineBuffering, buf) -> do
            writeBlocks handle True  add_nl nl buf str
       (BlockBuffering _, buf) -> do
            writeBlocks handle False add_nl nl buf str
...

и далее вплоть до c_write.

Где там hPutBuf ?

...
   writeChunk =>
                writeRawBuffer
...

А это где? Вот весь исходник:

writeChunk h_@Handle__{..} ptr bytes
  | Just fd <- cast haDevice  =  RawIO.write (fd::FD) ptr bytes
  | otherwise = error "Todo: hPutBuf"
monk ★★★★★
()
Ответ на: комментарий от monk

Где там hPutBuf ?

Хм-м-м, мне кажется, что мы смотрим на разные версии GHC. Конкретно я почему-то смотрю сорцы где-то 2005 года (хз какая это версия пакета base). В моих сорцах механизм функционирования ввода-вывода еще был ясен; в тех, что ты даешь, уже ничерта не понятно, потому что абстракция на абстракции абстракцией погоняет. На твоей версии hPutStr' в зависимости от буферизации вызывает hPutChars или writeBlocks, которые оба вызывают writeCharBuf, и в итоге writeCharBufPtr — последний уже является лишь протоколом буфера, а не конкретной реализацией.

hPutBuf там же:

https://hackage.haskell.org/package/base-4.14.1.0/docs/src/GHC.IO.Handle.Text...

Он делает bufWrite => writeChunk, и дальше, как ты правильно процитировал, «RawIO.write (fd::FD) ptr bytes» — которое является не конкретным действием, а опять-таки абстрактным протоколом, реализация которого (единственная!) находится в GHC.IO.FD:

https://hackage.haskell.org/package/base-4.14.1.0/docs/src/GHC.IO.FD.html#fdW...

и дальше: https://hackage.haskell.org/package/base-4.14.1.0/docs/src/GHC.IO.FD.html#wri...

writeRawBufferPtr loc !fd !buf !off !len
  | isNonBlocking fd = unsafe_write -- unsafe is ok, it can't block
  | otherwise   = do r <- unsafe_fdReady (fdFD fd) 1 0 0
                     if r /= 0
                        then write
                        else do threadWaitWrite (fromIntegral (fdFD fd));
  where
    do_write call = fromIntegral `fmap`
                      throwErrnoIfMinus1RetryMayBlock loc call
                        (threadWaitWrite (fromIntegral (fdFD fd)))
    write         = if threaded then safe_write else unsafe_write
    unsafe_write  = do_write (c_write (fdFD fd) (buf `plusPtr` off) len)
    safe_write    = do_write (c_safe_write (fdFD fd) (buf `plusPtr` off) len)

Да, здесь есть вызов c_write, однако, ни в одном сценарии он не вызывается без threadWaitWrite, который является частью менеджера ввода-вывода.

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

Нужно отдать должное, что отдельные хаскелисты замечают эту тенденцию и прямо признаются «я не могу написать ни одной программы, потому что я начинаю бесконечно придумывать более лучшую модель для записи моих алгоритмов». Но если он сделает еще один шаг в этом направлении, то поймет, что эти модели были изначально не нужны, это была ментальная мастурбация, всё то же самое в других языках программирования записывается без необходимости проектировать и перепроектировать модели ввода-вывода по десять лет; что без абстракций код писать и изменять проще и быстрее, чем с абстракциями. А это значит больше работающего кода — с чем у хаскеля очень большие проблемы.

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

И как можно сравнивать легко читающийся Haskell с стопкой макросов типа
которая разворачивается в
((*(struct _IO_jump_t **) ((void *) &((struct _IO_FILE_plus *) (((_IO_FILE*)(&_IO_2_1_stdout_)) ) )->vtable+(((_IO_FILE*)(&_IO_2_1_stdout_)) )->_vtable_offset))->__xsputn ) (((_IO_FILE*)(&_IO_2_1_stdout_)), str, len)

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

https://code.woboq.org/userspace/glibc/libio/genops.c.html#370
https://code.woboq.org/userspace/glibc/libio/wgenops.c.html#172
https://code.woboq.org/userspace/glibc/libio/obprintf.c.html#_IO_obstack_xsputn

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

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

У меня такая фигня и до хаскеля была. Как правило, это свидетельствует о том, что программа не очень-то и нужна и пишется для неопределённой целевой группы. И, как следствие, является предметом искусства со всеми вытекающими последствиями.

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

ни в одном сценарии он не вызывается без threadWaitWrite

| isNonBlocking fd = unsafe_write
...
unsafe_write  = do_write (c_write (fdFD fd) (buf `plusPtr` off) len)
monk ★★★★★
()
Ответ на: комментарий от monk

unsafe_write = do_write (c_write (fdFD fd) (buf `plusPtr` off) len)

do_write оборачивает c_write в threadWaitWrite, он двумя строчками выше определен.

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

У меня такая фигня и до хаскеля была. Как правило, это свидетельствует о том, что программа не очень-то и нужна и пишется для неопределённой целевой группы. И, как следствие, является предметом искусства со всеми вытекающими последствиями

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

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

Это проблема не в хаскеле, а в хаскелистах. Язык отбирает людей с математическим складом ума. А математики — это такие люди…

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

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

Математик выскакивает в коридор, видит на стене огнетушитель, и, 
обрадовано воскликнув: "Решение существует!", спокойно возвращается в номер...
monk ★★★★★
()
Ответ на: комментарий от monk

Странные люди. А средство транспорта они тоже по популярности выбирают?

Ну справедливости ради, лёгкость поиска сотрудников - вполне себе один из важных критериев выбора.

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

Ну почему же? Удобные комбинаторы монад. Так-то и монаду не нужна, можно IO гвоздями прибить как в Clean, а все остальные вручную функциями делать

Да, именно так. В 95% случаев никакого сверхъестественного механизма комбинирования функций не нужно, а оставшиеся 5% спокойно описываются явно. В тех же диалектах ML и в Elm есть подобие монад, но они там используются только когда нужны, а не для каждой строчки кода.

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

Так и монады: это просто удобная математическая абстракция произвольного контейнера (последовательное выполнение команд заключается в записи последовательности команд в контейнер IO)

Это просто неудобная и чрезмерно усложненная на ровном месте запись. Особенно провальным мне видится подход определения правил конвеера (который запутывающе называют «контейнер») через тип возвращаемого значения. А потому что IO STM STRef a, и давай попробуй пробросить возможность вызывать putStr в какой-то вложенной функции без сишных хаков. В результате эти самые монады не просто позволяют, но прямо-таки заставляют тщательно продумывать и просчитывать архитектуру функций — что, зачастую, непозволительная и бессмысленная роскошь.

Религиозное утверждение. Хотя в Racket также

Чисто прагматичная ретроспективная аналитика, опирающаяся на простой факт: все языки с буйным метапрограммированием провалились.

macroexpand спасает. Хотя в менее мощном отладчике всё грустно
Большая часть DSL’ей наоборот повышают читабельность.

Вот эти два тезиса на самом деле вступают в неявное противоречие. Ты утверждаешь, что для чтения макросов приходится использовать инструменты отладчика. То есть, переводчика. Когда я пишу тебе на русском языке, то читаемость моего текста выше, чем если бы я писал на китайском — потому что тебе не нужен был бы переводчик. Потому отсутствие необходимости в переводчике является важным критерием читаемости. Так-то отдельные уникумы доходяят до того, что «читай документацию и спрашивай у меня — всё просто», но в самом коде в реальности черт ногу сломит, потому нужны посредники для его чтения, потому читаемости низкая.

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

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

???

Если не использовать монады и ленивое вычисление, проще не использовать Haskell.

А потому что IO STM STRef a, и давай попробуй пробросить возможность вызывать putStr в какой-то вложенной функции без сишных хаков.

А зачем? В SQL внутри SELECT тоже нельзя сделать вывод строки при чтении строки из таблицы. Ты им тоже стараешься не пользоваться?

На чистые функции гораздо легче пишутся тесты.

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

Пиши всё в IO и не парься. Тогда ничего просчитывать не надо.

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

Common Lisp жив, Tcl тоже. C++, Ruby и Rust вообще очень сильно распространены.

В Racket не любят макросы ровно по одной причине: макрос нельзя сохранить как значение. В Racket я могу написать (map call-with-input-file files processors), а в Common Lisp (или Clojure) придётся писать что-то вроде (mapc (lambda (f p) (with-input-file f (funcall p))) files processors).

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

Не приходится, а возможно. Также как в Си иногда проще пропустить через cpp, чем искать во что раскрывается define. Но часто ли так делаешь?

К слову, для незнакомой функции быстро получить её раскрытый исходник (с инлайном всех не библиотечных функций) гораздо сложнее. Или для шаблона C++ получить во что он раскрывается.

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

Что проще читается:

(loop for i from 5 to 15 collecting i)

или

(let ((acc nil))
  (dotimes (i1 11 (nreverse acc))
    (push (+ i1 5) acc)))

?

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

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

От логичности и документированности конкретного DSL зависит. Зачем лезть с отладчиком туда, где и так все работает, как задумано?

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

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

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

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

Понятия можно и функциями описать. DSL позволяет описать новый синтаксис под задачу. К слову, не в лиспах они тоже есть: SQL, регулярные выражения, XPath, … Разница только в том, что лисп может DSL сразу компилировать в код при компиляции программы, а в других языках приходится DSL делать в строке, а компилировать во время работы программы.

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

Чем больше лисперов, тем больше свободных библиотек.

monk ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.