是否有类似"刚性类型"在F#?

时间:2018-01-20 09:45:07

标签: haskell f#

在Haskell类型类中可以引入" interface"你有一些"免费"类型参数,例如"对于所有a:实例支持这组函数"。如果你实例化这样的类型类,你不能缩小这个a类型,否则你会得到关于刚性类型a的错误(比如"对于所有类型,但现在变得更窄,哪些打破了原始类型承诺为所有")。好的,我在我的F#类型(类)中实现了IEnumerable接口:

    interface System.Collections.Generic.IEnumerable<byte> with
        member __.GetEnumerator () = (__.GetBytes ()).GetEnumerator ()

这是编译,按照我的预期工作,一切都很好。但是在这里我制作了更窄的界面:IEnumerable<byte>而不是IEnumerable<'Any>。那么,IEnumerable中的类型参数是什么?你可以实现更具体,更窄的界面,对吧?它不像&#34; forall&#34;在Haskell,还是?

2 个答案:

答案 0 :(得分:4)

在您的示例中,外部GetEnumerator()实现调用__.GetBytes(),我希望它具有类型unit -> IEnumerable<byte>,并将泛型类型参数修复为byte同样。如果你能说你的类型实现了IEnumerable<'Any>,那么如果我希望'Any让我们说bool,那么程序行为应该是什么,但它仍然给了我一个字节集合?这样的程序不会在F#中键入check,因为语言规范要求程序员明确地进行所有这样的转换,以确保它对程序有意义。

如果要指定open generic接口实现,那肯定是可能的(也是常见的)。您可以让类型实现类似IEnumerable<'Any>的通用接口,但必须在类型级别指定类型参数'Any。所以你不能写这个

type MyEnumerable () =
    interface IEnumerable<'Any> with ...

但是如果除了接口之外在类型本身上声明了type参数,那么你可以这样做:

type MyEnumerable<'Any> () =
    interface IEnumerable<'Any> with ...

这主要是由于F#在.NET中的起源,其中接口在泛型之前存在并且与运行时如何与类型一起工作密切相关,但是在其接口实现中将类型与类型参数断开的类型通常无论如何都没有意义界面只能具有非常有限的能力,或者会做与事物本身无关的事情,这很可能是一个糟糕的设计。

现在,如果我回到您的示例,您可以尝试将'Any类型参数添加到接口及其实现的类型,但由于第一段中的原因,编译器会给您错误。所以你必须决定类型本身是否真的是通用的,在这种情况下GetEnumerator实现应该改变,或者它是否真的只实现了“可枚举的字节”,在这种情况下你的原始代码是正确的,不能更广泛。

答案 1 :(得分:4)

您的代码段类似于Haskell实例,而不是Haskell类。此代码段显示周围类型是IEnumerable<byte>的一个实例,类似于Haskell中的instance IEnumerable T Byte(其中T是您实现IEnumerable的类型)。

我想这里的关键见解是IEnumerable必须被视为多参数类型类,其中一个参数表示序列的类型,另一个参数表示元素的序列。

在F#中,第一个参数是隐式 - 它没有直接在接口声明中提到,但是每个接口都有它 - 它是实现接口的类型。

// F#
type MyType = MyType with
    interface IEnumerable<byte> with
        member this.GetEnumerator() = xyz


-- Haskell
class IEnumerable t e where
    getEnumerator :: t -> [e]

data MyType = MyType

instance IEnumerable MyType Byte where
    getEnumerator t = xyz

在这里,很容易看出te实际上都是“僵化”类型。多参数反映了同一类型t可能具有针对不同IEnumerable的多个e实现的事实,在F#中对类型实现多个具体IEnumerable<e>的方式相同

F#不能做的一件事(如此)为任何e提供了一个实现,如下所示:

instance IEnumerable MyType e where
    getEnumerator t = ...

要在F#中实现类似(但不相同)的效果,可以创建另一个接口并实现它:

type AnyEnumerable =
    abstract member AnyEnumerate<'a>() : IEnumerable<'a>

type MyType = MyType with
    interface AnyEnumerable with
        member this.AnyEnumerate<'a>() = 
            { new IEnumerable<'a> with
                  member this.GetEnumerator() = ... }

let x = MyType.AnyEnumerate<int>()
let y = MyType.AnyEnumerate<string>()