Общая тема по проблеме написания бибилотек

Попытаюсь обобщить существующие проблемы при написании либ.

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

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

В этой же issue предложили ввести модификаторы доступа для типов. Это опять же в основном решило бы только первую часть проблемы. Например потомки класса всё равно бы не копировались в либу, если их явно не упомянуть. Да и пришлось бы постоянно ковыряться в модулях, что бы настроить их для очередной библиотеки, если модули переиспользуются.

Далее было предложено ввести ключевое слово import Если таким методом типы будут копироваться в соответствующее пространство имён, а не в основное, то было бы действительно удобно. Ну и соответственно типам по uses [unit] import [typeNames] давать public, а по обычному usesinternal и запретить использовать их в интерфейсе. Не знаю, стоит ли тут думать об обратной совместимости, тк ещё не видел серьёзных либ на pabc.

На данный момент можно решить только вторую часть проблемы, прописав пачку синонимов. Да, все необходимые классы откопируются в либу, но это не слишком удобно. Для других .net ЯП синонимы вообще бесполезны. А в pabc они пополняют (с точки зрения компилятора) основное пространство имён, в котором может быть и без того полно всего, то есть это не полезно для intellisense.

Помимо этого, при использовании либы на другом .net языке всё проходит не совсем гладко. От каждого модуля будет видно как минимум 1 фантомный класс. Вот, например, что оставляет PABCSystem и PABCExtensions

Да, пространства имён скрыты символом $, но классы внутри – public и Intellisese может их увидеть. Однако не знаю, можно ли их сделать internal Скажется ли это как-то на раздел инициализации допустим. А вот классы, описанные в разделе реализации было бы неплохо сделать internal, это, по идее, не должно ничего нарушить.

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

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

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

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

Хотелось бы услышать мнение @Admin и @ibond и сформировать какой-то пакет issue исходя из возможностей

2 лайка

В смысле не “упомянуть”? Под модификаторами доступа имели в виду t1 = public class, а если в либе класс публичный - это уже и есть сигнал что его надо добавить в интерфейс либы, даже если его не упомянули.

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

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

Поэтому в любом случае необходимо расширение синтаксиса uses.

Ну и по поводу import:

  1. Это новое ключевое слово.
  2. Не интуитивно, потому что указанные после него имена не импортируются, а экспортируются.
  3. Такой подход позволяет только 1 уровень вложенности.

По моему лучше сделать что то типа:

// name1 и name2 будут публичным в библиотеке
uses u1 public name1, name2;
// Ничего из u1 не будет публичным
// Но u1 позволено рекурсивно упоминать другие uses-public
// Полезно, если несколько либ экспортируют одинаковый набор имён из общих модулей
uses u1; forward;
// Экспортируется весь интерфейс u1
uses u1 public *;
// Комбинация предыдущих
uses u1 public *; forward;

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

не совсем понял суть forward. а uses - public выглядит хорошо

Да…

Без него только в главном файле либы uses-public будет иметь эффект.
А forward в этом контексте позволяет явно передать права указывать, что будет публичным, вспомогательному модулю.

Ага, значит я правильно понял. Возможно для этих целей будет достаточно {$include}. Можно конечно цепочку сделать из uses - forward, но это мне кажется уже не очень интуитивным. Скорее всего uses - forward будет полезно только для написания модуля с перечислением других модулей. Пока не очень представляю модуль, который выполняет какую-то работу и при этом ещё и прокидывает какой-то модуль выше. Мне кажется это тоже саму модульность нарушает в какой-то мере

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

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

1 лайк

Вы рассуждаете с точки зрения .NET - мы рассуждаем с точки зрения PascalABC.NET. В PascalABC.NET основная единица модульности - модули Паскаля - и это очень хорошо.

Кроме этого можно просто создавать библиотеки, которые синтаксически очень похожи на модули (одно слово поменять) но являются сущностью .NET. Библиотеки, скомпилированные в PascalABC.NET, очень удобно использовать, подключая их к программам на PascalABC.NET. Это была основная цель, и она выполнена.

Можно подключать библиотеки C# - придется подключать еще их пространства имен, что менее удобно.

К программе на C# принципиально можно подключать PascalABC.NET библиотеку, но немного неудобно за счет двойного имени Libname.Libname. Но принципиально можно. Так что эта цель также достигнута.

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

Про видимость. Никто не задумывался про видимость для C#. Это не была наша цель. Не планировалось, что Паскаль-dll будут использоваться в C#. И не планируется массово. Я понимаю, что вам этого бы хотелось - чистые dll и всё такое, но не планируется. Это не просто сложно - это дестабилизирует программу.

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

они и так удобные

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

2 лайка