stringtranslate.com

Модула-3

Modula-3 — это язык программирования , задуманный как преемник модернизированной версии Modula-2, известной как Modula-2+ . Хотя он оказал влияние на исследовательские круги (оказав влияние на дизайн таких языков, как Java , C# , Python [8] и Nim ), он не получил широкого распространения в промышленности. Он был разработан Лукой Карделли , Джеймсом Донахью, Люсиль Глассман, Миком Джорданом (ранее в Olivetti Software Technology Laboratory), Биллом Калсовом и Грегом Нельсоном в Digital Equipment Corporation (DEC) Systems Research Center (SRC) и Olivetti Research Center (ORC) в конце 1980-х годов.

Основными особенностями Modula-3 являются модульность , простота и безопасность при сохранении мощности языка системного программирования. Modula-3 стремился продолжить традицию Pascal в отношении безопасности типов, одновременно вводя новые конструкции для практического программирования в реальном мире. В частности, Modula-3 добавил поддержку обобщенного программирования (похожего на шаблоны ), многопоточности , обработки исключений , сборки мусора , объектно-ориентированного программирования , частичного раскрытия и явного обозначения небезопасного кода. Целью разработки Modula-3 был язык, реализующий наиболее важные особенности современных императивных языков программирования в довольно простых формах. Таким образом, были опущены предположительно опасные и усложняющие особенности, такие как множественное наследование и перегрузка операторов .

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

Проект Modula-3 начался в ноябре 1986 года, когда Морис Уилкс написал Никлаусу Вирту несколько идей для новой версии Modula. Уилкс работал в DEC как раз перед этим моментом, а затем вернулся в Англию и присоединился к Совету по исследовательской стратегии Olivetti. Вирт уже перешел в Oberon , но не имел никаких проблем с командой Уилкса, продолжающей разработку под названием Modula. Определение языка было завершено в августе 1988 года, а обновленная версия — в январе 1989 года. Вскоре последовали компиляторы от DEC и Olivetti, а затем и сторонние реализации.

Его дизайн во многом был обусловлен работой над языком Modula-2+, который использовался в SRC и в исследовательском центре Acorn Computers Research Center (ARC, позже ORC, когда Olivetti приобрела Acorn) в то время, на котором была написана операционная система для многопроцессорной рабочей станции DEC Firefly VAX и на котором был написан компилятор Acorn для Acorn C и библиотека выполнения Modula (CAMEL) в ARC для проекта операционной системы ARX для линейки компьютеров Acorn Archimedes на базе ARM . Как указано в пересмотренном отчете Modula-3, на язык оказали влияние другие языки, такие как Mesa , Cedar , Object Pascal , Oberon и Euclid . [9]

В 1990-х годах Modula-3 приобрел значительную популярность как учебный язык, но он так и не был широко принят для промышленного использования. Возможно, этому способствовал упадок DEC, ключевого сторонника Modula-3 (особенно когда она перестала эффективно поддерживать его до того, как DEC была продана Compaq в 1998 году). В любом случае, несмотря на простоту и мощь Modula-3, похоже, что спрос на процедурный компилируемый язык с ограниченной реализацией объектно-ориентированного программирования был невелик . Некоторое время коммерческий компилятор CM3 поддерживался одним из главных разработчиков, работавших в DEC SRC, который был нанят до того, как DEC была продана Compaq , интегрированная среда разработки (IDE) Reactor и расширяемая виртуальная машина Java (лицензированная в двоичном коде и исходном коде и собираемая с помощью Reactor) предлагались Critical Mass, Inc., но эта компания прекратила активную деятельность в 2000 году и передала часть исходного кода своих продуктов elego Software Solutions GmbH. Modula-3 сейчас преподается в университетах в основном на курсах сравнительного программирования, а ее учебники больше не издаются. По сути, единственным корпоративным сторонником Modula-3 является elego, которая унаследовала исходные коды от Critical Mass и с тех пор выпустила несколько выпусков системы CM3 в исходном и двоичном коде. Reactor IDE была выпущена с открытым исходным кодом после нескольких лет отсутствия под новым названием CM3-IDE. В марте 2002 года elego также взяла на себя управление репозиторием другого активного дистрибутива Modula-3, PM3, который до этого поддерживался в Политехнической школе Монреаля, но впоследствии продолжил работу над HM3, совершенствовавшимся с годами, пока он не устарел.

Синтаксис

Типичным примером синтаксиса языка является программа «Hello, World!» .

МОДУЛЬ  Main ;  ИМПОРТ  IO ; НАЧАЛО  IO . Put ( "Hello World\n" ) END  Main .

Все программы на Modula-3 имеют по крайней мере файл модуля, в то время как большинство также включают файл интерфейса, который используется клиентами для доступа к данным из модуля . Как и в некоторых других языках, программа Modula-3 должна экспортировать модуль Main, который может быть либо файлом с именем Main.m3, либо файлом, который может вызываться EXPORTдля экспорта модуля Main.

МОДУЛЬ  Foo  ЭКСПОРТИРУЕТ  Main

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

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

Особенности языка

Модульность

Прежде всего, все скомпилированные модули являются либо INTERFACEреализациями MODULEs, одного или другого вида. Скомпилированный модуль интерфейса, начинающийся с ключевого слова INTERFACE, определяет константы, типы, переменные, исключения и процедуры. Модуль реализации, начинающийся с ключевого слова MODULE, предоставляет код и любые дополнительные константы, типы или переменные, необходимые для реализации интерфейса. По умолчанию модуль реализации реализует интерфейс с тем же именем, но модуль может явно ссылаться EXPORTна модуль с другим именем. Например, основная программа экспортирует модуль реализации для интерфейса Main.

МОДУЛЬ  HelloWorld  EXPORTS  Main ;  IMPORT  IO ; BEGIN  IO . Put ( "Hello World\n" ) END  HelloWorld .

Любой скомпилированный модуль может иметь IMPORTдругие интерфейсы, хотя циклический импорт запрещен. Это можно решить, выполнив импорт из реализации MODULE. Сущности внутри импортируемого модуля можно импортировать, а не только имя модуля, используя синтаксис FROM Module IMPORT Item [, Item]*:

МОДУЛЬ  HelloWorld  ЭКСПОРТИРУЕТ  Main ;  FROM  IO  IMPORT  Put ; BEGIN  Put ( "Hello World\n" ) END  HelloWorld .

Обычно импортируется только интерфейс, и используется нотация «точка» для доступа к элементам в интерфейсе (аналогично доступу к полям в записи). Типичное использование — определение одной структуры данных (записи или объекта) на интерфейс вместе с любыми вспомогательными процедурами. Здесь основной тип получит имя «T», и используется как в MyModule.T.

В случае конфликта имен между импортированным модулем и другим объектом внутри модуля ASможно использовать зарезервированное слово, как вIMPORT CollidingModule AS X;

Безопасный против небезопасного

Некоторые возможности считаются небезопасными, когда компилятор больше не может гарантировать, что результаты будут согласованными; например, при взаимодействии с языком C. Ключевое слово UNSAFE, предваряющее INTERFACEили MODULE, может использоваться для указания компилятору включить определенные низкоуровневые функции языка. Например, небезопасной операцией является обход системы типов с использованием LOOPHOLEкопирования битов целого числа в REALчисло с плавающей точкой.

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

Дженерики

Общий интерфейс и соответствующий ему общий модуль, префикс ключевого слова INTERFACEor MODULEс GENERICи принимают в качестве формальных аргументов другие интерфейсы. Таким образом (как шаблоны C++ ) можно легко определять и использовать абстрактные типы данных, но в отличие от C++ гранулярность находится на уровне модуля. Интерфейс передается в общий интерфейс и модули реализации в качестве аргументов, а компилятор сгенерирует конкретные модули.

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

Голые типы INTEGERили REALне могут быть использованы, поскольку они не являются модулями, а система дженериков основана на использовании модулей в качестве аргументов. Для сравнения, в шаблоне C++ будет использоваться голый тип.

ФАЙЛ: IntegerElem.i3

INTERFACE  IntegerElem ; CONST  Имя  =  "Integer" ; ТИП  T  =  INTEGER ; ПРОЦЕДУРА  Формат ( x :  T ):  TEXT ; ПРОЦЕДУРА  Scan ( txt :  TEXT ;  VAR  x :  T ):  BOOLEAN ; END  IntegerElem .

ФАЙЛ: GenericStack.ig

ОБЩИЙ  ИНТЕРФЕЙС  GenericStack ( Element ); (* Здесь Element.T — тип, который будет сохранен в универсальном стеке. *) ТИП  T  =  Public  OBJECT ;  Public  =  OBJECT  МЕТОДЫ  init ():  TStack ;  format ():  TEXT ;  isEmpty ():  BOOLEAN ;  count ():  INTEGER ;  push ( elm :  Element . T );  pop ( VAR  elem :  Element . T ):  BOOLEAN ;  END ; END  GenericStack .

ФАЙЛ: GenericStack.mg

ОБЩИЙ  МОДУЛЬ  GenericStack ( Element ); <  ...  подробности общей  реализации  ... > ПРОЦЕДУРА Формат ( self : T ): TEXT = VAR str : TEXT ; BEGIN str := Element . Name & "Stack{" ; FOR k := 0 TO self . n - 1 DO IF k > 0 THEN str := str & ", " ; END ; str := str & Element . Format ( self . arr [ k ]); END ; str : = str & "};" ; RETURN str ; END Формат ; < ... подробности общей реализации ... > END GenericStack .                                                    

ФАЙЛ: IntegerStack.i3

ИНТЕРФЕЙС  IntegerStack  =  GenericStack ( IntegerElem )  END  IntegerStack .

ФАЙЛ: IntegerStack.m3

МОДУЛЬ  IntegerStack  =  GenericStack ( IntegerElem )  END  IntegerStack .

Прослеживаемость

Любой идентификатор можно отследить до места его происхождения, в отличие от функции «include» других языков. Скомпилированная единица должна импортировать идентификаторы из других скомпилированных единиц с помощью оператора IMPORT. Даже перечисления используют ту же нотацию «точка», которая используется при доступе к полю записи.

ИНТЕРФЕЙС  A ; ТИП  Цвет  =  { Черный ,  Коричневый ,  Красный ,  Оранжевый ,  Желтый ,  Зеленый ,  Синий ,  Фиолетовый ,  Серый ,  Белый };КОНЕЦ  А ;
МОДУЛЬ  Б ;ИМПОРТ  А ; ИЗ  А  ИМПОРТ  Цвет ;VAR  aColor :  A . Color ;  (* Использует имя модуля в качестве префикса *)  theColor :  Color ;  (* Не имеет имени модуля в качестве префикса *)  anotherColor :  A . Color ;BEGIN  aColor  : =  A.Color.Brown ; theColor : = Color.Red ; anotherColor : = Color.Orange ; ( * Нельзя просто использовать Orange * ) END B.        

Динамическое распределение

Modula-3 поддерживает выделение данных во время выполнения . Существует два типа памяти, которые могут быть выделены, TRACEDи UNTRACED, разница в том, видит ли ее сборщик мусораNEW() или нет. используется для выделения данных любого из этих классов памяти. В UNSAFEмодуле DISPOSEдоступен для освобождения неотслеживаемой памяти.

Объектно-ориентированный

Методы объектно-ориентированного программирования могут использоваться в Modula-3, но их использование не является обязательным. Многие другие функции, предоставляемые в Modula-3 (модули, дженерики), обычно могут заменить объектно-ориентированное программирование.

Поддержка объектов намеренно сведена к самым простым терминам. Тип объекта (называемый «классом» в других объектно-ориентированных языках) вводится с OBJECTобъявлением, которое по сути имеет тот же синтаксис, что и RECORDобъявление, хотя тип объекта является ссылочным типом, тогда как RECORD в Modula-3 таковыми не являются (аналогично структурам в C). Экспортируемые типы обычно именуются T по соглашению и создают отдельный тип «Public» для раскрытия методов и данных. Например:

ИНТЕРФЕЙС  Person ; ТИП  T  < :  Public ;  Public  =  МЕТОДЫ ОБЪЕКТА  getAge (): INTEGER ; init ( name : TEXT ; age : INTEGER ): T ; END ;        КОНЕЦ  Человек .

Это определяет интерфейс Personс двумя типами, T, и Public, который определяется как объект с двумя методами, getAge()а init(). Tопределяется как подтип Publicс помощью оператора <:.

Для создания нового Person.Tобъекта используйте встроенную процедуру NEWс методом init()as

VAR  jim  :=  NEW ( Person . T ). init ( "Джим" ,  25 );

Откровение

Конструкция Modula-3 REVEALобеспечивает концептуально простой и чистый, но очень мощный механизм сокрытия деталей реализации от клиентов с произвольно большим количеством уровней дружественности . Полное раскрытие формы REVEAL T = Vможет быть использовано для демонстрации полной реализации интерфейса Personсверху. Частичное раскрытие формы REVEAL T <: Vпросто показывает, что T является супертипом V, не раскрывая никакой дополнительной информации о T. [10]

МОДУЛЬ  Человек ;REVEAL  T  =  Public  BRANDED  OBJECT  name :  TEXT ;  (* Эти две переменные *)  age :  INTEGER ;  (* являются закрытыми. *) OVERRIDES  getAge  :=  Age ;  init  :=  Init ; END ;ПРОЦЕДУРА  Возраст ( self :  T ):  ЦЕЛОЕ ЧИСЛО  =  НАЧАЛО  ВОЗВРАТ  self . age ;  КОНЕЦ  Возраст ;ПРОЦЕДУРА  Init ( self :  T ;  name :  TEXT ;  age :  INTEGER ):  T  =  BEGIN  self . name  :=  name ;  self . age  :=  age ;  RETURN  self ;  END  Init ;НАЧАЛО КОНЕЦ  Человек .

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

Modula-3 — один из немногих языков программирования, который требует, чтобы внешние ссылки из модуля были строго квалифицированы. То есть, ссылка в модуле Aна объект, xэкспортируемый из модуля, Bдолжна иметь вид B.x. В Modula-3 невозможно импортировать все экспортируемые имена из модуля.

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

Исключения

Обработка исключений основана на системе блоков TRY... EXCEPT, которая с тех пор [ требуется ссылка ] стала общепринятой. Одна особенность, которая не была принята в других языках [ требуется ссылка ] , за исключением Delphi , Python [1], Scala [2] и Visual Basic.NET , заключается в том, что EXCEPTконструкция определяет форму оператора switch с каждым возможным исключением как случаем в собственном предложении EXCEPT. Modula-3 также поддерживает конструкцию LOOP... EXIT... END, которая выполняет цикл до тех пор, пока не EXITпроизойдет ... , структура, эквивалентная простому циклу внутри предложения TRY... EXCEPT.

Многопоточный

Язык поддерживает использование многопоточности и синхронизацию между потоками. В библиотеке времени выполнения ( m3core ) есть стандартный модуль Thread, который поддерживает использование многопоточных приложений. Среда выполнения Modula-3 может использовать отдельный поток для внутренних задач, таких как сборка мусора.

Встроенная структура данных MUTEXиспользуется для синхронизации нескольких потоков и защиты структур данных от одновременного доступа с возможным повреждением или условиями гонки. LOCKОператор вводит блок, в котором заблокирован мьютекс. Разблокировка a MUTEXподразумевается выходом локуса выполнения кода из блока. Это MUTEXобъект, и, как таковой, из него могут быть получены другие объекты.

Например, в разделе ввода/вывода (I/O) библиотеки libm3 читатели и писатели (Rd.T и Wr.T) являются производными от MUTEX и блокируют себя перед доступом или изменением любых внутренних данных, таких как буферы.

Краткое содержание

Подводя итог, можно сказать, что язык обладает следующими особенностями:

Modula-3 — один из редких языков, эволюция функций которого задокументирована.

В разделе «Системное программирование с Modula-3» интенсивно обсуждаются четыре основных момента проектирования языка. Это следующие темы: эквивалентность структур и имен, правила подтипирования, универсальные модули и режимы параметров, такие как READONLY.

Стандартные библиотечные функции

Продолжая тенденцию, начатую с языка C , многие из функций, необходимых для написания реальных программ, были исключены из определения языка и вместо этого предоставлялись через стандартный набор библиотек . Большинство интерфейсов ниже подробно описаны в [11]

Стандартные библиотеки, предоставляющие следующие возможности. Они называются стандартными интерфейсами и требуются (должны быть предоставлены) в языке.

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

Как и в C, ввод-вывод также предоставляется через библиотеки, в Modula-3 называемые Rdи Wr. Объектно-ориентированный дизайн библиотек Rd (readers) и Wr (writers) подробно описан в книге Грега Нельсона. Интересным аспектом Modula-3 является то, что это один из немногих языков программирования, стандартные библиотеки которых были формально проверены на отсутствие различных типов ошибок, включая ошибки блокировки. Это было сделано под эгидой проектов Larch/Modula-3 (см. семейство Larch ) [12] и Extended static checking [13] в исследовательском центре DEC Systems .

Реализации

Доступно несколько компиляторов, большинство из которых имеют открытый исходный код .

Поскольку единственным аспектом структур данных C, отсутствующим в Modula-3, является тип объединения, все существующие реализации Modula-3 способны обеспечить хорошую совместимость двоичного кода с объявлениями типов массивов и структур языка C.

Книги

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

Проекты с использованием Modula-3

Программное обеспечение, разработанное с помощью Modula-3, включает в себя:

Влияние на другие языки программирования

Хотя Modula-3 не получил статуса основного языка, несколько частей дистрибутива DEC-SRC M3 получили его. Вероятно, наиболее влиятельной частью была библиотека сетевых объектов, которая легла в основу первой реализации удаленного вызова методов (RMI) Java, включая сетевой протокол. Только когда Sun перешла от стандарта Common Object Request Broker Architecture (CORBA) к протоколу на основе IIOP, она была отменена. Документация Java по сборке мусора удаленных объектов по-прежнему ссылается на новаторскую работу, проделанную для сетевых объектов Modula-3. [21] Реализация классов в Python также была вдохновлена ​​механизмом классов, найденным в C++ и Modula-3. [22] Также язык Nim использует некоторые аспекты Modula-3, такие как трассируемые и нетрассируемые указатели.

Ссылки

  1. ^ "Critical Mass Modula-3 (CM3)". Critical Mass Modula-3 . elego Software Solutions GmbH . Получено 21.03.2020 .
  2. ^ «Политехнический университет Монреаля Модуль-3 (PM3): Что это такое» . Политехнический институт Монреаля Модуль-3 . Элего Программные решения ГмбХ . Проверено 21 марта 2020 г.
  3. ^ Полстра, Джон Д. (9 ноября 2006 г.). "Ezm3: более простое распределение Modula-3". CVSup.org . Архивировано из оригинала 10 апреля 2013 г. . Получено 21.03.2020 .
  4. ^ Вайх, Карстен. "M3/PC Klagenfurt 96: среда Modula-3 для MS-DOS". Кафедра информатики . Университет Клагенфурта. Архивировано из оригинала 20 мая 2000 года . Получено 21.03.2020 .
  5. ^ Пичета, Доминик; Локурсио, Хьюго. «Часто задаваемые вопросы» . Получено 21.03.2020 .
  6. ^ "R/Rust — я только что узнал о Modula-3, языке, у которого было много схожих целей с Rust, и даже была экспериментальная ОС, которая полагалась на безопасность, предоставляемую этим языком". Сентябрь 2019 г.
  7. ^ ван Россум, Гвидо (май 1996 г.). «Программирование на Python: Предисловие (1-е изд.)». Python.org . Получено 21.03.2020 .
  8. ^ «Часто задаваемые вопросы по дизайну и истории: почему «self» должно использоваться явно в определениях и вызовах методов?». Python.org . 21 марта 2020 г. Получено 21 марта 2020 г.
  9. ^ Отчет abc Modula-3 (пересмотренный) Лука Карделли, Джеймс Донахью, Люсиль Глассман, Мик Джордан, Билл Калсов, Грег Нельсон. Исследовательский центр DEC Systems (SRC) Исследовательский отчет 52 (ноябрь 1989 г.)
  10. ^ Карделли, Лука; Донахью, Джеймс; Глассман, Люсиль; Джордан, Мик; Калсов, Билл; Нельсон, Грег (август 1992 г.). «Определение языка Modula-3». ACM SIGPLAN Notices . 27 (8): 15–42. doi :10.1145/142137.142141. ISSN  0362-1340.
  11. ^ ab Некоторые полезные интерфейсы Modula-3 Архивировано 2016-03-04 на Wayback Machine Джим Хорнинг, Билл Калсов, Пол МакДжонс, Грег Нельсон. Исследовательский центр DEC Systems (SRC) Исследовательский отчет 113 (декабрь 1993 г.)
  12. ^ LM3 Архивировано 2016-03-03 в Wayback Machine Кевин Д. Джонс. Исследовательский центр DEC Systems (SRC) Исследовательский отчет 72 (июнь 1991 г.)
  13. ^ Расширенная статическая проверка Архивировано 2017-07-05 в Wayback Machine Дэвид Л. Детлефс, К. Растан М. Лейно, Грег Нельсон, Джеймс Б. Сакс . Исследовательский отчет Compaq SRC 159 (декабрь 1998 г.)
  14. ^ SRC Modula-3 3.3 [ постоянная мертвая ссылка ‍ ] Билл Калсов и Эрик Мюллер. Digital Equipment Corporation (январь 1995 г.)
  15. ^ Джордан, Мик (1990). «Расширяемая среда программирования для Modula-3». SIGSOFT Softw. Eng. Notes . 15 (6): 66–76. doi : 10.1145/99278.99285 .
  16. ^ Введение в программирование с потоками Архивировано 2017-07-05 в Wayback Machine Эндрю Д. Биррелл. Исследовательский центр DEC Systems (SRC) Исследовательский отчет 35 (январь 1989)
  17. ^ Примитивы синхронизации для мультипроцессора: формальная спецификация. Архивировано 4 марта 2016 г. на Wayback Machine. AD Birrell, JV Guttag, JJ Horning, R. Levin. Исследовательский центр DEC Systems (SRC). Исследовательский отчет 20 (август 1987 г.)
  18. ^ IO Streams: Abstract Types, Real Programs Архивировано 2016-03-03 на Wayback Machine Марк Р. Браун и Грег Нельсон. Исследовательский центр DEC Systems (SRC) Исследовательский отчет 53 (ноябрь 1989 г.)
  19. ^ Справочное руководство по Modula-3 Лука Карделли, Джеймс Донахью, Люсиль Глассман, Мик Джордан, Билл Калсов, Грег Нельсон. Исследовательский центр систем DEC (SRC) (февраль 1995 г.)
  20. ^ Trestle Tutorial Архивировано 2016-03-03 в Wayback Machine Марк С. Манассе и Грег Нельсон. Исследовательский центр DEC Systems (SRC) Исследовательский отчет 69 (май 1992 г.)
  21. ^ Сборка мусора удаленных объектов, Документация по удаленному вызову методов Java для Java SE 8.
  22. ^ Классы, Официальная документация Python.

Внешние ссылки