有没有办法让 F# 保持参数的类型泛型?

Alp*_*cat 2 f#

我是 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
Run Code Online (Sandbox Code Playgroud)

我了解 F# 的类型推断是如何工作的,并且我可以看到在 testFunc 中,它int list在第一次调用时触发groupTest,然后分配该类型(并检查groupTestIDE 中的参数将类型显示为int list -> int list option),然后使其第二次调用无效. 但是,如果我在函数(第一个函数块)之外运行代码,它就可以正常工作并pairTest<'a>int list和之间无缝切换string list

我的问题是:有没有办法让 F# 不锁定groupTest? 或者,如果没有,是否有(F#-惯用的)方法来解决这个问题?

在这个特定的例子中,我可以只传递一个length参数并List.length在函数内部移动检查,但我仍然对更一般情况的答案感到好奇(例如,如果我的检查更复杂。)

Tom*_*cek 6

这不能在 F# 中仅使用普通函数来完成。问题是在 F# 中,您不能将泛型函数作为参数传递给另一个函数。当 F# 打印您的 类型时testFunc,您会得到:

('a list -> 'a list option) -> unit
Run Code Online (Sandbox Code Playgroud)

这里的关键是它'a是一个在调用函数时固定的类型变量。正式地,对整个类型有一个通用的量化,即:

forall 'a . (('a list -> 'a list option) -> unit)
Run Code Online (Sandbox Code Playgroud)

这意味着当您调用 时testFunc,您首先设置'a为一种特定类型。您在这里需要(以及 F# 不支持的)是仅对参数类型进行量化:

(forall 'a . ('a list -> 'a list option)) -> unit
Run Code Online (Sandbox Code Playgroud)

如果你能做到这一点,那么函数体本身就可以'a在访问函数时设置为两种不同的类型。这不能在 F# 中使用普通函数完成,但您可以使用接口对其进行编码:

type GroupTest =
  abstract Invoke<'T> : 'T list -> 'T list option
Run Code Online (Sandbox Code Playgroud)

这个接口有一个泛型方法,即本质上是一个类型为 的函数(forall 'a . ('a list -> 'a list option))。然后,您可以编写testFunc为以下函数GroupTest -> unit

('a list -> 'a list option) -> unit
Run Code Online (Sandbox Code Playgroud)

这个语法有点笨拙,所以我只会在对我的域至关重要的情况下使用这个技巧。如果你只在一个地方需要这个,你可能最好使用一个简单的技巧,比如复制参数(或者可能testFunc为每个不同的复制整个函数groupTest)。但是界面技巧是有效的,它是执行您所要求的操作的通用方法。

forall 'a . (('a list -> 'a list option) -> unit)
Run Code Online (Sandbox Code Playgroud)