Функциональные типы
Функциональный тип — это тип, значениями которого являются методы. Функциональные типы позволяют хранить методы в переменных, передавать их в другие методы как аргументы и возвращать как результат.
Имя функционального типа содержит типы параметров и возвращаемого значения, разделенные символом лямбда-операции ->.
Например, иниц иализация и присваивание значения для функционального типа, значением которого является метод, принимающий два параметра типа Число и возвращающий значение типа Строка, может выглядеть следующим образом:
пер СуммаВСтроку: (Число, Число)->Строка = (А, Б) -> (А + Б).ВСтроку()
Значение функциональному типу может быть присвоено двумя способами:
-
через лямбда-выражение:
пер СравнитьНаМеньше = (А: Строка, Б:Строка) -> А.Длина() < Б.Длина() -
через ссылку на существующий метод:
пер Результат = &СуммаВСтроку
Имя типа
Имя функционального типа содержит типы параметров и возвращаемого значения, разделенные символом лямбда-операции ->. Например:
-
Значением типа является метод с одним параметром типа
Число, возвращающий значение типаЧисло:знч Квадрат: (Число)->Число = (Число) -> Число * Число -
Значением типа является метод с двумя параметрами типа
Число, возвращающий значение типаБулево:знч Равенство: (Число, Число)->Булево = (А, Б) -> А == Б -
Значением типа является метод с двумя параметрами типа
Число, возвращающий значение составного типа (СтрокаилиНеопределено):знч ДелениеВСтроку: (Число, Число)->Строка? = (А, Б) -> Б == 0 ? Неопределено : (А / Б).ВСтроку() -
Значением типа является метод с одним параметром составного типа (
ЧислоилиНеопределено), возвращающий значение составного типа (СтрокаилиЧисло):знч КвадратИлиНеопределено: (Число?)->Строка|Число = (Ч) -> (Ч == Неопределено ? "Не определено" : Ч * Ч) -
Значением типа является метод без параметров, возвращающий значение типа
Строка:знч Приветствие: ()->Строка = () -> "Привет, Мир!" -
Значением типа является метод с одним параметром типа
Строка, не возвращающий ничего:знч ДобавитьВМассив: (Строка)->ничто = (Строка) -> Массив.Добавить(Строка)
- Тип
ничтонельзя присвоить и с ним никак нельзя взаимодействовать. - Значение типа
(XXX)->YYYможно присвоить в переменную типа(XXX)->ничто.
-
Составной тип, значением которого является либо
Неопределено, либо метод с одним параметром типаЧисло, возвращающий значение типаСтрока:пер ОбработчикДоступен: Булево = Истина
// Может быть методом или Неопределено в зависимости от флага
знч ОбработчикЧисел: ((Число)->Строка)? = ОбработчикДоступен ? (Число) -> "Обработано: ${(Число * 3).ВСтроку()}" : Неопределено -
Составной тип, значением которого является либо
Булево, либо метод с одним параметром типаЧисло, возвращающий значение типаСтрока:пер РежимАктивен = Истина
// Может быть методом или флагом типа Булево в зависимости от условий
знч ПреобразовательИлиОшибка: ((Число)->Строка)|Булево = РежимАктивен ? (Число) -> "[${Число.ВСтроку()}]" : Ложь
Если функциональный тип входит в составной тип, то имя такого функционального типа должно быть заключено в скобки, иначе все, что следует за лямбда-операцией ->, будет считаться составным типом результата.
-
При вызове составного функционального типа необходимо проверять его тип перед выполнением кода, чтобы избежать ошибок компиляции:
Правильнометод Преобразователь(РежимАктивен: Булево): Булево|Строка|((Число)->Строка)
знч ПреобразовательИлиОшибка: ((Число)->Строка)|Булево = РежимАктивен ? (Число) -> "[${Число.ВСтроку()}]" : Ложь
если (ПреобразовательИлиОшибка).Получ итьТип() != Тип<Булево> // Проверка типа
пер Функция = (ПреобразовательИлиОшибка как (Число)->Строка)
возврат Функция(10) // Вернет [10], если РежимАктивен = Истина
иначе
возврат ПреобразовательИлиОшибка // Вернет Ложь, если РежимАктивен = Ложь
;
;Неправильнометод Преобразователь(): Булево|Строка|((Число)->Строка)
пер РежимАктивен = Истина
знч ПреобразовательИлиОшибка: ((Число)->Строка)|Булево = РежимАктивен ? (Число) -> "[${Число.ВСтроку()}]" : Ложь
если ПреобразовательИлиОшибка это (Число)->Строка
возврат ПреобразовательИлиОшибка(10) // Функциональный вызов от свойства составного типа
иначе
возврат ПреобразовательИлиОшибка
;
; -
Если в качестве типа переменной указано
неизвестно, то проверка типов компилятора отключается и такое значение можно вызывать с любым количеством и типом параметров. Будет ошибка или нет, станет ясно только во время выполнения кода:пер ЛюбойМетод: неизвестно
ЛюбойМетод = (Текст: Строка) -> Текст.ВВерхнийРегистр()
попытка
пер Результат1 = ЛюбойМетод("пример") // "ПРИМЕР"
поймать Иск: Исключение
// В этот блок не попадем, т. к. в ЛюбойМетод() передан корректный аргумент
;
попытка
пер Результат2 = ЛюбойМетод(42) // Ошибки компиляции нет
поймать Искл: Исключение
// Здесь будет выброшено ИсключениеПроверкиТипа
;
В общем виде имя функционального типа выглядит следующим образом:
(тип-параметра-1, ..., тип-параметра-n)->тип-результата
Где:
- тип-параметра-1, ..., тип-параметра-N — типы параметров, если они есть;
->— лямбда-операция;- тип-результата — тип возвращаемого значения.
Вызов значения функционального типа
Синтаксически выглядит как вызов обычного метода:
[выражение.]имя([аргументы])
Например:
пер ФильтрЧисел: (Число)->Булево = (Ч) -> Ч > 0 // Инициализация
пер Результат = ФильтрЧисел(-5) // Вызов
пер ФорматироватьДату: (Дата)->Строка = (Д) -> Д.Представление("dd.MM.yyyy") // Инициализация
пер Результат = ФорматироватьДату(Дата.Сейчас()) // Вызов
При этом действует следующий приоритет разрешения имен:
- если существует метод с указанным именем (любого приоритета), то считается, что это вызов метода;
- иначе ищется свойство с указанным именем в порядке приоритетов и считается вызовом функционального типа.
Иерархия типов и вариантность
Значения функциональных типов обеспечивают ковариантность по возвращаемым типам и контравариантность по типам параметров.