所以,我非常了解代数类型和类型类,但我对它的软件工程/最佳实践方面很感兴趣。
现代的共识是什么,如果有的话,关于类型?他们邪恶吗?它们方便吗?它们是否应该被使用,何时使用?
这是我的案例研究。我正在写一个RTS风格的游戏,我有不同类型的“单位”(坦克,童子军等)。假设我想得到每个单位的最大健康值。关于如何定义它们的类型,我的两个想法如下:
ADT的不同构造器:
data Unit = Scout ... | Tank ...
maxHealth :: Unit -> Int
maxHealth Scout = 10
maxHealth Tank = 20单元的类型,每种类型都是一个实例
class Unit a where
maxHealth :: a -> Int
instance Unit Scout where
maxHealth scout = 10
instance Unit Tank where
maxHealth tank = 20显然,在最终产品中将会有更多的领域和功能。(例如,每个单位都有不同的位置,等等,所以并非所有的功能都是不变的)。
诀窍是,有些功能可能对某些单位有意义,但对另一些单位则没有意义。例如,每个单位都有一个getPosition函数,但是坦克可能有一个getArmour函数,这对于没有装甲的侦察兵来说是没有意义的。
如果我想让其他Haskellers能够理解和遵循我的代码,那么哪种“普遍接受”的方式来写这个呢?
发布于 2013-07-25 04:53:46
大多数Haskell程序员对不必要的类型不屑一顾。这些伤害类型推断;您甚至不能在没有技巧的情况下列出一个Unit的列表;在GHC中,所有的秘密字典都在传递;它们不知何故使Haddocks更难读;它们可能导致脆弱的层次结构.也许其他人可以给你进一步的理由。我想一个很好的规则是,当避免使用它们时会更痛苦的时候使用它们。例如,如果没有Eq,您必须手动传递函数来比较(例如,两个[[[Int]]])(或者使用一些临时运行时测试),这是ML编程的难点之一。
看看这篇博客文章。您使用sum类型的第一种方法是可以的,但是如果您想让用户使用新的单位或其他方法来修改游戏,我建议如下
data Unit = Unit { name :: String, maxHealth :: Int }
scout, tank :: Unit
scout = Unit { name = "scout", maxHealth = 10 }
tank = Unit { name = "tank", maxHealth = 20 }
allUnits = [ scout
, tank
, Unit { name = "another unit", maxHealth = 5 }
]在你的例子中,你需要在坦克有装甲但没有侦察的地方进行编码。很明显的可能性是用额外的信息来增加单位类型,比如一个Maybe Armor字段或者一个特殊能力列表……不一定有明确的方法。
一个重量级的解决方案,可能是过火,是使用像Vinyl这样的库来提供可扩展的记录,为您提供一种子类型。
发布于 2013-07-25 07:19:39
我倾向于只在手工生成和传递实例时才使用类型类。在我编写的代码中,这几乎是从来没有的。
发布于 2013-07-25 05:38:14
我不会考虑使用类型错误的确切时间的答案,但我目前正在编写一个库,它使用了您为Unit类描述的两种方法。我一般依赖sum类型,但是类型类型方法有一个很大的优势:它为您的Unit提供了类型级别的区别。
这迫使您将更多的代码写入到接口中,因为任何需要在Unit上具有多态性的函数都必须只使用在抽象类型分类基础上定义的函数。在我的例子中,它还允许我在幻影类型中使用Unit-type类型作为类型参数。
例如,我正在编写到纳氏的Haskell绑定(ZMQ类似于ZMQ的原始作者的项目)。在Nanomsg中,您有共享表示和语义的Socket类型。每个Socket都有一个Protocol,某些函数只能在特定Protocol的Socket上调用。我可以让这些函数抛出错误或返回Maybe,但相反,我将Protocol定义为单独的类型,它们都共享一个类。
class Protocol p where ...
data Protocol1 = Protocol1
data Protocol2 = Protocol2
instance Protocol Protocol1 where ...
instance Protocol Protocol2 where ...并使Sockets有一个幻影类型参数。
newtype Socket p = Socket ...现在,我可以在错误的协议上调用函数,这是一个类型错误。
funOnProto1 :: Socket Protocol1 -> ...然而,如果Socket只是一个sum类型,那么在编译时就不可能检查它。
https://stackoverflow.com/questions/17849249
复制相似问题