Дилемма между точечной свободной функцией / замыканием и типом 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 это значение, а не функция, вас действительно волнует, сколько раз оно будет оцениваться? Я не уверен, но я не думаю, что карри даже повлияет на это.

Другие вопросы по тегам