Дилемма между бесточечной функцией/замыканием и общим типом?

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 только один раз, а также быть общим?


person colinfang    schedule 16.11.2012    source источник


Ответы (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) 
person Tomas Petricek    schedule 16.11.2012

Как бы то ни было, mainAttr является значением функции. Значения не могут быть общими. Решение, как вы обнаружили, состоит в том, чтобы сделать ее «истинной» функцией, сделав параметр явным.

Что касается вашего последнего вопроса, учитывая, что alignment является значением, а не функцией, вас действительно волнует, сколько раз оно будет оцениваться? Я не уверен, но я не думаю, что каррирование даже повлияет на это.

person Daniel    schedule 16.11.2012