一种将Haskell的任何类型概括为任意多种类型的方法?

我正在创建一个回合制游戏。我想定义一个数据类型,从许多可能的类型中编码一种类型。这是一个励志的例子:

我已经Turn使用 GADT定义了一个类型,所以每个值的类型都Turn a说明了它的值。

data Travel
data Attack
data Flee
data Quit

data Turn a where
   Travel :: Location -> Turn Travel
   Attack :: Int -> Turn Attack
   Flee :: Turn Flee
   Quit :: Turn Quit

现在我可以写出这样的类型decideTravel :: GameState -> Turn Travel,非常有表​​现力而且很好。

当我想返回多种可能的转弯类型之一时,就会出现问题。我想编写类似于以下的函数:

-- OneOf taking two types
decideFightingTurn :: GameState -> OneOf (Turn Attack) (Turn Flee)
-- OneOf takes three types
decideTurn :: GameState -> OneOf (Turn Attack) (Turn Travel) (Turn Quit)

OneOf数据类型带有“多种可能类型中的一种类型”的概念。问题在于定义此数据类型,我需要以某种方式处理类型级别的类型列表。

到目前为止,我有两个低于标准的解决方案:

解决方案 1:创建包装总和类型

只需为每个Turn a构造函数创建一个具有构造函数的新类型:

data AnyTurn
   = TravelTurn (Turn Travel)
   | TravelAttack (Turn Attack)
   | TravelFlee (Turn Flee)
   | TravelQuit (Turn Quit)

这并没有以我想要的方式帮助我。现在我必须AnyTurn对无效输入类型的所有情况进行模式匹配并考虑在内。此外,类型级别信息被AnyTurn类型掩盖了,因为它无法指示在类型级别实际上哪些特定转弯是可能的。

解决方案 2:为不同数量的类型创建“Either”类型

这个解决方案给了我我想要的类型级别,但使用起来很麻烦。基本上Either为任意数量的组合创建一个-like 类型,如下所示:

data OneOf2 a b
   = OneOf2A a
   | OneOf2B b

data OneOf3 a b c
   = OneOf3A a
   | OneOf3B b
   | OneOf3C c

-- etc, for as many as are needed.

这在类型级别传达了我想要的内容,因此我现在可以将前面的示例编写为:

decideFightingTurn :: GameState -> OneOf2 (Turn Travel) (Turn Flee)
decideTurn :: GameState -> OneOf3 (Turn Attack) (Turn Travel) (Turn Quit)

这是有效的,但是只用一种 type 来表达它会很好OneOf。有没有办法OneOf在 Haskell 中编写通用类型?

回答

像这样,我猜:

data OneOf as where
    ThisOne :: a -> OneOf (a : as)
    Later :: OneOf as -> OneOf (a : as)

然后你可以写,例如:

decideFightingTurn :: GameState -> OneOf [Turn Travel, Turn Flee]

您可能还想:

type family Map f xs where
    Map f '[] = '[]
    Map f (x:xs) = f x : Map f xs

通过这种方式,您可以编写如下内容:

decideFightingTurn :: GameState -> OneOf (Map Turn [Travel, Flee])

或者,Map如果您认为自己会一直这样做,则可以将其构建到 GADT 中:

data OneOfF f as where
    ThisOneF :: f a -> OneOfF f (a : as)
    LaterF :: OneOfF f as -> OneOfF f (a : as)

进而:

decideFightingTurn :: GameState -> OneOfF Turn [Travel, Flee]

如果这成为一个问题,可以采取各种措施来提高效率;我会尝试的第一件事是使用二进制索引而不是这里所示的一元索引。


以上是一种将Haskell的任何类型概括为任意多种类型的方法?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>