当我将泛型函数作为局部值传递,而不是作为参数传递时,我可以使用具有不同类型参数的泛型函数,这有什么原因吗?例如:
let f = id
let g (x,y) = (f x, f y)
g ( 1, '2')运行良好,但如果我尝试将函数作为参数传递
let g f (x,y) = (f x, f y)
g id ( 1, '2')它失败是因为它接受版本f< int >,并尝试应用它两次。
我已经找到了一个变通方法,但它迫使我编写两倍于所传递函数的代码:
let g f1 f2 (x,y) = (f1 x, f2 y)
g id id ( 1, '2')这是我第二次面对这个问题。
为什么它会这样,如果函数是一个局部值,或者如果它是作为参数传递的,那么它就不应该是相同的?
有没有一种方法可以在不复制函数的情况下做到这一点?
一个黑客,也许使用显式类型约束,内联魔术,引用?
发布于 2011-08-29 06:04:14
这是内联的魔术。让我们以kvb的代码为例,定义一个处理所有情况的gmap函数:
let inline gmap f (x, y) = f $ x, f $ y
type One = One with static member ($) (One, x) = 1 // Example1 ConvertAll
type Id = Id with static member ($) (Id , x) = x // Example2 PassThrough
type SeqSingleton = SeqSingleton with static member ($) (SeqSingleton , x) = seq [x]
type ListSingleton = ListSingleton with static member ($) (ListSingleton, x) = [x]
type ListHead = ListHead with static member ($) (ListHead, x) = List.head x
// Usage
let pair1 = gmap One ("test", true)
let pair2 = gmap Id ("test", true)
let pair3 = gmap SeqSingleton ("test", true)
let pair4 = gmap ListSingleton ("test", true)
let pair5 = gmap ListHead (["test";"test2"], [true;false])
let pair6 = ("test", true) |> gmap ListSingleton |> gmap ListHead
(* results
val pair1 : int * int = (1, 1)
val pair2 : string * bool = ("test", true)
val pair3 : seq<string> * seq<bool> = (["test"], [true])
val pair4 : string list * bool list = (["test"], [true])
val pair5 : string * bool = ("test", true)
val pair6 : string * bool = ("test", true)
*)更新
也可以使用更通用的gmap函数定义的here,然后它也可以处理n元组(n < 9)。
发布于 2011-08-28 22:32:44
正如rkhayrov在评论中提到的,当你可以有更高等级的类型时,类型推断是不可能的。在您的示例中,您有
let g f (x,y) = (f x, f y)这里有两种可能的g类型,它们是不兼容的(用一种混合的F#/Haskell语法编写的):
给定第一种类型,我们可以调用g (fun x -> 1) ("test", true)并获取(1,1)。对于第二种类型,我们可以调用g id ("test", true)并获取("test", true)。这两种类型都不比另一种更通用。
如果你想在F#中使用排名更高的类型,你可以,但是你必须明确的使用一个中间的名义类型。以下是对上述每种可能性进行编码的一种方法:
module Example1 =
type ConvertAll<'b> =
abstract Invoke<'a> : 'a -> 'b
let g (f:ConvertAll<'b>) (x,y) = (f.Invoke x, f.Invoke y)
//usage
let pair = g { new ConvertAll<int> with member __.Invoke(x) = 1 } ("test", true)
module Example2 =
type PassThrough =
abstract Invoke<'a> : 'a -> 'a
let g (f:PassThrough) (x,y) = (f.Invoke x, f.Invoke y)
//usage
let pair = g { new PassThrough with member __.Invoke(x) = x } ("test", true)发布于 2011-08-27 17:30:36
我猜这是预期的行为:
在第一种情况下,您调用两个不同版本的f (一个使用int,另一个使用char),在第二种情况下,您对这两个版本都使用相同的版本,编译器会推断它(上下左右-还记得吗?)成为int->int
问题是,编译器会将通用版本翻译成具体的版本。我看不到解决这个问题的办法--即使是用inline也不行,但也许有人可以在这里施展魔法;)
https://stackoverflow.com/questions/7213599
复制相似问题