Spec-Zone.ru › OCaml 5.0

11.9 Классы

  • 11.9.1 Типы классов
  • 11.9.2 Выражения классов
  • 11.9.3 Определения классов
  • 11.9.4 Спецификации классов
  • 11.9.5 Определения типов классов

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

11.9.1 Типы классов

Типы классов являются аналогом выражений типов на уровне класса: они задают общую форму и свойства типов классов.

class-type ::= [[?]label-name:] typexpr -> class-type
∣ class-body-type
class-body-type ::= object [( typexpr )] { class-field-spec } end
∣ [[ typexpr { , typexpr } ]] classtype-path
∣ let open module-path in class-body-type
class-field-spec ::= inherit class-body-type
∣ val [mutable] [virtual] inst-var-name : typexpr
∣ val virtual mutable inst-var-name : typexpr
∣ method [private] [virtual] method-name : poly-typexpr
∣ method virtual private method-name : poly-typexpr
∣ constraint typexpr = typexpr

Смотрите также следующие расширения языка: атрибуты и узлы расширений.

Простые выражения классов

Выражение classtype-path эквивалентно типу класса, связанному с именем classtype-path. Аналогично, выражение [ typexpr1 , … typexprn ] classtype-path эквивалентно параметрическому типу класса, связанному с именем classtype-path, в котором параметры типа были заменены соответственно typexpr1, …typexprn.

Функциональный тип класса

Выражение типа класса typexpr -> class-type — это тип функций классов (функций от значений до классов), которые принимают в качестве аргумента значение типа typexpr и возвращают в результате класс типа class-type.

Тип тела класса

Выражение типа класса object [( typexpr )] { class-field-spec } end — это тип тела класса. Он задаёт его переменные экземпляра и методы. В этом типе typexpr сопоставляется с типом self, тем самым предоставляя имя для типа self.

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

Локальные открытия

Локальные открытия поддерживаются в типах классов с OCaml 4.06.

Наследование

Конструктор наследования inherit class-body-type обеспечивает включение методов и переменных экземпляра из других типов классов. Типы переменных экземпляра и методов из class-body-type добавляются в текущий тип класса.

Спецификация переменной экземпляра

Спецификация переменной экземпляра записывается как val [mutable] [virtual] inst-var-name : typexpr, где inst-var-name — имя переменной экземпляра, а typexpr — её ожидаемый тип. Флаг mutable указывает, может ли эта переменная экземпляра физически изменяться. Флаг virtual указывает, что эта переменная экземпляра не инициализирована. Её можно инициализировать позднее посредством наследования.

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

Спецификация метода

Спецификация метода записывается как method [private] method-name : poly-typexpr, где method-name — имя метода, а poly-typexpr — его ожидаемый тип, возможно полиморфный. Флаг private указывает, что к методу нельзя получить доступ извне объекта.

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

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

Спецификация виртуального метода

Спецификация виртуального метода записывается method [private] virtual method-name : poly-typexpr, где method-name — имя метода, а poly-typexpr — его ожидаемый тип.

Ограничения на параметры типа

Конструкция constraint typexpr1 = typexpr2 заставляет два выражения типа быть равными. Это обычно используется для указания параметров типа: таким образом, они могут быть привязаны к конкретным выражениям типа.

11.9.2 Выражения класса

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

