封闭类型的类 [英] Closed type classes

查看:130
本文介绍了封闭类型的类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有可能创建一个不能再承认新成员的类型类(可能通过使用模块边界)?我可以拒绝导出完整实例定义所必需的函数,但如果有人产生无效实例,那只会导致运行时错误。我可以让它成为编译时错误吗?

解决方案

我相信答案是合格的,取决于您尝试的是什么您可以避免从接口模块 1 中导出类型类名,而仍然导出类型类的名称功能。然后,没有人可以创建该类的实例,因为没有人可以命名它!



示例:

<$ p $ (
foo,
bar
)其中

类SecretClass a其中
foo :: a
bar :: a - > a - > a

实例SecretClass Int其中
foo = 3
bar =(+)

缺点是没有人可以用你的类作为约束来编写一个类型。这并不完全阻止人们编写具有这种类型的函数,因为编译器仍然能够推断类型。但它会非常恼人。



您可以通过提供另一个空类型类来缓解这个缺点,并将您的closed类作为超类。您将原始类的每个实例也作为子类的一个实例,并且导出子类(以及所有类型类函数),但不导出超类。 (为了清楚起见,你可能应该使用公开类而不是所有类型中的秘密类,但是我相信它可以有效)。

>例子:

$ $ p $ { - #LANGUAGE FlexibleInstances,UndecidableInstances# - }

module Foo(
PublicClass,
foo,
bar
)其中

类SecretClass a其中
foo :: a
bar :: a - > a - > a

class SecretClass a => PublicClass a

实例SecretClass Int其中
foo = 3
bar =(+)

实例SecretClass a => PublicClass a

如果您愿意手动声明一个<$对于每个 SecretClass 实例,c $ c> PublicClass 。

现在客户端代码可以使用 PublicClass 编写类型类约束,但是 PublicClass 的每个实例都需要一个 SecretClass的实例为同一类型,并且无法声明 SecretClass 的新实例,任何人都无法再创建 PublicClass 2



所有这些不会编译器将该类视为关闭的能力。它仍然会抱怨模糊的类型变量,可以通过选择唯一可见的关闭实例来解决。




<纯粹的意见:通常是一个好主意,有一个单独的内部模块,它带有一个可怕的名字,可以导出所有内容,以便您可以通过一个接口模块进行测试/调试,导入内部模块和只输出你想输出的东西。



2 我想用扩展名可以声明一个新的重叠实例。例如。如果您为 [a] 提供了一个实例,那么某人可以为<$ c声明一个新的 PublicClass 实例$ c> [Int] ,它将背负 SecretClass 的实例,用于 [a] 。但是鉴于 PublicClass 没有函数,并且它们不能写一个 SecretClass 的实例,所以我看不到那么多可以这样做。


Is it possible to create a typeclass that can no longer admit new members (perhaps by using module boundaries)? I can refuse to export a function necessary for a complete instance definition, but that only results in a runtime error if someone produces an invalid instance. Can I make it a compile time error?

解决方案

I believe the answer is a qualified yes, depending on what you're trying to achieve.

You can refrain from exporting the type class name itself from your interface module1, while still exporting the names of the type class functions. Then no one can make an instance of the class because no one can name it!

Example:

module Foo (
    foo,
    bar
) where

class SecretClass a where
    foo :: a
    bar :: a -> a -> a

instance SecretClass Int where
    foo = 3
    bar = (+)

The downside is no one can write a type with your class as a constraint either. This doesn't entirely prevent people from writing functions that would have such a type, because the compiler will still be able to infer the type. But it would be very annoying.

You can mitigate the downside by providing another empty type class, with your "closed" class as a super-class. You make every instance of your original class also an instance of the sub class, and you export the sub class (along with all of the type class functions), but not the super class. (For clarity you should probably use the "public" class rather than the "secret" one in all of the types you expose, but I believe it works either way).

Example:

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

module Foo ( 
    PublicClass,
    foo,  
    bar   
) where 

class SecretClass a where 
    foo :: a
    bar :: a -> a -> a

class SecretClass a => PublicClass a

instance SecretClass Int where 
    foo = 3
    bar = (+) 

instance SecretClass a => PublicClass a

You can do without the extensions if you're willing to manually declare an instance of PublicClass for each instance of SecretClass.

Now client code can use PublicClass to write type class constraints, but every instance of PublicClass requires an instance of SecretClass for the same type, and with no way to declare a new instance of SecretClass no one can make any more types instances of PublicClass2.

What all of this doesn't get you is the ability for the compiler to treat the class as "closed". It will still complain about ambiguous type variables that could be resolved by picking the only visible instance of "closed".


1 Pure opinion: it's usually a good idea to have a separate internal module with a scary name which exports everything so that you can get at it for testing/debugging, with an interface module that imports the internal module and only exports the things you want to export.

2 I guess with extensions someone could declare a new overlapping instance. E.g. if you've provided an instance for [a], someone could declare an new instance of PublicClass for [Int], which would piggyback on the instance of SecretClass for [a]. But given that PublicClass has no functions and they can't write an instance of SecretClass I can't see that much could be done with that.

这篇关于封闭类型的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