Как-то где-то читал пространные философские рассуждения какого то "умного" гуру о том, что using-и в C# (те что открывают пространства имен) нужно держать в "чистом теле", т.е. заботиться о них, вычищать ненужные, сортировать по алфавиту, и т.п.
Аргументировалось это тем, что мол тру-программисты начинают чтение кода именно с них и из них они получают представление о том, какие типы используются и т.п.
Но на практике это все булшит. Реальный C#-программист пользуется Решарпером который автоматом вставляет/удаляет/сортирует using-и, а на их состав не смотрит вообще, за исключением случаев когда встречаются конфликты имен.
Лично я вообще сворачиваю using-и с помощью фолдинга (колапсед-регион в Студии), а просмотр кода в файле начинаю с описания класса (который, обычно, один на файл).
В Васике даже есть возможность засунуть стандартные пространства имен в специальный файл и не дублировать их в каждом файле проекта.
Собственно, подумалось, что идея открытия пространств имен порочна сама по себе. Намного проще и удобнее исходить из предположения, что все имена доступны глобально, и воздействовать на их видимость только в случае конфликта имен в конкретном файле.
Другими словами предлагаю рассмотреть концепцию закрытия пространств имен, сокрытия ненужных символов и разрешения неоднозначности между конфликтующими именами.
В языке с сильным выводом типов многие неоднозначности могут вообще разрешаться в автомате, подсистемой вывода типов. Если оная не справляется, она выдает программисту сообщение об ошибке в котором говорится о том, что два или более символа конфликтуют между собой. Далее программист может сделать одно из двух:
1. Закрыть пространство имен (или тип) содержащий ненужные символы.
2. Определить какой из типов имеется в виду указав полностью или частично квалифицированное имя.
3. Введя псевдоним с указанием на какой из символов будет разрешаться имя.
По моему такой подход может серьезно упростить жизнь программисту.
Еще одной идеей видится пренос юсингов из отдельных файлов с кодов в описание проекта. Вместо того чтобы по 100500 раз описывать using-и в каждом файле имеет смысл указать какие из пространств имен импортируются из библиотеки. Такой подход за одно позволил бы лучше контролировать использование типов из внешних сборок.
Понятно, что эти подходы не удастся реализовать в имеющихся языках. Но при проектировании новых языков он был бы очень полезен.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>1. Закрыть пространство имен (или тип) содержащий ненужные символы.
К примеру, есть у нас класс Utils, на который куча исходников ссылается. Потом мы добавляем к проекту библиотеку, которая также содержит Utils. Эта библиотека может быть нужна всего-то в одном исходнике, а лопатить придётся кучу.
Здравствуйте, dimgel, Вы писали:
D>К примеру, есть у нас класс Utils, на который куча исходников ссылается. Потом мы добавляем к проекту библиотеку, которая также содержит Utils. Эта библиотека может быть нужна всего-то в одном исходнике, а лопатить придётся кучу.
Имена не так уж часто пересекаются на 100%. Так что не придется. А вот доступ к функциям из Utils-ов нужет очень много где.
Если же у нас в проекте два типа или метода с одним именем, то это уже проблема требующая какого-то решения. Иначе трах с ними будет повсеместный. Да и читать код когда не знаешь, что за тип в данном месте имеется в виду, тоже неудобно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, dimgel, Вы писали:
D>К примеру, есть у нас класс Utils, на который куча исходников ссылается. Потом мы добавляем к проекту библиотеку, которая также содержит Utils. Эта библиотека может быть нужна всего-то в одном исходнике, а лопатить придётся кучу.
Да, возможно я не очень хорошо объяснил. Наш гипотетический язык умеет различать перегрузки. Например, если в одном модуле Utils у нас есть функция Convert(x : int) : string, а в другом Convert(x : double) : string, то вывод типов сам поймет из какого модуля вызывается функция. Проблемы будет только, если в коде будет создаваться экземпляр класса Utils или будут два идентичных по сигнатуре и имени метода внутри этих классов. Но это уже бардак который нужно устранять, а не разруливать по месту.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Если же у нас в проекте два типа или метода с одним именем, то это уже проблема требующая какого-то решения. Иначе трах с ними будет повсеместный. Да и читать код когда не знаешь, что за тип в данном месте имеется в виду, тоже неудобно.
Ну в общем да, я и сам уже понял, что речь надо вести не про Utils, а про Utils.method().
Здравствуйте, VladD2, Вы писали:
VD>Но на практике это все булшит. Реальный C#-программист пользуется Решарпером который автоматом вставляет/удаляет/сортирует using-и, а на их состав не смотрит вообще, за исключением случаев когда встречаются конфликты имен. VD>Собственно, подумалось, что идея открытия пространств имен порочна сама по себе. Намного проще и удобнее исходить из предположения, что все имена доступны глобально, и воздействовать на их видимость только в случае конфликта имен в конкретном файле.
VD>Другими словами предлагаю рассмотреть концепцию закрытия пространств имен, сокрытия ненужных символов и разрешения неоднозначности между конфликтующими именами.
VD>Еще одной идеей видится пренос юсингов из отдельных файлов с кодов в описание проекта. Вместо того чтобы по 100500 раз описывать using-и в каждом файле имеет смысл указать какие из пространств имен импортируются из библиотеки. Такой подход за одно позволил бы лучше контролировать использование типов из внешних сборок.
Мне эта идея импонирует. Механизм using в C# выглядит каким-то полупродуманным.
То есть ни зависимости между неймспейсами по нему определить невозможно (потому что в коде могут быть прямые ссылки), ни зависимости между сборками.
Опять же — никаких гарантий консистентности между алиасами, использованными в разных файлах проекта.
С учётом того, что зависимости по сборкам всё равно настраиваются на уровне проекта, то я бы, наверое, предпочёл юсинги видеть где-то там же.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, VladD2, Вы писали:
VD>Имена не так уж часто пересекаются на 100%. Так что не придется. А вот доступ к функциям из Utils-ов нужет очень много где.
Вообще то очень часто. Чем больше проект, тем больше однофамильцев. Вот есть в проекте 50-100 модулей. Тех же Utils будет встречаться штук 10-20.
Отдельно замечу, что данная идея НЕ противоречит импорту не только классов, но и методов. Временами я это дело юзаю — несмотря на то, что эта фича Идеей не поддерживается, так что приходится прописывать ручками (да и вообще я, как честный идеалист, частенько вылизываю импорты вручную): import blablabla.MacroUtil0.assertx (таки заюзал я эту гадость, выводящую "a.==(0)" вместо "a == 0", не дождался).
Здравствуйте, VladD2, Вы писали:
VD>Другими словами предлагаю рассмотреть концепцию закрытия пространств имен, сокрытия ненужных символов и разрешения неоднозначности между конфликтующими именами.
Мне кажется здесь поможет что-то вроде области видимости с открытостью выше чем public. Это можно сделать практически не ломая C#. Все, что для этого нужно — экспорт основных экспортируемых типов модуля в глобальное пространство имен. Подключая модуль к проекту, мы получаем все, что от него нужно в большинстве случаев, без лишних юзингов.
VD>Но на практике это все булшит. Реальный C#-программист пользуется Решарпером который автоматом вставляет/удаляет/сортирует using-и, а на их состав не смотрит вообще, за исключением случаев когда встречаются конфликты имен.
Ну то есть C# окончательно превратился в Java, в которой без IDE в жизни не разберешься, что откуда берется.
Здравствуйте, Mamut, Вы писали:
M>Ну то есть C# окончательно превратился в Java, в которой без IDE в жизни не разберешься, что откуда берется.
1. В java это просто следствие огромного количества разнообразного кода, написанного под платформу. Что само по себе есть гуд.
2. А часто приходится разбираться без IDE?
Когда-то давно в языках программирования не было модификаторов доступа. Всё было открыто. Но в итоге пришли к пониманию, что ничего лишнего не должно выставляться наружу. Причём по умолчанию сейчас всегда private. Твоя идея в какой-то мере возврат к прошлому.
Или, например, в современных языках прослеживается тенденция делать типы по-умолчанию иммутабельными (то есть закрытыми от изменения). А если хочешь мутабельность — объявляй это явно.
Всё-таки логичнее явно открывать, чем явно закрывать. Хотя идея с автоматическим определением конфликтов заставляет задуматься. Может ты и прав. Современные IDE меняют старые правила.
VD> возможность засунуть стандартные пространства имен в специальный файл и не дублировать их в каждом файле проекта.
Это с одной стороны логично. Но с другой — в крупном проекте не будет ли слишком сложно разбираться в одном таком огромном файле? Но, опять же, современные средства разработки рулят, да.
Здравствуйте, koodeer, Вы писали:
K>Причём по умолчанию сейчас всегда private. Твоя идея в какой-то мере возврат к прошлому.
В скале по умолчанию public, а ещё есть case-классы для алгебраических типов. И жавовские checked exceptions они убрали.
Вообще, мне всё это напоминает мой собственный путь: дорвавшись до языка со столь мощной системой типов, я начал закручивать гайки по максимуму где надо и где не надо, иногда в ущерб удобству. Со временем начинает приходить понимание, что кое-где всё-таки не надо. (Но классы и traits тем не менее до сих пор наследую из NotNull! )
M>>Ну то есть C# окончательно превратился в Java, в которой без IDE в жизни не разберешься, что откуда берется.
D>1. В java это просто следствие огромного количества разнообразного кода, написанного под платформу. Что само по себе есть гуд. D>2. А часто приходится разбираться без IDE?
Достаточно часто код можно просто просмотреть через веб-интерфейс, например (ну типа GitHub'а или внутреннего Stash'а какого-нибудь). Например.
Во-вторых, что плохого в том, чтобы разобраться в коде без IDE? С каких пор IDE стало обязательным условием для ЯП?
Здравствуйте, Mamut, Вы писали:
M>Достаточно часто код можно просто просмотреть через веб-интерфейс, например (ну типа GitHub'а или внутреннего Stash'а какого-нибудь). Например.
В гитхабе смотреть по месту имеет смысл разве что небольшие куски, в которых не запутаешься. А что-то серьёзное — всё равно скачиваешь проект и настраиваешь IDE. Лично я жму на идентификаторах ctrl+click (goto definition) на порядок чаще, чем даже смотрю в api docs.
M>Во-вторых, что плохого в том, чтобы разобраться в коде без IDE? С каких пор IDE стало обязательным условием для ЯП?
Ты ещё скажи, с каких пор для ЯП стал обязательным условием компилятор. Бейсик, помнится, обходился.
Вопрос в сложности софта. И IDE — один из инструментов, в которые эта сложность вытесняется. Для "hello world" IDE, разумеется, не нужен. Там и веб-интерфейса хватит, ага.
Здравствуйте, Ikemefula, Вы писали:
VD>>Имена не так уж часто пересекаются на 100%. Так что не придется. А вот доступ к функциям из Utils-ов нужет очень много где.
I>Вообще то очень часто. Чем больше проект, тем больше однофамильцев. Вот есть в проекте 50-100 модулей. Тех же Utils будет встречаться штук 10-20.
У меня щас 59 модулей.
$ find -name "*Util*.scala" | wc -l
22
Хм. До хрена, и правда. Но они называются все по-разному, descriptive. Ну, кроме локальных для модулей, которые и не модули вовсе, а отдельные приложения, выполняющиеся во время сборки (т.е. не используемые в качестве зависимостей), — там просто Util.
В проектах на Java очень много конфликтов имён. Часто я пишу идентификатор, жму Ctrl+Space и получаю 3-5 вариантов из разных пакетов. Поэтому идея импортировать всё, на мой взгляд, не практична.
Сортировать импорты нужно. Бывают случаи, когда я смотрю исходники не в IDE. Хорошо упорядоченные импорты сильно помогают. Впрочем согласен, что с этой задачей IDE прекрасно справляется, главное — импортировать конкретные символы, а не целые пакеты.
Здравствуйте, vsb, Вы писали:
vsb>В проектах на Java очень много конфликтов имён. Часто я пишу идентификатор, жму Ctrl+Space и получаю 3-5 вариантов из разных пакетов. Поэтому идея импортировать всё, на мой взгляд, не практична.
Удалил свой предыдущий ответ. Влад уже отвечал мне на эту же претензию: речь не о имени класса, а об имени метода, тут совпадений будет гораздо меньше. А ещё речь о том, что компилятор с помощью вывода типов сузит количество вариантов до одного, т.к. ситуаций, когда и имена классов, и имена методов, и сигнатуры методов совпадают — исчезающе мало.
Неудобно только будет с autocompletion: вводишь имя — и всё равно видишь все эти 3-5 вариантов, т.к. пока что у компилятора/IDE никакой доп. информации нет.
Здравствуйте, VladD2, Вы писали:
VD>Собственно, подумалось, что идея открытия пространств имен порочна сама по себе. Намного проще и удобнее исходить из предположения, что все имена доступны глобально, и воздействовать на их видимость только в случае конфликта имен в конкретном файле.
Я, может не совсем в тему, расскажу, как у нас в DSL реализован пространства имен.
У нас проект состоит из набора модулей. Каждый модуль указывает список модулей, от которых он непосредственно зависит.
Модуль полностью содержится в некотором пространстве имен. Поиск имени идет сначала в текущем пространстве имен, затем во всех остальных, но только среди модулей, от которых текущий модуль зависит (либо непосредственно, либо через другие модули). Если находится несколько вариантов, то надо явно квалифицировать имя. Есть еще фича с указанием приоритета среди пространств имен, но она, конечно, костыльная, и делалась в основном для обратной совместимости (кстати надо бы ее убрать, и добавить что-нибудь вроде упомянутых псевдонимов).
Но у нас кроме функций с оверлоадингом такой же алгоритм поиска используется для других сущностей вроде классов, форм, таблиц, у которых важно лишь имя. А вот если бы нужно было действительно работать только с функциями, то тут можно было бы искать глобально (правда, не в нашем конкретном случае, нам иерархия модулей все равно нужна), так как действительно довольно редко на практике бывают фунции с одинаковыми именем и сигнатурой, но в разных пространствах имен. Так что идея мне кажется вполне рабочей, и, вроде бы, поддержку этого в IDE организовать несложно.
Представим такую ситуацию. В проекте используются две библиотеки, в которых есть два класса с одинаковым именем. Мы используем методы с разной сигнатурой, и вывод типов может определить класс какой библиотеки используется каждый раз. Теоретически, при одновременном обновлении сразу обеих этих библиотек может получиться так, что сигнатуры поменяются и вызываться будут методы не тех классов что нужно. И никаких ошибок компилятора. Случай конечно крайне гипотетический, но все же.