我是F#的新手(到目前为止很喜欢),并且我一直在尝试代码。我发现了一些我理解其原因的东西,但不知道如何克服它。
我有一些类似于以下内容的代码:
let pairTest list = if List.length list = 2 then Some list else None
// This seems to compile and work just fine
[ [1; 2] ]
|> List.choose pairTest
|> List.map (fun l -> l |> List.map (fun i -> "a"))
|> List.choose pairTest
|> printfn "%A"
let testFunc groupTest =
[ [1; 2] ]
|> List.choose groupTest // Okay
|> List.map (fun l -> l |> List.map (fun i -> "a"))
|> List.choose groupTest // Error: expected int list, not string list
|> printfn "%A"
testFunc pairTest
我了解F#的类型推断的工作原理,并且可以看到在testFunc中,它是int list
在第一次调用时到达的groupTest
,然后分配该类型(然后groupTest
在IDE中检查该参数将其显示为int list -> int list option
),然后使其对于第二次调用无效。但是,如果我运行一个函数(第一功能块)的代码外,它工作得很好并无缝地切换pairTest<'a>
之间int list
和string list
。
我的问题是:有没有办法阻止F#锁定类型groupTest
?或者,如果不是,是否有一种(F#-惯用的)方法来解决这个问题?
在这种特定情况下,我只可以传入一个length
参数并将List.length
检查移到函数内部,但是我仍然对更一般情况的答案感到好奇(例如,如果我的检查比较复杂)。
仅使用普通函数无法在F#中完成此操作。问题是在F#中,你不能将通用函数作为参数传递给另一个函数。当F#打印的类型时testFunc
,你将得到:
('a list -> 'a list option) -> unit
这里的关键是,'a
当你调用函数时,它是一个固定的类型变量。形式上,对整个类型有一个通用的量化,即:
forall 'a . (('a list -> 'a list option) -> unit)
这意味着,当你调用时testFunc
,首先要设置'a
为一种特定的类型。这里需要的(以及F#不支持的功能)是仅对参数类型进行量化:
(forall 'a . ('a list -> 'a list option)) -> unit
如果可以执行此操作,则函数主体本身可以'a
在访问该函数时设置为两种不同的类型。这在F#中无法通过普通函数完成,但是你可以使用接口对此进行编码:
type GroupTest =
abstract Invoke<'T> : 'T list -> 'T list option
该接口具有通用方法,即本质上是type的函数(forall 'a . ('a list -> 'a list option))
。然后,你可以testFunc
根据以下内容编写GroupTest -> unit
:
let testFunc (groupTest:GroupTest) =
[ [1; 2] ]
|> List.choose groupTest.Invoke
|> List.map (fun l -> l |> List.map (fun i -> "a"))
|> List.choose groupTest.Invoke
|> printfn "%A"
testFunc { new GroupTest with member x.Invoke a = pairTest a }
这样做的语法有点笨拙,因此,只有对我的域而言必不可少的情况下,我才使用此技巧。如果仅在一个地方需要此功能,则最好使用简单的技巧,例如复制参数(或testFunc
为每个不同的函数复制整个函数groupTest
)。但是界面技巧起作用了,这是完成你所要询问的一种通用方法。
let testFunc groupTest1 groupTest2 =
[ [1; 2] ]
|> List.choose groupTest1
|> List.map (fun l -> l |> List.map (fun i -> "a"))
|> List.choose groupTest2
|> printfn "%A"
testFunc pairTest pairTest
谢谢!那正是我想知道的。我特别感谢您的答复中的形式主义。