2
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一次并且是通用的?

4

2 回答 2

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) 
于 2012-11-16T16:01:45.417 回答
1

实际上,mainAttr是一个函数。值不能是通用的。正如您所发现的,解决方案是通过显式参数使其成为“真正的”函数。

关于你的最后一个问题,考虑到它alignment是一个值而不是一个函数,你真的关心它会被评估多少次吗?我不确定,但我认为咖喱甚至不会影响这一点。

于 2012-11-16T15:50:57.220 回答