class-expr ::= class-path
∣ [ typexpr { , typexpr } ] class-path
∣ ( class-expr )
∣ ( class-expr : class-type )
∣ class-expr { argument }+
∣ fun { parameter }+ -> class-expr
∣ let [rec] let-binding { and let-binding } in class-expr
∣ object class-body end
∣ let open module-path in class-expr
class-field ::= inherit class-expr [as lowercase-ident]
∣ inherit! class-expr [as lowercase-ident]
∣ val [mutable] inst-var-name [: typexpr] = expr
∣ val! [mutable] inst-var-name [: typexpr] = expr
∣ val [mutable] virtual inst-var-name : typexpr
∣ val virtual mutable inst-var-name : typexpr
∣ method [private] method-name { parameter } [: typexpr] = expr
∣ method! [private] method-name { parameter } [: typexpr] = expr
∣ method [private] method-name : poly-typexpr = expr
∣ method! [private] method-name : poly-typexpr = expr
∣ method [private] virtual method-name : poly-typexpr
∣ method virtual private method-name : poly-typexpr
∣ constraint typexpr = typexpr
∣ initializer expr
END_OF_DOCUMENT_MARKER ```

См. также следующие расширения языка: локально абстрактные типы, атрибуты и узлы расширения.

Простые выражения классов

Выражение class-path вычисляет класс, связанный с именем class-path. Аналогично, выражение [ typexpr1 , … typexprn ] class-path вычисляет параметрический класс, связанный с именем class-path, в котором параметры типов соответственно инициализированы значениями typexpr1, …typexprn.

Выражение ( class-expr ) вычисляет тот же модуль, что и class-expr.

Выражение ( class-expr : class-type ) проверяет, соответствует ли class-type типу class-expr (то есть, что реализация class-expr соответствует спецификации типа class-type). Всё выражение вычисляет тот же класс, что и class-expr, за исключением того, что все компоненты, не указанные в class-type, скрыты и больше недоступны.

Применение класса

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

Функция класса

Выражение fun [[?]label-name:]pattern -> class-expr вычисляет функцию от значений к классам. При применении этой функции к значению v, это значение сопоставляется с образцом pattern, а результатом является результат вычисления class-expr в расширенной среде.

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

Выражение

fun parameter1 … parametern -> class-expr

является сокращенной формой

fun parameter1 -> … fun parametern -> expr

Локальные определения

Конструкции let и let rec связывают имена значений локально, как и для выражений языка ядра.

Если локальное определение встречается в самом начале определения класса, оно будет вычислено при создании класса (точно так же, как если бы определение было вне класса). В противном случае оно будет вычислено при вызове конструктора объекта.

Локальные открытия

Локальные открытия поддерживаются в выражениях классов начиная с OCaml 4.06.

Тело класса

class-body ::= [( pattern [: typexpr] )] { class-field }

Выражение object class-body end обозначает тело класса. Это прототип объекта: оно перечисляет переменные экземпляра и методы объекта этого класса.

Тело класса — это значение класса: оно не вычисляется сразу. Вместо этого его компоненты вычисляются каждый раз при создании объекта.

В теле класса шаблон ( pattern [: typexpr] ) сопоставляется с self, поэтому предоставляя привязку для self и типа self. Self может использоваться только в методах и инициализаторах.

Тип Self не может быть закрытым типом объекта, так что класс остаётся расширяемым.

Начиная с OCaml 4.01, ошибка возникает, если одно и то же имя метода или переменной экземпляра определяется несколько раз в одном теле класса.

Наследование

Конструктор наследования inherit class-expr позволяет повторно использовать методы и переменные экземпляра из других классов. Выражение класса class-expr должно вычисляться в тело класса. Переменные экземпляра, методы и инициализаторы из этого тела класса добавляются в текущий класс. Добавление метода переопределит любой ранее определённый метод с тем же именем.

Предок может быть связан, добавив as lowercase-ident к конструкции наследования. lowercase-ident не является истинной переменной и может использоваться только для выбора метода, т.е. в выражении lowercase-ident # method-name. Это даёт доступ к методу method-name так, как он был определён в родительском классе, даже если он переопределён в текущем классе. Область действия этой привязки предка ограничена текущим классом. К методу предка можно обратиться из подкласса, но только косвенно.

Определение переменной экземпляра

Определение val [mutable] inst-var-name = expr добавляет переменную экземпляра inst-var-name со значением по умолчанию — значение выражения expr. Флаг mutable позволяет физическое изменение этой переменной методами.

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

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

Виртуальное определение переменной экземпляра

Спецификация переменной записывается как val [mutable] virtual inst-var-name : typexpr. Она указывает, является ли переменная изменяемой, и даёт её тип.

Виртуальные переменные экземпляра были добавлены в версии 3.10.

END_OF_DOCUMENT_MARKER

Определение метода

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

Закрытый метод, метод закрытый имя-метода = выражение, — это метод, который может быть вызван только на self (из других методов одного и того же объекта, определённых в этом классе или в одном из его подклассов). Этот вызов выполняется с помощью выражения имя-значения # имя-метода, где имя-значения напрямую привязано к self в начале определения класса. Закрытые методы не появляются в типах объектов. Метод может иметь как общедоступные, так и закрытые определения, но как только появляется общедоступное определение, все последующие определения будут общедоступными.

Методы могут иметь явно полиморфный тип, позволяющий использовать их полиморфно в программах (даже для одного и того же объекта). Явное объявление можно сделать тремя способами: (1) указанием явного полиморфного типа в определении метода сразу после имени метода, т.е. метод [закрытый] имя-метода : { ' идентификатор }+ . типвыражение = выражение; (2) путём предварительного объявления явного полиморфного типа через определение виртуального метода; (3) импортом такого объявления через наследование и/или ограничение типа self.

В телах методов доступны некоторые специальные выражения для управления переменными экземпляров и дублирования self:

выражение ::= …
∣ имя-переменной-экземпляра <- выражение
∣ {< [ имя-переменной-экземпляра = выражение { ; имя-переменной-экземпляра = выражение } [;] ] >}

Выражение имя-переменной-экземпляра <- выражение изменяет текущий объект на месте, заменяя значение, связанное с имя-переменной-экземпляра, значением выражение. Конечно, эта переменная экземпляра должна быть объявленна изменяемой.

Выражение {< имя-переменной-экземпляра1 = выражение1 ; … ; имя-переменной-экземпляраn = выражениеn >} вычисляется как копия текущего объекта, в котором значения переменных экземпляра имя-переменной-экземпляра1, …, имя-переменной-экземпляраn были заменены значениями соответствующих выражений выражение1, …, выражениеn.

Определение виртуального метода

Спецификация метода записывается как метод [закрытый] виртуальный имя-метода : полиморфное-типвыражение. Она определяет, является ли метод общедоступным или закрытым, и указывает его тип. Если метод предназначен для полиморфного использования, тип должен быть явно полиморфным.

Явное переопределение

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

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

Ограничения параметров типа

Конструкт ограничение типвыражение1 = типвыражение2 принуждает два типа выражений быть равными. Это обычно используется для задания параметров типов: таким образом, они могут быть привязаны к конкретным типам выражений.

Инициализаторы

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

11.9.3 Определения классов

определение-класса ::= класс связывание-класса { и связывание-класса }
связывание-класса ::= [виртуальный] [[ параметры-типов ]] имя-класса { параметр } [: тип-класса] = выражение-класса
параметры-типов ::= ' идентификатор { , ' идентификатор }

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

END_OF_DOCUMENT_MARKER

Связывание класса связывает имя класса имя-класса со значением выражения выражение-класса. Оно также связывает тип класса имя-класса с типом класса и определяет два сокращения типов: имя-класса и # имя-класса. Первое — тип объектов этого класса, а второе — более общее, так как оно объединяется с типом любого объекта, принадлежащего подклассу (см. раздел 11.4).

Виртуальный класс

Класс должен быть помечен как виртуальный, если один из его методов является виртуальным (то есть, появляется в типе класса, но фактически не определён). Объекты не могут быть созданы из виртуального класса.

Параметры типа

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

11.9.4 Спецификации классов

спецификация-класса ::= class спецификация-класса { and спецификация-класса }
спецификация-класса ::= [virtual] [[ параметры-типов ]] имя-класса : тип-класса

Это аналог в сигнатурах определений классов. Спецификация класса соответствует определению класса, если у них одинаковые параметры типа и их типы совпадают.

11.9.5 Определения типов классов

определение-типа-класса ::= class type определение-типа-класса { and определение-типа-класса }
определение-типа-класса ::= [virtual] [[ параметры-типов ]] имя-класса = тип-тела-класса

Определение типа класса class имя-класса = тип-тела-класса определяет сокращение имя-класса для типа тела класса тип-тела-класса. Как и для определений классов, также определяются два сокращения типов имя-класса и # имя-класса. Определение может быть параметризовано некоторыми параметрами типа. Если какой-либо метод в теле типа класса является виртуальным, определение должно быть помечено как virtual.

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

© 1995-2022 INRIA.
https://v2.ocaml.org/releases/5.0/htmlman/classes.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API