gpt4 book ai didi

.net - 为什么F#中有这么多不同类型的 `map`函数

转载 作者:行者123 更新时间:2023-12-03 15:22:55 24 4
gpt4 key购买 nike

我正在学习 F#。我用 Haskell 开始了 FP,对此我很好奇。

由于 F# 是 .NET 语言,对我来说声明像 Mappable 这样的接口(interface)似乎更合理。 ,就像haskell Functor类型类。

enter image description here

但是就像上图一样,F# 函数是分开的,并且是自己实现的。这种设计的设计目的是什么?对我来说,介绍 Mappable.map并且为每种数据类型实现它会更方便。

最佳答案

是的,表面上是一个非常简单的问题。但是如果你花时间把它思考到底,你就会进入无法估量的类型理论的深处。类型理论也盯着你。

首先,当然,您已经正确地发现 F# 没有类型类,这就是原因。但是你提出了一个接口(interface)Mappable .好的,让我们研究一下。

假设我们可以声明这样一个接口(interface)。你能想象它的签名会是什么样子吗?

type Mappable =
abstract member map : ('a -> 'b) -> 'f<'a> -> 'f<'b>

哪里 f是实现接口(interface)的类型。等一下! F# 也没有!这里 f是一个更高级的类型变量,而 F# 根本没有更高的类型。没有办法声明一个函数 f : 'm<'a> -> 'm<'b>或类似的东西。

但是好吧,假设我们也克服了那个障碍。现在我们有了一个接口(interface) Mappable可以通过 List 实现, Array , Seq ,和厨房水槽。可是等等!现在我们有了一个方法而不是一个函数,而且方法组合得不好!让我们看看将 42 添加到嵌套列表的每个元素:
// Good ol' functions:
add42 nestedList = nestedList |> List.map (List.map ((+) 42))

// Using an interface:
add42 nestedList = nestedList.map (fun l -> l.map ((+) 42))

看:现在我们必须使用 lambda 表达式!没有办法通过这个 .map实现到另一个函数作为值。有效地结束“函数作为值”(是的,我知道,在这个例子中使用 lambda 看起来并不是很糟糕,但相信我,它变得非常丑陋)

但是等等,我们还没有完成。现在它是一个方法调用,类型推断不起作用!由于 .NET 方法的类型签名取决于对象的类型,因此编译器无法推断两者。这实际上是新手在与 .NET 库互操作时遇到的一个非常常见的问题。唯一的解决方法是提供类型签名:
add42 (nestedList : #Mappable) = nestedList.map (fun l -> l.map ((+) 42))

哦,但这还不够!尽管我已经为 nestedList 提供了签名本身,我没有为 lambda 的参数提供签名 l .这样的签名应该是什么?你说应该是 fun (l: #Mappable) -> ... ?哦,现在我们终于得到了 N 级类型,你看, #Mappable是“任何类型 'a 使得 'a :> Mappable”的快捷方式——即一个 lambda 表达式,它本身就是通用的。

或者,或者,我们可以回到更高的善良并声明 nestedList 的类型。更确切地说:
add42 (nestedList : 'f<'a<'b>> where 'f :> Mappable, 'a :> Mappable) = ...

但是好吧,让我们暂时搁置类型推断,回到 lambda 表达式以及我们现在如何无法通过 map作为另一个函数的值。假设我们稍微扩展了语法以允许像 Elm 对记录字段所做的那样:
add42 nestedList = nestedList.map (.map ((+) 42))
.map的类型是什么?是?它必须是受约束的类型,就像在 Haskell 中一样!
.map : Mappable 'f => ('a -> 'b) -> 'f<'a> -> 'f<'b>

哇,好的。抛开 .NET 甚至不允许存在这样的类型这一事实,实际上我们只是找回了类型类!

但首先 F# 没有类型类是有原因的。上面描述了这个原因的许多方面,但更简洁的说法是:简单。

你看,这是一个毛线球。一旦你有了类型类,你就必须有约束、更高等级、N 级(或至少 2 级),并且在你知道它之前,你要求使用不可预测的类型、类型函数、GADTs,以及所有的剩下的。

但是 Haskell 确实为所有的好东西付出了代价。事实证明,没有什么好方法可以推断出所有这些东西。高级类型有点工作,但约束已经有点不。 Rank-N - 做梦都别想。即使它有效,你也会遇到类型错误,你必须有博士学位才​​能理解。这就是为什么在 Haskell 中你被温和地鼓励在所有东西上都加上类型签名。嗯,不是所有的东西,而是几乎所有的东西。在您没有放置类型签名的地方(例如,在 letwhere 中)——出人意料的是,这些地方实际上是单态的,所以您基本上又回到了简单的 F# 领域。

另一方面,在 F# 中,类型签名很少见,主要用于文档或 .NET 互操作。除了这两种情况之外,您可以在 F# 中编写一个完整的大型复杂程序,并且一次不使用类型签名。类型推断工作正常,因为它没有什么太复杂或模棱两可的事情要处理。

这就是 F# 相对于 Haskell 的一大优势。是的,Haskell 可以让你以非常精确的方式表达 super 复杂的东西,这很好。但是 F# 会让你变得非常虚幻,就像 Python 或 Ruby 一样,如果你绊倒了,编译器仍然会捕获你。

关于.net - 为什么F#中有这么多不同类型的 `map`函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61093378/

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