Перейти к основному содержимому

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

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

Имя функционального типа содержит типы параметров и возвращаемого значения, разделенные символом лямбда-операции ->.

Например, инициализация и присваивание значения для функционального типа, значением которого является метод, принимающий два параметра типа Число и возвращающий значение типа Строка, может выглядеть следующим образом:

пер СуммаВСтроку: (Число, Число)->Строка = (А, Б) -> (А + Б).ВСтроку()

Значение функциональному типу может быть присвоено двумя способами:

Имя типа

Имя функционального типа содержит типы параметров и возвращаемого значения, разделенные символом лямбда-операции ->. Например:

  • Значением типа является метод с одним параметром типа Число, возвращающий значение типа Число:

    знч Квадрат: (Число)->Число = (Число) -> Число * Число
  • Значением типа является метод с двумя параметрами типа Число, возвращающий значение типа Булево:

    знч Равенство: (Число, Число)->Булево = (А, Б) -> А == Б
  • Значением типа является метод с двумя параметрами типа Число, возвращающий значение составного типа (Строка или Неопределено):

    знч ДелениеВСтроку: (Число, Число)->Строка? = (А, Б) -> Б == 0 ? Неопределено : (А / Б).ВСтроку()
  • Значением типа является метод с одним параметром составного типа (Число или Неопределено), возвращающий значение составного типа (Строка или Число):

    знч КвадратИлиНеопределено: (Число?)->Строка|Число = (Ч) -> (Ч == Неопределено ? "Не определено" : Ч * Ч)
  • Значением типа является метод без параметров, возвращающий значение типа Строка:

    знч Приветствие: ()->Строка = () -> "Привет, Мир!"
  • Значением типа является метод с одним параметром типа Строка, не возвращающий ничего:

    знч ДобавитьВМассив: (Строка)->ничто = (Строка) -> Массив.Добавить(Строка)
примечание
  • Тип ничто нельзя присвоить и с ним никак нельзя взаимодействовать.
  • Значение типа (XXX)->YYY можно присвоить в переменную типа (XXX)->ничто.
  • Составной тип, значением которого является либо Неопределено, либо метод с одним параметром типа Число, возвращающий значение типа Строка:

    пер ОбработчикДоступен: Булево = Истина
    // Может быть методом или Неопределено в зависимости от флага
    знч ОбработчикЧисел: ((Число)->Строка)? = ОбработчикДоступен ? (Число) -> "Обработано: ${(Число * 3).ВСтроку()}" : Неопределено
  • Составной тип, значением которого является либо Булево, либо метод с одним параметром типа Число, возвращающий значение типа Строка:

    пер РежимАктивен = Истина
    // Может быть методом или флагом типа Булево в зависимости от условий
    знч ПреобразовательИлиОшибка: ((Число)->Строка)|Булево = РежимАктивен ? (Число) -> "[${Число.ВСтроку()}]" : Ложь
примечание

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

  • При вызове составного функционального типа необходимо проверять его тип перед выполнением кода, чтобы избежать ошибок компиляции:

    Правильно
    метод Преобразователь(РежимАктивен: Булево): Булево|Строка|((Число)->Строка)
    знч ПреобразовательИлиОшибка: ((Число)->Строка)|Булево = РежимАктивен ? (Число) -> "[${Число.ВСтроку()}]" : Ложь

    если (ПреобразовательИлиОшибка).ПолучитьТип() != Тип<Булево> // Проверка типа
    пер Функция = (ПреобразовательИлиОшибка как (Число)->Строка)
    возврат Функция(10) // Вернет [10], если РежимАктивен = Истина
    иначе
    возврат ПреобразовательИлиОшибка // Вернет Ложь, если РежимАктивен = Ложь
    ;
    ;
    Неправильно
    метод Преобразователь(): Булево|Строка|((Число)->Строка)
    пер РежимАктивен = Истина
    знч ПреобразовательИлиОшибка: ((Число)->Строка)|Булево = РежимАктивен ? (Число) -> "[${Число.ВСтроку()}]" : Ложь

    если ПреобразовательИлиОшибка это (Число)->Строка
    возврат ПреобразовательИлиОшибка(10) // Функциональный вызов от свойства составного типа
    иначе
    возврат ПреобразовательИлиОшибка
    ;
    ;
  • Если в качестве типа переменной указано неизвестно, то проверка типов компилятора отключается и такое значение можно вызывать с любым количеством и типом параметров. Будет ошибка или нет, станет ясно только во время выполнения кода:

    пер ЛюбойМетод: неизвестно
    ЛюбойМетод = (Текст: Строка) -> Текст.ВВерхнийРегистр()
    попытка
    пер Результат1 = ЛюбойМетод("пример") // "ПРИМЕР"
    поймать Иск: Исключение
    // В этот блок не попадем, т. к. в ЛюбойМетод() передан корректный аргумент
    ;
    попытка
    пер Результат2 = ЛюбойМетод(42) // Ошибки компиляции нет
    поймать Искл: Исключение
    // Здесь будет выброшено ИсключениеПроверкиТипа
    ;

