【发布时间】:2015-08-04 03:36:29
【问题描述】:
给定:
newtype PlayerHandle = PlayerHandle Int deriving (Show)
newtype MinionHandle = MinionHandle Int deriving (Show)
newtype WeaponHandle = WeaponHandle Int deriving (Show)
在下面的代码中,我希望handle 是完全三种类型之一:PlayerHandle、MinionHandle 和WeaponHandle。这可以在 Haskell 中实现吗?
data Effect where
WithEach :: (??? handle) => [handle] -> (handle -> Effect) -> Effect -- Want `handle' to be under closed set of types.
下面的太繁琐了:
data Effect' where
WithEachPlayer :: [PlayerHandle] -> (PlayerHandle -> Effect) -> Effect
WithEachMinion :: [MinionHandle] -> (MinionHandle -> Effect) -> Effect
WithEachWeapon :: [WeaponHandle] -> (WeaponHandle -> Effect) -> Effect
编辑:
Ørjan Johansen 提议使用封闭类型族,这确实让我离我想要的更近了一步。我在使用它们时遇到的问题是我似乎无法编写以下内容:
type family IsHandle h :: Constraint where
IsHandle (PlayerHandle) = ()
IsHandle (MinionHandle) = ()
IsHandle (WeaponHandle) = ()
data Effect where
WithEach :: (IsHandle handle) => [handle] -> (handle -> Effect) -> Effect
enactEffect :: Effect -> IO ()
enactEffect (WithEach handles cont) = forM_ handles $ \handle -> do
print handle -- Eeek! Can't deduce Show, despite all cases being instances of Show.
enactEffect $ cont handle
GHC 在这里抱怨它不能推断出句柄是Show 的一个实例。由于各种原因,我很犹豫通过在WithEach 构造函数中移动Show 约束来解决这个问题。这些包括模块化和可扩展性。像封闭数据族这样的东西会解决这个问题吗(我知道类型族映射不是单射的......即使是封闭的也是这个问题吗?)
【问题讨论】:
-
我觉得这很有趣,你已经得到了我的支持,但我希望你不介意这个问题:为什么不为你的处理程序使用 sum-type - 我相信你有你的理由但是这里的例子似乎对这个基本的解决方案大喊大叫。
-
@Carsten:主要是因为我希望有更好的方法。我以前从未使用过封闭类型系列(我忘记了 GHC 已经支持它们)。在这一点上,我想我可能只使用三个不同的构造函数,当模式匹配它们时,我可以将它们直接传递给
enactEffect :: (Show h) => [h] -> (h -> Effect) -> IO ()。这将使我能够处理比Show更复杂的约束(假设Show是WithEach构造函数的先决条件),包括模块私有约束。 -
只要你不想扩展到其他处理程序 IMO 没有 更好 方式this 似乎更容易(对我来说;))
-
@Carsten:我明白你在说什么。我真的很想强制执行静态类型。我将它用于我的 DSL 炉石模型(效果和能力)。这将排除 sum 类型允许的非法卡片结构。
-
@Thomas Eding:你不能隐藏 sum 类型本身,以排除非法卡片结构吗?
标签: haskell types polymorphism ghc