为什么我不能将类型 Int 与类型 a 匹配
Why can't I match type Int with type a
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
这是怎么回事?有没有办法解决这个问题,还是您不能这样做?
Is there a way to work around this or is it just something you can't do?
恐怕这只是“你做不到的事情”。原因很简单。
当你写一个类型签名时(以你的第一个更简单的例子为例)
test :: Int -> a
或者,写成更直白的扩展形式
test :: forall a. Int -> a
你的意思是字面上的意思,“对于所有 a
”,这个函数可以接受一个 Int 和 return 一个 a
类型的值。这很重要,因为调用代码必须相信这个类型签名,因此能够做这样的事情(这不是现实的代码,但想象一下你将 test 2
的结果提供给需要的函数的情况Char
或其他类型之一):
test 2 :: Char
test 2 :: [Int]
test 2 :: (Double, [Char])
等等。很明显,您的函数无法与这些示例中的任何一个一起使用 - 但它 必须 能够与其中的 any 一起使用,如果您给它这个类型签名。很简单,您的代码不符合该类型签名。 (而且也不可能,除非你通过 test x = undefined
来“作弊”。)
不过这应该不是问题 - 编译器只是在保护您免于出错,因为我相信您意识到您的代码无法满足此类型签名。以你的“真实”为例:
myFunc :: EnumType -> MyType Enum1 -> MyType any
虽然这会产生编译错误,但函数中的代码可能是正确的,问题出在类型签名上。如果将其替换为
myFunc :: EnumType -> MyType Enum1 -> MyType Enum1
然后它将编译(除非有任何进一步的错误,我没有检查过),并且大概会做你想做的事。看起来您实际上并不希望能够调用 myFunc
并让它生成一个 MyType Int
。 (如果你有机会这样做,我建议你问一个单独的问题,在这里详细说明你实际需要什么。)
对于你要写的GADT函数,标准的技术是使用单例。问题是 EnumType
类型的值是值级别的东西,但你想通知类型系统一些事情。因此,您需要一种方法将 types of kind EnumType
与 values of type EnumType
(它本身有种类 Type
)。这是不可能的,所以我们作弊:我们将种类 EnumType
的类型 x
与新类型 SEnumType x
的值连接起来,这样该值唯一确定 x
。外观如下:
data SEnumType a where
SEnum1 :: SEnumType Enum1
SEnum2 :: SEnumType Enum2
SEnum3 :: SEnumType Enum3
myFunc :: SEnumType a -> MyType Enum1 -> MyType a
myFunc SEnum1 t = t
myFunc SEnum2 _ = Type2 "hi"
myFunc SEnum3 _ = Type3 True
现在 return 类型 MyType a
中的 a
不是凭空捏造的;它被限制为等于来自 SEnumType
的传入 a
,并且 SEnumType
所在的模式匹配让您观察 a
是否为 Enum1
,Enum2
或 Enum3
.
如前所述,您的签名表示通用类型
myFunc :: ∀ a . EnumType -> MyType 'Enum1 -> MyType a
而你实际上想表达的是存在类型
myFunc :: EnumType -> MyType 'Enum1 -> (∃ a . MyType a)
Haskell 没有类似的功能,但它确实有一些方法可以实现基本相同的功能。
GADT 和 ExistentialTypes
扩展都允许表达存在性,但您需要为它们定义一个单独的类型。
data MyDynType where
MyDynWrap :: MyType a -> MyDynType
myFunc :: EnumType -> MyType 'Enum1 -> MyDynType
myFunc Enum1 t = MyDynWrap t
myFunc Enum2 _ = MyDynWrap $ Type2 "hi"
myFunc Enum3 _ = MyDynWrap $ Type3 True
也许您甚至不需要单独的类型,但可以简单地将 MyType
修改为“动态”。
data MyType = Type1 Int | Type2 String | Type3 Bool
myFunc :: EnumType -> MyType -> MyType
myFunc Enum1 (Type1 i) = Type1 i
myFunc Enum2 _ = Type2 "hi"
myFunc Enum3 _ = Type3 True
existentials 可以在现场匿名模拟,方法是打开一层连续传递样式,然后通过 RankNTypes 扩展使用双通用量词。
{-# LANGUAGE RankNTypes #-}
data MyType (a :: EnumType) where ... -- as original
myFunc :: EnumType -> MyType 'Enum1 -> (∀ a . MyType a -> r) -> r
myFunc Enum1 t q = q t
myFunc Enum2 _ q = q (Type2 "hi")
myFunc Enum3 _ q = q (Type3 True)
你要写的GADT函数,标准的技术是使用单例。问题是 EnumType 类型的值是 website...
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
这是怎么回事?有没有办法解决这个问题,还是您不能这样做?
Is there a way to work around this or is it just something you can't do?
恐怕这只是“你做不到的事情”。原因很简单。
当你写一个类型签名时(以你的第一个更简单的例子为例)
test :: Int -> a
或者,写成更直白的扩展形式
test :: forall a. Int -> a
你的意思是字面上的意思,“对于所有 a
”,这个函数可以接受一个 Int 和 return 一个 a
类型的值。这很重要,因为调用代码必须相信这个类型签名,因此能够做这样的事情(这不是现实的代码,但想象一下你将 test 2
的结果提供给需要的函数的情况Char
或其他类型之一):
test 2 :: Char
test 2 :: [Int]
test 2 :: (Double, [Char])
等等。很明显,您的函数无法与这些示例中的任何一个一起使用 - 但它 必须 能够与其中的 any 一起使用,如果您给它这个类型签名。很简单,您的代码不符合该类型签名。 (而且也不可能,除非你通过 test x = undefined
来“作弊”。)
不过这应该不是问题 - 编译器只是在保护您免于出错,因为我相信您意识到您的代码无法满足此类型签名。以你的“真实”为例:
myFunc :: EnumType -> MyType Enum1 -> MyType any
虽然这会产生编译错误,但函数中的代码可能是正确的,问题出在类型签名上。如果将其替换为
myFunc :: EnumType -> MyType Enum1 -> MyType Enum1
然后它将编译(除非有任何进一步的错误,我没有检查过),并且大概会做你想做的事。看起来您实际上并不希望能够调用 myFunc
并让它生成一个 MyType Int
。 (如果你有机会这样做,我建议你问一个单独的问题,在这里详细说明你实际需要什么。)
对于你要写的GADT函数,标准的技术是使用单例。问题是 EnumType
类型的值是值级别的东西,但你想通知类型系统一些事情。因此,您需要一种方法将 types of kind EnumType
与 values of type EnumType
(它本身有种类 Type
)。这是不可能的,所以我们作弊:我们将种类 EnumType
的类型 x
与新类型 SEnumType x
的值连接起来,这样该值唯一确定 x
。外观如下:
data SEnumType a where
SEnum1 :: SEnumType Enum1
SEnum2 :: SEnumType Enum2
SEnum3 :: SEnumType Enum3
myFunc :: SEnumType a -> MyType Enum1 -> MyType a
myFunc SEnum1 t = t
myFunc SEnum2 _ = Type2 "hi"
myFunc SEnum3 _ = Type3 True
现在 return 类型 MyType a
中的 a
不是凭空捏造的;它被限制为等于来自 SEnumType
的传入 a
,并且 SEnumType
所在的模式匹配让您观察 a
是否为 Enum1
,Enum2
或 Enum3
.
如前所述,您的签名表示通用类型
myFunc :: ∀ a . EnumType -> MyType 'Enum1 -> MyType a
而你实际上想表达的是存在类型
myFunc :: EnumType -> MyType 'Enum1 -> (∃ a . MyType a)
Haskell 没有类似的功能,但它确实有一些方法可以实现基本相同的功能。
GADT 和
ExistentialTypes
扩展都允许表达存在性,但您需要为它们定义一个单独的类型。data MyDynType where MyDynWrap :: MyType a -> MyDynType myFunc :: EnumType -> MyType 'Enum1 -> MyDynType myFunc Enum1 t = MyDynWrap t myFunc Enum2 _ = MyDynWrap $ Type2 "hi" myFunc Enum3 _ = MyDynWrap $ Type3 True
也许您甚至不需要单独的类型,但可以简单地将
MyType
修改为“动态”。data MyType = Type1 Int | Type2 String | Type3 Bool myFunc :: EnumType -> MyType -> MyType myFunc Enum1 (Type1 i) = Type1 i myFunc Enum2 _ = Type2 "hi" myFunc Enum3 _ = Type3 True
existentials 可以在现场匿名模拟,方法是打开一层连续传递样式,然后通过 RankNTypes 扩展使用双通用量词。
{-# LANGUAGE RankNTypes #-} data MyType (a :: EnumType) where ... -- as original myFunc :: EnumType -> MyType 'Enum1 -> (∀ a . MyType a -> r) -> r myFunc Enum1 t q = q t myFunc Enum2 _ q = q (Type2 "hi") myFunc Enum3 _ q = q (Type3 True)
你要写的GADT函数,标准的技术是使用单例。问题是 EnumType 类型的值是 website...