LINUX.ORG.RU

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

 , ,


0

3

Навеяно одной из соседних тем. Стало интересно, что вообще люди думают по этому поводу.

Какой бы вы хотели видеть систему модулей?

Тут можно выделить ряд вопросов.

Должны ли модули быть файлами(один файл - один модуль) или они должны декларироваться в коде? Или это можно как-то скомбинировать?

Должен ли модуль быть единицей инкапсуляции или же это лучше оставить системе типов(классы в случае ООП)?

Должен ли модуль формировать пространство имен? Как должен работать import?

Нужно ли явно отделять интерфейс модуля от реализации в коде(или даже их стоит по разным файлам разносить)?

Как система модулей сочетается с системой пакетов/зависимостей? Как сочетается с системой сборки?

И т.д. и т.п.

Делитесь мыслями и идеями=)


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

В Go идеальная система модулей

Что в ней идеального?

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

Да где ж там сложность-то

В использовании, чтении и поддержке функторов.

Может это дженерики - ненадёждый и уродливый костыль для тех «современных» языков, создатели которых не осилили понять функторы.

Нет. Функторы - нежизнеспособная фича. И ход эволюции ЯП это показал.

anonymous ()

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

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

самая интересная по моему мнению система модулей в Go. Возможно она наиболее близка к идеальной

Это там экспортируемость имени определяется кейсом его первой буквы?

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

Но в файле может быть несколько подмодулей

А какая в этом практическая польза?

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

Во-вторых, тесты удобно делать подмодулем и держать в том же файле (так как писать их надо параллельно с кодом).

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

Не всегда. Например:

export foo

foo()
{
   ....
   ....
   bar();
   ....
}

bar()
{
   ...
   ...
   baz()
   ...
}

baz()
{
   ...
   ...
   if(...) foo();
   ...
}

как делить будешь? Можно сделать параметрические модули (в Racket это называется Unit), но тогда использовать становится намного менее удобно.

Возможно, все таки нужен какой-то конфиг для пакеты/сборки.

А что там должно быть написано?

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

самая интересная по моему мнению система модулей в Go

Что в ней интересного?

Возможно она наиболее близка к идеальной

Почему?

anonymous ()

Rust и Cargo же.

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

Rust и Cargo же.

Судя по https://github.com/servo/servo/blob/master/components/layout/flex.rs чересчур многословно

...
use flow;
use flow::INLINE_POSITION_IS_STATIC;
use flow::IS_ABSOLUTELY_POSITIONED;
use flow::ImmutableFlowUtils;
use flow::mut_base;
use flow::{Flow, FlowClass, OpaqueFlow};
use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS};
...
monk ★★★★★ ()
Ответ на: удаленный комментарий

и писали бы на красивых и мощных функциональных языках.

Часто слышно об этом. Но я вот моде поддался, изучил функциональщину(haskell, ocaml, erlang, scala из гибридов), но честно говоря разочарован... Ничего тут особо хорошего нет. И на ОО-языках можно встретить красивые с инженерной точки зрения системы. И в функциональщину ничего особенного нет.

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

в нашем мире идеал типичного современного «программиста»

Ну расскажи про идеал старорежимного Программиста (с придыханием). Це, паскаль, фортран? Там то конечно лямбд полные штаны, да?

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

Ну расскажи про идеал старорежимного Программиста (с придыханием). Це, паскаль, фортран? Там то конечно лямбд полные штаны, да?

Лисп, очевидно. С 1984 года.

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

А лисп тогда использовали? Просто сейчас как-то не очень.

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

но самая интересная по моему мнению система модулей в Go

А что именно вызвало интерес?

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

Это там экспортируемость имени определяется кейсом его первой буквы?

Странное решение.

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

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

Может быть не модулей, а пространств имен?

тесты удобно делать подмодулем и держать в том же файле (так как писать их надо параллельно с кодом).

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

как делить будешь?

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

А что там должно быть написано?

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

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

Судя по https://github.com/servo/servo/blob/master/components/layout/flex.rs чересчур многословно