В общем виде имя функционального типа выглядит следующим образом:

(тип-параметра-1, ..., тип-параметра-n)->тип-результата

Где:

  • тип-параметра-1, ..., тип-параметра-N — типы параметров, если они есть;
  • -> — лямбда-операция;
  • тип-результата — тип возвращаемого значения.

Вызов значения функционального типа

Синтаксически выглядит как вызов обычного метода:

[выражение.]имя([аргументы])

Например:

пер ФильтрЧисел: (Число)->Булево = (Ч) -> Ч > 0     // Инициализация
пер Результат = ФильтрЧисел(-5) // Вызов
пер ФорматироватьДату: (Дата)->Строка = (Д) -> Д.Представление("dd.MM.yyyy")    // Инициализация
пер Результат = ФорматироватьДату(Дата.Сейчас()) // Вызов

При этом действует следующий приоритет разрешения имен:

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

Иерархия типов и вариантность

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

Ковариантность по возвращаемым типам

Метод, возвращающий значение производного типа, можно присвоить в метод, возвращающий значение базового типа:

метод Контрвариативность()
пер БазовыйТип: (Строка)->Объект? = (А) -> А.ПолучитьТип().ВСтроку()
пер НаследникОбъекта: (Строка)->Число = (А) -> А.Длина()

БазовыйТип("Строка") // Стд::Строка
БазовыйТип = НаследникОбъекта // Ошибки нет
БазовыйТип("Строка") // 6
;

Метод, возвращающий значение простого типа, можно присвоить в метод, возвращающий значение составного типа, в который входит этот простой тип:

метод Контрвариативность()
пер Составной: (Строка)->Число|Строка = (Текст) -> Текст.Длина() > 5 ? Текст.Длина() : "Короткая строка"
пер ТолькоЧисло: (Строка)->Число = (Текст) -> Текст.Длина()

пер Результат1 = Составной("Пример") // 6
пер Результат2 = Составной("Кот") // "Короткая строка"

Составной = ТолькоЧисло // Ошибки нет

пер Результат3 = Составной("ДлиннаяСтрока") // 13
пер Результат4 = Составной("А") // 1
;

Контравариантность по типам параметров

Метод с параметром базового типа можно присвоить в метод с параметром производного типа:

метод Контравариантность()
пер ОбработчикБазовый: (Объект)->Строка = (Параметр) -> "Тип: ${Параметр.ПолучитьТип().ВСтроку()}"
пер ОбработчикПроизводный: (Число)->Строка = (Цифра) -> "Число: $Цифра"

пер Результат = ОбработчикПроизводный(42) // Число: 42
ОбработчикПроизводный = ОбработчикБазовый // Ошибки нет
Результат = ОбработчикПроизводный(42) // Тип: Стд::Число
;

Метод с параметром составного типа можно присвоить в метод с параметром простого типа, который входит в этот составной тип:

метод Контравариантность()
пер ОбработчикСоставной: (Число|Строка)->Строка = (Данные) -> Данные это Число ? "Число: $Данные" : "Текст: $Данные"
пер ОбработчикЧисла: (Число)->Строка = (Цифра) -> "Значение: $Цифра"

пер Результат = ОбработчикЧисла(100) // Значение: 100
ОбработчикЧисла = ОбработчикСоставной // Ошибки нет
Результат = ОбработчикЧисла(100) // Число: 100
;

Присваивания в другую сторону неверны.

Примеры

См. также