gpt4 book ai didi

f# - 有没有办法让 F# 保持参数的类型通用?

转载 作者:行者123 更新时间:2023-12-02 16:23:08 25 4
gpt4 key购买 nike

我是 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然后分配该类型(并在 IDE 中检查 groupTest 参数显示类型为 int list -> int list option ),然后使其在第二次调用时无效。然而,如果我在一个函数(第一个函数 block )之外运行代码,它工作得很好并且无缝切换 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# 中使用普通函数完成,但您可以使用接口(interface)对其进行编码:

type GroupTest =
abstract Invoke<'T> : 'T list -> 'T list option

这个接口(interface)有一个通用方法,即本质上是一个类型为 (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 }

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

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

关于f# - 有没有办法让 F# 保持参数的类型通用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65115658/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com