А мне кажется, что там:

При импорте имена можно добавлять как есть (все; списком; все, кроме списка), добавлять с явным префиксом, добавлять с переименованием отдельных имён.

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

Может быть не модулей, а пространств имен?

Модулей. У них есть экспорт и импорт.

Это уже похоже на ошибку дизайна.

Не всегда. Например, есть окно, а у него есть меню. В меню есть пункт «новое окно», которые создаёт новое такое же окно. Как разделить меню и окно? А кода описания у обоих может быть достаточно много.

На самом деле, есть разные пути обхода. Можно так: https://docs.racket-lang.org/guide/Linking_Units.html. Можно передавать нужную функцию параметром: make_main_menu(&make_main_window). Можно на уровне языка разрешить циклы в зависимостях (тогда цикл компилируется всегда как одна единица компиляции). Ну или разрешить вставлять через include.

Название, описание, версия, зависимости

Понял. Согласен. В Racket есть https://docs.racket-lang.org/pkg/metadata.html . Зависимости там не между модулями, а от других пакетов (что надо скачать и установить, чтобы пакет установился).

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

А мне кажется, что там

Если судить по документации, то должно быть годно. Разве что разделение на extern crate и use непонятно зачем.

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

как делить будешь?

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

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

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

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

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

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

Мне вот интересно, если можно добавлять имена с переименовыванием, то есть ли там поиск по файлам. Уже само наличие пространств имён сильно затрудняет поиск. Виртуальные функции ещё затрудняют. Если ещё ввести переименовывание, то как вообще можно изучать код? Хотелось бы услышать об этом от тех, кто практически применяет это переименовывание.

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

А мне кажется, что там: ... можно добавлять как есть (все; списком; все, кроме списка),

«всё, кроме списка» там нет. Есть только всё и «в списке». Вот поэтому и многословно. Там где в Racket я просто напишу (requre foo (except-in bar foobar)) в Rust придётся явно указывать все символы, импортированные из foo и bar, если есть хоть одно пересечение.

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

Если ещё ввести переименовывание, то как вообще можно изучать код? Хотелось бы услышать об этом от тех, кто практически применяет это переименовывание.

Применяю в Racket сплошь и рядом. Стандартная IDE DrRacket позволяет перейти к модулю или определению символа (а также документации по ним) вне зависимости от используемого имени.

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

Не всегда. Например, есть окно, а у него есть меню. В меню есть пункт «новое окно», которые создаёт новое такое же окно.

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

Вообще, если есть такой цикл, то это или сильное сцепление и два модуля по факту должны быть одним, или косяк проектирования.

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

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

Так я про кусок в приложении, описывающий меню (все действия по пунктам меню и его структуру) + кусок, описывающий окно. Эти два куска нельзя вынести в модули, а желательно в одном модуле не более 1000 строк иметь.

Вообще, если есть такой цикл, то это или сильное сцепление и два модуля по факту должны быть одним, или косяк проектирования.

Сильное сцепление. Вот поэтому иногда желательны методы разбиения такого мега-модуля на файлы: Идеальная система модулей в языке программирования (комментарий)

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

поиск использований

Внутри одного модуля — тривиально (все переименования вверху). В каталоге — никогда не было нужно, если честно. В принципе, можно написать (загрузка через racket синтаксического дерева + сравнение по идентификатору)

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

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

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

а желательно в одном модуле не более 1000 строк иметь.

Бизнес логика должна быть не в этом модуле. У тебя опять ошибка проектирования.

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

Лисп, очевидно.

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

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

Бизнес логика должна быть не в этом модуле.

При чём тут бизнес логика?

Общее количество пунктов меню — около 300, описание каждого — 2-3 строки, даже без тела обработчика.

Общее количество элементов в главном окне тоже около 100. Чистого описания минимум строк на 500.

По-отдельности терпимо, вместе — перебор. А если обработчики пунктов меню ещё и должны взаимодействовать с областями главного окна, а не просто выполнять действия и отображать результат (в виде нового объекта), то всё становится совсем грустно.

