首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ADTs与Typeclasses -规范用法

ADTs与Typeclasses -规范用法
EN

Stack Overflow用户
提问于 2012-11-29 00:46:51
回答 1查看 610关注 0票数 8

我在一个特定数据结构的两种实现之间左右为难,Haskell社区对什么是正确的/标准的输入将不胜感激。

数据类型

以ADT "Server“为例,它将多个服务器定义为空数据构造函数。

代码语言:javascript
复制
data Server = Server1
            | Server2
            | Server3

现在,对于这些服务器中的每一个,我都希望(在其他方面)能够获得IP地址。假设我可以静态地编写这些代码,我可以使用一些函数"getURL“和模式匹配。

代码语言:javascript
复制
getUrl :: Server -> String
getUrl Server1 = "192.168.1.1"

现在,任何使用服务器的函数都可以将服务器放在类型中并调用getURL。

代码语言:javascript
复制
serverStuff :: Server -> IO ()

这种方法似乎具有简单、非多态函数的优点,但代价是getURL中有大量的模式匹配。此外,如果程序员添加了一个服务器,但忘记将该模式添加到getURL中,除非他们使用-Wall进行编译,否则他们将在没有警告的情况下收到运行时错误。

类型类

解决与类型类相同的问题,我可以将我的多构造函数ADT分解为一组特定于服务器的ADT,并为URL创建一个类型类。

代码语言:javascript
复制
data Server1 = Server1
data Server2 = Server2
data Server3 = Server3

class Server a where
    getUrl :: a -> String

instance Server Server1 where
    getUrl Server1 = "192.168.1.1"

现在,我必须创建如下内容,而不是我以前使用的简单的非多态函数

代码语言:javascript
复制
serverStuff :: Server a => a -> IO ()

并处理ad-hoc多态的含义(函数专门化等)。

好的一面是,类型类方法易于扩展,将模式匹配分解为更小的块,允许更大的抽象,例如分组服务器(data ServerCenter1 = Server1 | Server2 | Server3),尽管如果不声明getUrl,您仍然可以获得运行时错误(没有编译器警告),但至少在创建实例时必须做出这个决定。

因此,我很困惑,但倾向于将实例作为一种更好的处理方式。有没有一个标准的方法来处理这个问题,或者它是一种“看起来干净的东西”类型的东西?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-11-29 01:16:01

如果您确信您的服务器类型需要包含的唯一信息,我会将它们实现为一个字符串周围的newtype:

代码语言:javascript
复制
newtype Server = Server { getURL :: String }

让它成为一个完整的记录(就像在哈马尔的评论中那样)将允许您添加信息,同时只更改构造函数,而不是以GeneralizedNewtypeDeriving为代价。

一般来说,我会使用类型来表示事物的类,使用变量来表示细节,这样空值构造函数只用于表示抽象,例如data Status = Published | Draft (或内置布尔值)。应避免将数据(如ip地址)硬编码到类型系统或函数中,除非有特定原因。

如果您想要特定于服务器的行为,则很容易将字段添加到记录中:

代码语言:javascript
复制
data Server = Server {
              getURL      :: String
            , doSomething :: a -> IO () --Or any other functional signature
            }

但是,我建议不要这样做,因为这会使其他代码变得模糊:

代码语言:javascript
复制
runSomething :: Server -> a -> IO ()
runSomething server arg = (doSomething server) arg

完全可以做任何事情,并且您需要定位该字段的最后一次更新以确定要做什么(因为函数没有Show实例)。如果依赖关系依赖于服务器的某些属性,我倾向于编码该属性,然后在该属性上进行调度,例如

代码语言:javascript
复制
data ServerType = Production
                | Development

data Server = Server {
              getURL     :: String
            , serverType :: ServerType
            }

runSomething :: Server -> a -> IO ()
runSomething server arg = case serverType server of
                              Production  -> foo arg
                              Development -> bar arg

我认为这种方法比将服务器名称硬编码到函数中更好,因为它解释了为什么给定的服务器具有给定的行为(并使更改特定服务器的行为更加本地化),并且将函数放在记录字段中,因为它更容易告诉给定的runSomething调用将做什么(因为可以检查和记录ServerType)。

票数 13
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13610164

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档