gpt4 book ai didi

f# - 在 F# 中制作鱼

转载 作者:行者123 更新时间:2023-12-04 18:59:05 26 4
gpt4 key购买 nike

Kleisli 组合算子 >=> ,在 Haskell 圈子中也被称为“鱼”,在许多需要组合专用函数的情况下可能会派上用场。它有点像 >>运算符,而不是组合简单的函数 'a -> 'b它赋予它们一些特殊属性,可能最好表示为 'a -> m<'b> , 其中 m要么是类单子(monad)类型,要么是函数返回值的某些属性。

在更广泛的 F# 社区中可以找到这种做法的证据,例如在 Scott Wlaschin 的 Railway oriented programming (part 2)作为返回 Result<'TSuccess,'TFailure> 的函数的组合类型。

推断哪里有绑定(bind),哪里也有鱼,我尝试参数化规范的 Kleisli 运算符的定义 let (>=>) f g a = f a >>= g使用绑定(bind)函数本身:

let mkFish bind f g a = bind g (f a)

这与通常不应在面向用户的代码上释放特殊运算符的警告非常有效。我可以编写返回选项的函数...
module Option =
let (>=>) f = mkFish Option.bind f
let odd i = if i % 2 = 0 then None else Some i
let small i = if abs i > 10 then None else Some i
[0; -1; 9; -99] |> List.choose (odd >=> small)
// val it : int list = [-1; 9]

...或者我可以为堆栈的两个最顶层值设计一个函数应用程序并将结果推回,而无需显式引用我正在操作的数据结构:
module Stack =
let (>=>) f = mkFish (<||) f
type 'a Stack = Stack of 'a list
let pop = function
| Stack[] -> failwith "Empty Stack"
| Stack(x::xs) -> x, Stack xs
let push x (Stack xs) = Stack(x::xs)
let apply2 f =
pop >=> fun x ->
pop >=> fun y ->
push (f x y)

但困扰我的是签名 val mkFish : bind:('a -> 'b -> 'c) -> f:('d -> 'b) -> g:'a -> a:'d -> 'c没有意义。类型变量的顺序令人困惑,它过于笼统( 'a 应该是一个函数),而且我没有看到一种自然的注释方式。

在没有正式仿函数和单子(monad)的情况下,我如何在这里进行抽象,而不必为每种类型显式定义 Kleisli 运算符?

最佳答案

如果没有 Higher Kinds,您将无法以自然的方式做到这一点。
鱼的签名应该是这样的:

let (>=>) (f:'T -> #Monad<'U>``) (g:' U -> #Monad<'V>) (x:'T) : #Monad<'V> = bind (f x) g
这在当前的 .NET 类型系统中无法表示,但您可以替换 #Monad使用您的特定单子(monad),即: Async并在实现中使用其对应的绑定(bind)函数。
话虽如此,如果您真的想使用通用鱼运算符,可以使用 F#+它已经通过使用静态约束来定义。如果你看第 5 个 code sample here您将看到它在不同类型上的作用。
当然你也可以定义你自己的,但是有很多东西要编码,以使它在最常见的场景中正常运行。您可以从库中获取代码,或者如果您愿意,我可以编写一个小(但有限)的代码示例。
通用鱼在 this line 中定义。 .
我认为总的来说,您在使用运算符时确实会感到缺乏通用功能,因为正如您所发现的,您需要打开和关闭模块。它不像函数那样使用模块名称作为前缀,您也可以使用运算符(例如 Option.(>=>) ),但是它违背了使用运算符的全部目的,我的意思是它不再是运算符。

关于f# - 在 F# 中制作鱼,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42816639/

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