Активные шаблоны (active patterns)
Уникальная фишка F#: создаём собственные образцы для match — с произвольной логикой разбора.
Активный шаблон (active pattern) — пользовательская функция-разбиратель, которую можно использовать как образец в
match, придавая ему семантику предметной области.
Зачем активные шаблоны
Обычные образцы разбирают структуру данных «как есть». Но иногда хочется сопоставлять по смыслу: «чётное/нечётное», «валидный email», «попадает в диапазон». Активные шаблоны позволяют завернуть произвольную логику в имя-образец и использовать его как встроенный.
Полный активный шаблон
Объявляется в «банановых скобках» (| ... |). Он делит вход на несколько именованных случаев.
let (|Even|Odd|) n =
if n % 2 = 0 then Even else Odd
let classify n =
match n with
| Even -> sprintf "%d чётное" n
| Odd -> sprintf "%d нечётное" n
printfn "%s" (classify 4)
printfn "%s" (classify 7)Вывод:
4 чётное 7 нечётное
Теперь Even и Odd читаются в match как полноценные образцы, хотя за ними стоит наша логика.
Частичный активный шаблон
Когда вход подходит не всегда, используют частичный шаблон: он возвращает option (Some — совпало, None — нет). Его имя оформляют как (|Имя|_|).
let (|Positive|_|) n =
if n > 0 then Some n else None
let sign n =
match n with
| Positive x -> sprintf "положительное %d" x
| _ -> "не положительное"
printfn "%s" (sign 5)
printfn "%s" (sign -2)Вывод:
положительное 5 не положительное
Параметризованный шаблон
Активный шаблон может принимать аргументы — настраиваемый разбор.
let (|DivisibleBy|_|) divisor n =
if n % divisor = 0 then Some () else None
let fizz n =
match n with
| DivisibleBy 15 -> "FizzBuzz"
| DivisibleBy 3 -> "Fizz"
| DivisibleBy 5 -> "Buzz"
| _ -> string n
printfn "%s" (fizz 15)
printfn "%s" (fizz 9)Вывод:
FizzBuzz Fizz
Как работает под капотом
Активный шаблон — это обычная функция, которую компилятор вызывает при сопоставлении. Полный шаблон возвращает значение специального choice-типа (какой случай выбран), частичный — option. Компилятор подставляет вызов этой функции в дерево решений match. Поэтому активные шаблоны типобезопасны и могут участвовать в любых образцах наравне со встроенными.
Частые ошибки
- Ожидать проверку исчерпывающности для частичных шаблонов — её нет, нужна ветка
_. - Делать активные шаблоны с тяжёлыми побочными эффектами — они вызываются при сопоставлении.
- Путать полный (
(|A|B|)) и частичный ((|A|_|)) синтаксис.
Итоги
- Активные шаблоны добавляют в
matchсобственные образцы с произвольной логикой. - Полный шаблон
(|A|B|)делит вход на несколько случаев. - Частичный
(|A|_|)возвращаетoptionи требует ветку_. - Параметризованные шаблоны принимают аргументы для настраиваемого разбора.