【发布时间】:2011-05-01 09:24:58
【问题描述】:
我正在尝试将 Haskell 核心库的 Arrows 翻译成 F#(我认为这是更好地理解 Arrows 和 F# 的一个很好的练习,并且我可能能够在我正在从事的项目中使用它们。)但是,由于范式不同,无法直接翻译。 Haskell 使用类型类来表达这些东西,但我不确定哪种 F# 构造最能将类型类的功能与 F# 的习惯用法相映射。我有一些想法,但我认为最好将其提出来,看看在功能上什么被认为是最接近的。
对于 tl;dr 人群:我如何将类型类(Haskell 习惯用法)翻译成 F# 惯用代码?
对于那些接受我冗长解释的人:
Haskell 标准库中的这段代码是我试图翻译的示例:
class Category cat where
id :: cat a a
comp :: cat a b -> cat b c -> cat a c
class Category a => Arrow a where
arr :: (b -> c) -> a b c
first :: a b c -> a (b,d) (c,d)
instance Category (->) where
id f = f
instance Arrow (->) where
arr f = f
first f = f *** id
尝试1:模块、简单类型、让绑定
我对此的第一个尝试是直接使用模块进行组织映射,例如:
type Arrow<'a,'b> = Arrow of ('a -> 'b)
let arr f = Arrow f
let first f = //some code that does the first op
这行得通,但它失去了多态性,因为我没有实现类别,也无法轻松实现更专业的箭头。
尝试 1a:使用签名和类型进行优化
纠正尝试 1 的一些问题的一种方法是使用 .fsi 文件来定义方法(以便类型更容易执行)并使用一些简单的类型调整来专门化。
type ListArrow<'a,'b> = Arrow<['a],['b]>
//or
type ListArrow<'a,'b> = LA of Arrow<['a],['b]>
但是 fsi 文件不能被其他实现重用(以强制执行 let 绑定函数的类型),并且类型重命名/封装的东西很棘手。
尝试 2:对象模型和接口
将 F# 也构建为 OO 合理化,也许类型层次结构是做到这一点的正确方法。
type IArrow<'a,'b> =
abstract member comp : IArrow<'b,'c> -> IArrow<'a,'c>
type Arrow<'a,'b>(func:'a->'b) =
interface IArrow<'a,'b> with
member this.comp = //fun code involving "Arrow (fun x-> workOn x) :> IArrow"
除了让静态方法(如 comp 和其他运算符)像实例方法一样工作可能会带来多大的痛苦之外,还需要显式地向上转换结果。我也不确定这种方法是否仍能捕捉到类型类多态性的完整表现力。这也使得使用必须是静态方法的东西变得困难。
尝试 2a:使用类型扩展进行优化
因此,另一种可能的改进是尽可能地声明接口,然后使用扩展方法为所有实现类型添加功能。
type IArrow<'a,'b> with
static member (&&&) f = //code to do the fanout operation
啊,但这让我只能对所有类型的 IArrow 使用一种方法。如果我想要 ListArrows 稍有不同 (&&&),我该怎么办?我还没有尝试过这种方法,但我想我可以隐藏 (&&&),或者至少提供一个更专业的版本,但我觉得我无法强制使用正确的变体。
帮帮我
那么我应该在这里做什么?我觉得 OO 应该足够强大以取代类型类,但我似乎无法弄清楚如何在 F# 中实现这一点。我的任何尝试都接近了吗?他们中的任何一个都“尽可能好”并且必须足够好吗?
【问题讨论】:
-
“我如何将类型类(Haskell 习惯用法)翻译成 F# 惯用代码?”馊主意。您应该查看您试图解决的问题,而不是碰巧使用类型类的解决方案,并弄清楚如何使用 F# 提供的功能来解决它。
-
@Jon Harrop 这就是问题的重点。 Haskell 用类型类解决了几十个问题,我想知道 F# 的替代方案是什么来解决类似的 class 问题。此外,Arrow 端口并不是为了解决任何问题,它只是一个“我认为了解更多关于 Arrows 的信息会很有趣”的活动。
-
那么我认为如果你能解释一些类型类解决的问题并询问人们如何在 F# 中解决同样的问题,那将是非常有价值的。
-
@Gustavo 我很乐意换个答案(我很长一段时间都没有想过这个问题),但我并没有完全遵循你的代码。您的方法是否解决了第一个问题中提出的关于更高种类多态性的问题?就像我有一个包含 Monad 类型的 Arrow 一样,这一切都会奏效吗?
-
@CodexArcanum 是的,有点。推断的静态约束要求泛型类中存在一些静态方法。它适用于方法级别,因此它不仅适用于 Monad,而且适用于每个具有 Return 和 Bind 的类,最终它可能只需要 Return(如 Id 的情况)。
标签: oop haskell f# functional-programming typeclass