不能对使用 haskell 中的 GADT 定义的数据类型使用模式匹配
can't use pattern matching with datatypes defined using GADT in haskell
我正在尝试定义一个 Complex
数据类型,我希望构造函数能够将任何数字作为实例,所以我想使用泛型,只要它实现了Num
实例
我使用 GADT
s 是为了这样做,因为据我了解 DataTypeContexts
语言扩展是一个“错误特征”,即使我认为这在这种情况下会很有用...
无论如何这是我的代码:
data Complex where
Real :: ( Num num, Show num ) => num -> Complex
Imaginary :: ( Num num, Show num ) => num -> Complex
Complex :: ( Num num, Show num ) => num -> num -> Complex
real :: ( Num num, Show num ) => Complex -> num
real (Real r) = r
real (Imaginary _i ) = 0
real (Complex r _i ) = r
这里的 real
实现给出了以下错误:
Couldn't match expected type ‘num’ with actual type ‘num1’
‘num1’ is a rigid type variable bound by
a pattern with constructor:
Real :: forall num. (Num num, Show num) => num -> Complex,
in an equation for ‘real’
at <...>/Complex.hs:29:7-12
‘num’ is a rigid type variable bound by
the type signature for:
real :: forall num. (Num num, Show num) => Complex -> num
at <...>/Complex.hs:28:1-47
• In the expression: r
In an equation for ‘real’: real (Real r) = r
• Relevant bindings include
r :: num1
(bound at <...>/Complex.hs:29:12)
real :: Complex -> num
(bound at <...>/Complex.hs:29:1)
据我了解,这是由于 return 类型确实被解释为不同...
所以我尝试删除类型定义并让 ghc
对类型施展魔法,但结果类型签名是相同的...
任何人都可以向我解释这里有什么问题吗?
问题是,这些定义允许您在 (1) 构造 Complex
值和 (2) 应用 real
函数时选择不同的类型。这两种情况之间没有任何联系,因此没有什么可以强制它们之间的类型相同。例如:
c :: Complex
c = Real (42 :: Int)
d :: Double
d = real c
d
的定义需要real
函数来return一个Double
,但是[=19=里面没有包裹Double
], 只有 Int
.
至于解决方案,有两种可能:(1)在这两点之间建立联系,强制类型相同,(2)允许将里面的类型转换为任何其他数字类型.
要在两点之间建立 type-level 连接,我们需要使用在两点都存在的类型。那会是什么类型的?很明显,这就是 c
的类型。所以我们需要让 c
的类型以某种方式传达它包含的内容:
data Complex num = Real num | Imaginary num | Complex num num
real :: Complex num -> num
real = ...
-- Usage:
c :: Complex Int
c = Real 42
d :: Int
d = real c
请注意,我实际上不需要 GADT。
要允许类型转换,您需要为 num
类型要求另一种类型 class。 class Num
有办法将 从 转换为任何整数类型,但是没有办法将 转换为 任何这样的类型,因为它没有意义:3.1415 不能有意义地转换为整数类型。
所以你必须想出你自己的转换方式,并为所有允许的类型实现它:
class Convert a where
toNum :: Num n => a -> n
data Complex where
Real :: ( Num num, Show num, Convert num ) => num -> Complex
...
real :: Num num => Complex -> num
real (Real r) = toNum r
...
明确一点,我认为第二种选择非常疯狂。我提供它只是为了说明。不要这样做。选择选项 1。
我正在尝试定义一个 Complex
数据类型,我希望构造函数能够将任何数字作为实例,所以我想使用泛型,只要它实现了Num
实例
我使用 GADT
s 是为了这样做,因为据我了解 DataTypeContexts
语言扩展是一个“错误特征”,即使我认为这在这种情况下会很有用...
无论如何这是我的代码:
data Complex where
Real :: ( Num num, Show num ) => num -> Complex
Imaginary :: ( Num num, Show num ) => num -> Complex
Complex :: ( Num num, Show num ) => num -> num -> Complex
real :: ( Num num, Show num ) => Complex -> num
real (Real r) = r
real (Imaginary _i ) = 0
real (Complex r _i ) = r
这里的 real
实现给出了以下错误:
Couldn't match expected type ‘num’ with actual type ‘num1’
‘num1’ is a rigid type variable bound by
a pattern with constructor:
Real :: forall num. (Num num, Show num) => num -> Complex,
in an equation for ‘real’
at <...>/Complex.hs:29:7-12
‘num’ is a rigid type variable bound by
the type signature for:
real :: forall num. (Num num, Show num) => Complex -> num
at <...>/Complex.hs:28:1-47
• In the expression: r
In an equation for ‘real’: real (Real r) = r
• Relevant bindings include
r :: num1
(bound at <...>/Complex.hs:29:12)
real :: Complex -> num
(bound at <...>/Complex.hs:29:1)
据我了解,这是由于 return 类型确实被解释为不同...
所以我尝试删除类型定义并让 ghc
对类型施展魔法,但结果类型签名是相同的...
任何人都可以向我解释这里有什么问题吗?
问题是,这些定义允许您在 (1) 构造 Complex
值和 (2) 应用 real
函数时选择不同的类型。这两种情况之间没有任何联系,因此没有什么可以强制它们之间的类型相同。例如:
c :: Complex
c = Real (42 :: Int)
d :: Double
d = real c
d
的定义需要real
函数来return一个Double
,但是[=19=里面没有包裹Double
], 只有 Int
.
至于解决方案,有两种可能:(1)在这两点之间建立联系,强制类型相同,(2)允许将里面的类型转换为任何其他数字类型.
要在两点之间建立 type-level 连接,我们需要使用在两点都存在的类型。那会是什么类型的?很明显,这就是 c
的类型。所以我们需要让 c
的类型以某种方式传达它包含的内容:
data Complex num = Real num | Imaginary num | Complex num num
real :: Complex num -> num
real = ...
-- Usage:
c :: Complex Int
c = Real 42
d :: Int
d = real c
请注意,我实际上不需要 GADT。
要允许类型转换,您需要为 num
类型要求另一种类型 class。 class Num
有办法将 从 转换为任何整数类型,但是没有办法将 转换为 任何这样的类型,因为它没有意义:3.1415 不能有意义地转换为整数类型。
所以你必须想出你自己的转换方式,并为所有允许的类型实现它:
class Convert a where
toNum :: Num n => a -> n
data Complex where
Real :: ( Num num, Show num, Convert num ) => num -> Complex
...
real :: Num num => Complex -> num
real (Real r) = toNum r
...
明确一点,我认为第二种选择非常疯狂。我提供它只是为了说明。不要这样做。选择选项 1。