Дилемма между точечной свободной функцией / замыканием и типом generic?
type Alignment =
| Horizontal
| Vertical
let getMainAttr = function
| Horizontal -> fst
| Vertical -> snd
let check alignment =
let mainAttr = getMainAttr alignment
mainAttr (2,3) |> ignore
mainAttr (2.0, 3.0) // error
val getMainAttr : _arg1:Alignment -> ('a * 'a -> 'a)
mainAttr : (int * int -> int) // because of the value restriction
кажется, единственный способ сделать его родовым - это сделать его явным, например, let mainAttr x = getMainAttr alignment x
Тем не менее, таким образом, он больше не использует закрытие, так что каждый раз mainAttr
называется alignment
должен быть проверен.
Есть ли способ только проверить alignment
один раз, а также быть общим?
2 ответа
Как описывает @Daniel, вы попали в ограничение ограничения значений, которое запрещает создание общих значений, являющихся результатом некоторых вычислений F# (даже если значение является функцией). Вы можете найти больше информации об этом в других вопросах SO, а также есть статья о дополнительных моментах. Причина этого ограничения заключается в том, что общие значения могут создать лазейку в типе безопасности.
В вашем примере вам не нужно беспокоиться, потому что выполнение getMainAttr
Функция многократно не собирается добавлять столько накладных расходов. Если функция выполняет более сложные вычисления, вы можете вернуть интерфейс с помощью универсального метода (вместо простой функции):
/// Interface with as single generic function that selects element of a pair
type PairSelector =
abstract Invoke<'T> : 'T * 'T -> 'T
// Two possible implementations of the interface
let first = { new PairSelector with member x.Invoke(a, b) = a }
let second = { new PairSelector with member x.Invoke(a, b) = b }
// Return a non-generic `PairSelector` value instead of a generic function
let getMainAttr = function
| Horizontal -> first
| Vertical -> second
// Now we can get `PairSelector` value and call it with different type arguments
let check alignment =
let mainAttr = getMainAttr alignment
mainAttr.Invoke (2,3) |> ignore
mainAttr.Invoke (2.0, 3.0)
Как это, mainAttr
является значением функции. Значения не могут быть общими. Как вы обнаружили, решение состоит в том, чтобы сделать ее "истинной" функцией, сделав параметр явным.
Что касается вашего последнего вопроса, учитывая, что alignment
это значение, а не функция, вас действительно волнует, сколько раз оно будет оцениваться? Я не уверен, но я не думаю, что карри даже повлияет на это.