#f#
#f#
Вопрос:
У меня следующая проблема:
у меня есть 2 типа, которые параметризованы и которые должны работать с универсальной функцией, если оба типа имеют одинаковые параметры типа
type A<'a> = A of 'a
type C<'a> = C of 'a
let inline save ((A a) :A<'a>) ((C c):C<'a>) = saveToDB (a :: c :: []) |> ignore
save (A 1) (C 2)
save (A "1") (C "2")
Теперь представьте функцию, которая должна выполнять save
, но с разными типами, которые будут созданы с помощью некоторого индикатора
let inline save indicator (otherval:C<'a>) =
match indicator with
| "i" -> save (A 1) otherval
| "s" -> save (A "s") otherval
В этом случае я получаю сообщение об ошибке, | "s" -> adder (A "s") otherval
говорящее, что otherval
должно иметь тип C<int>
Есть идеи о том, как подойти к этому?
Комментарии:
1. О чем говорит ошибка?
2. У вас не может быть функции типа, который определяется во время выполнения. Тип должен быть известен во время компиляции. Если вам нужно это сделать, вам придется стереть тип (т. е. использовать
obj
). Возможно, если бы вы описали более масштабную проблему, кто-нибудь мог бы предложить лучший подход.3. @FyodorSoikin представьте точку входа веб-сервиса, которая сначала создает экземпляр типа путем чтения из базы данных, а затем применяет некоторые данные к этому новому типу. Создание экземпляра происходит с помощью некоторого индикатора, который определяет, с какими параметрами этого типа следует создавать экземпляры. Теперь я хочу убедиться, что данные и новый тип имеют один и тот же параметр
4. Итак… Если вы каким-то образом определили, какой тип создавать, разве вы не можете использовать тот же механизм, чтобы определить, какую функцию вызывать?
5. @FyodorSoikin уххх — да, конечно. НО, кроме подписи типа, эти функции были бы точно такими же. Поэтому мне пришлось бы создать много-много шаблонов
Ответ №1:
Основываясь на ответе Жюльена Ронкальи, этот метод должен, по крайней мере, быть типобезопасным, потому что мы используем when guards:
let inline save ((A a) :A<'a>) ((C c):C<'a>) = saveToDB (a :: c :: []) |> ignore
let boxToGenericC<'a, 'b> (c: C<'a>) =
unbox<C<'b>>(box(c))
let save1 indicator (otherval:C<'a>) =
match indicator with
| "i" when typeof<'a> = typeof<System.Int32> -> save (A 1) (boxToGenericC<'a, int> otherval)
| "s" when typeof<'a> = typeof<string>-> save (A "s") (boxToGenericC<'a, string> otherval)
и попытка сделать save1 "s" (C 1)
приводит к сбою сопоставления с шаблоном.
Ответ №2:
Хотя это может быть и не «чистый» дизайн (и я не вижу его без изменения вашей спецификации), вы можете просто сказать системе типов перестать заботиться :
let inline firstval indicator:A<'a> =
let boxed =
match indicator with
| "i" -> box (A 1)
| "s" -> box (A "s")
unbox<A<'a>> boxed
let inline save2 indicator (otherval:C<'a>) =
save (firstval indicator) otherval
save2 "i" (C 2)
save2 "s" (C "2")
Это работает, но вы теряете некоторую проверку:
save2 "s" (C 1)
Невозможно привести объект типа ‘A`1[System.Строка]’ для ввода ‘A`1[System.Int32]’.