Я разрабатываю функциональную библиотеку пользовательских интерфейсов в F #, и я столкнулся с ситуацией, когда мне нужно создать "коллекции" элементов гетерогенных типов. Я не хочу этого делать, прибегая к динамическому программированию и отбрасывая все на obj (технически возможно здесь, особенно потому, что я компилирую Fable). Вместо этого я хочу сохранить как можно больше безопасности типа.
Решение, с которым я столкнулся, - создать простой пользовательский оператор %%%
, который строит кортежи, а затем использует его следующим образом:
let x = 4 %%% "string" %%% () %%% 2.4
Это создает значение со следующим типом:
val x: (((int * string) * unit) * float)
Результирующие типы кажутся немного беспорядочными (особенно по мере увеличения количества значений), но это обеспечивает сильную безопасность типа для моего сценария и будет (в идеале) быть несколько скрытым для пользователей библиотеки.
Но я пытаюсь найти элегантный способ сопоставления шаблонов с этими вложенными типами кортежей, поскольку пользователям библиотеки иногда приходится писать функции по этим значениям. Очевидно, это можно сделать вручную, например,
match x with
| (((a,b),c),d) -> ...
и компилятор выводит правильные типы для a
, b
, c
и d
. Тем не менее, я не хочу, чтобы пользователю приходилось беспокоиться обо всем, что гнездится. Мне бы хотелось сделать что-то вроде:
match x with
| a %%% b %%% c %%% d -> ...
и компилятор просто вычислит все. Есть ли способ сделать что-то подобное с F #, используя активные шаблоны (или некоторые другие функции)?
EDIT:
Я должен уточнить, что я не пытаюсь сопоставить значения кортежей неизвестной "arity" во время выполнения. Я хочу сделать это только тогда, когда число (и типы) элементов известно во время компиляции. Если бы я делал первое, мне было бы хорошо с динамичным подходом.
В настоящее время я создал активные шаблоны:
let (|Tuple2|) = function | (a,b)-> (a,b)
let (|Tuple3|) = function | ((a,b),c) -> (a,b,c)
let (|Tuple4|) = function | (((a,b),c),d) -> (a,b,c,d)
...
Что можно использовать следующим образом:
let x = 4 %%% "string" %%% () %%% 2.4
let y = match x with | Tuple4 (a,b,c,d) -> ...
Это, наверное, самое лучшее, что можно сделать, и это действительно не так уж плохо для пользователей (просто нужно считать "arity" кортежа, а затем использовать правильный шаблон TupleN). Однако он все еще меня беспокоит, потому что он просто не кажется таким изящным, каким он может быть. Вам не нужно указывать количество элементов при создании x
, почему вы должны это делать, когда сопоставляете его? Мне кажется асимметричным, но я не вижу способа избежать этого.
Есть ли более глубокие причины, почему моя оригинальная идея не будет работать в F # (или статически типизированных языках вообще)? Существуют ли какие-либо функциональные языки там, где это возможно?