Если же ограничения на разбивку по файлам нет, то можно вполне разумно разбить по темам.

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

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

JavaScript? Соглашусь.

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

Поиск вхождений - это одна из наиболее нужных и часто используемых мной «процедур» по ходу разработки-переработки. Что я делаю не так?

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

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

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

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

Но там искал просто по имени, так как библиотечные редко переименовывают.

monk ★★★★★ ()

В теории неплохи модули в SML. На практике - в Python.

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

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

Искать по связыванию (free-identifier=?), как выше monk сказал. Но придется написать специальный костыль для иде, т.к. дефолтно его нет.

И, собственно, в каком контексте тебе приходится искать вхождения? Зачем?

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

Можешь рассказать, для чего это используешь?

Неужели тебя никогда не интересует вопрос, где используется твоя функция? Я просто не могу это уложить в голове.

1. Хочу переименовать.

2. Предполагаю, что ф-я устарела и хочу узнать, не используется ли она где-нибудь.

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

(free-identifier=?), как выше monk сказал

Где он так сказал?

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

1. Хочу переименовать.

2. Предполагаю, что ф-я устарела и хочу узнать, не используется ли она где-нибудь.

Я просто убираю из экспорта, делаю make и смотрю, где ругаться начало. Так быстрее, чем делать поиск. Кстати, даже без переименования, какой regexp искать, чтобы найти, где используется идентификатор «a» я тоже с трудом представляю.

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

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

Тебе повезло с компилятором.

1. А если у тебя много проектов?

2. А если позднее связывание (например, tcl, sql, apply, пользовательские скрипты, конфиг.файлы)?

3. А если медленный компилятор?

4. А если компилятор покажет 2-3 ошибки и запнётся, а вхождений 100, ты так и будешь 30-50 раз запускать сборку? Неужели 1 раз найти это дольше чем 30 раз запустить сборку?

какой regexp искать, чтобы найти, где используется идентификатор «a» я тоже с трудом представляю.

regexp-ы Бог послал людям за съеденный плод, помимо терний и прочих неприятностей. «Поиск по словам».

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

А у меня в clcon есть «поиск лиспового идентификатора», который учитывает специфику синтаксиса лиспа. Правда, не будет работать для эскейпенных идентификаторов.

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

1. А если у тебя много проектов?

Поиск по многим каталогам ничем не отличается от make по тем же каталогам

2. А если позднее связывание (например, tcl, sql, apply, пользовательские скрипты, конфиг.файлы)?

Так проверка по символам, а не по их значениям. Символ либо определён в данном контексте (через define или let) либо импортирован. Если ни того ни другого нет, значит неизвестен. Или ты про eval?

3. А если медленный компилятор?

Перекомпилруются только те модули у которых изменённый прописан в зависимостях (импорте). Синтаксический анализ — самая быстрая часть компиляции (по сути это команда read-syntax). Если идентификатор не найден, то на этом компиляция заканчивается.

4. А если компилятор покажет 2-3 ошибки и запнётся, а вхождений 100, ты так и будешь 30-50 раз запускать сборку

Если я уже открыл модуль, то я могу переименовать символ при импорте или переименовать его по всему модулю. DrRacket имеет стандартную функцию. Причём переименование нормальное. То есть

(require (only-in foo x))

(displayln x)
(myfunc x)
(let ([x 2])
  (myfunc x))

(define (bar x) (+ x 1))

При переименовании x -> y (того, который в displayln) превратится в

(require (only-in foo y))

(displayln y)
(myfunc y)
(let ([x 2])
  (myfunc x))

(define (bar x) (+ x 1))
так как остальные вхождения x означают другие переменные.

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

Там где «сравнение по идентификатору».

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

Бизнес логика притом что я насмотрелся джанговских моделек на 2к строк. Никогда никакого смысла в этом не было.

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

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

А в твоем случае прям все-все сцеплено? Не верю.

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

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