我是 haskell ·努布。
我在这里要做的是一个过于简单的例子:
test :: Int -> a
test i = i -- Couldn't match expected type ‘a’ with actual type ‘Int’. ‘a’ is a rigid type variable bound by ...
我不太明白为什么这不起作用,我的意思是,Int
肯定包含在a
类型的东西中。
我真正想实现的是:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
data EnumType = Enum1 | Enum2 | Enum3
data MyType (a :: EnumType) where
Type1 :: Int -> MyType 'Enum1
Type2 :: String -> MyType 'Enum2
Type3 :: Bool -> MyType 'Enum3
myFunc :: EnumType -> MyType 'Enum1 -> MyType any
myFunc Enum1 t = t -- Can't match type `any` with `Enum1`. any is a rigid type variable bound by ...
myFunc Enum2 _ = Type2 "hi"
myFunc Enum3 _ = Type3 True
这是怎么回事?有没有办法解决这个问题,或者只是一些你不能做的事情?
4条答案
按热度按时间ki0zmccv1#
对于你想写的GADT函数,标准的技术是使用单例。问题是
EnumType
类型的值是值级别的东西,但是你想通知类型系统一些东西。所以你需要一种方法来连接 kindEnumType
的 types 和 typeEnumType
的 values(它本身有kindType
)。这是不可能的,所以我们作弊:我们将x1m5 n1,类型的x
与一个新类型SEnumType x
的值连接起来,使得该值唯一地确定x
。现在返回类型
MyType a
中的a
不是凭空构造的;它被约束为等于来自SEnumType
的传入a
,模式匹配将使您能够观察a
是Enum1
、Enum2
还是Enum3
。wgmfuz8q2#
有没有办法解决这个问题,或者只是一些你不能做的事情?
恐怕这只是“你做不到的事”,原因很简单。
当你编写一个类型签名时,比如(举第一个更简单的例子)
或者,把它写得更字面,更扩展的形式
你是说,从字面上看,“对于所有
a
“,这个函数可以接受一个Int,并返回一个a
类型的值。(这不是实际的代码,但是想象一下,您将test 2
的结果提供给一个需要Char
或其他类型的函数):等等。很明显,你的函数不能处理这些例子中的任何一个--但是如果你给予它这个类型签名,它 * 必须 * 能够处理 * 其中的任何一个 *。很简单,你的代码不适合这个类型签名。(任何一个也不适合,除非你“作弊”,比如
test x = undefined
。)不过这应该不是问题-编译器只是保护你不出错,因为我相信你会意识到你的代码不能满足这个类型签名。举个“真实”的例子:
但函数中的代码很可能是正确的,问题出在类型签名上。如果将其替换为
然后它将进行编译(除非出现其他错误,我没有检查它),并可能执行您想要的操作。您实际上并不希望能够调用
myFunc
并让它生成一个MyType Int
。(如果您希望这样做,我建议您提出一个单独的问题,详细说明您在这里实际需要什么。)hrirmatl3#
如前所述,您的签名表示一个通用类型
而你实际上想表达的是一种存在主义类型
Haskell没有这样的特性,但它确实有一些方法来实现本质上相同的事情。
ExistentialTypes
扩展都允许表达存在性,但是需要为它们定义一个单独的类型。1.也许您甚至不需要一个单独的类型,而只需将
MyType
修改为“动态”即可。1.通过展开一层延续传递风格,然后通过RankNTypes扩展使用对偶全称量词,可以在现场匿名地模拟存在体。
3qpi33ja4#
你想写的GADT函数,标准的技术是使用单例。问题是类型EnumType的值是...