haskell - 一种将 Haskell 的 Either 类型推广到任意多种类型的方法?

标签 haskell types either parametric-polymorphism

我正在创建一个回合制游戏。我想定义一个数据类型,从许多可能的类型中编码一种类型。这是一个激励的例子:
我定义了一个 Turn type 使用 GADT,所以 Turn a 的每个值的类型说明了它的值(value)。

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)
这行得通,但是最好用一种类型来表达它 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 - 一种将 Haskell 的 Either 类型推广到任意多种类型的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67952859/

相关文章:

typescript - 如何在 TypeScript 中手动柯里化(Currying)一个函数?

r - 使用 cbind 和 or 参数组合两个向量

exception-handling - 在 Clojure 中处理纯度错误?

scala - 如何使用任一个产生所有错误(失败)

Haskell:为什么多行 let 表达式是语法错误?

parsing - Elm 是否有相当于 Haskell 的 "Read"

haskell - 使用 Network.Browser 创建 cookie

mysql - 将无符号字符数组插入数据库时​​出现问题

Java不允许调用泛型类型的递归方法?

haskell - 在 Haskell 中创建以下列表 [1, 2, [3, 4], [3, [4, 5]]]