可以使用与Haskell中使用GADT定义的数据类型的模式匹配

发布于 2025-01-23 20:01:50 字数 1571 浏览 0 评论 0原文

我正在尝试定义复杂 datatype,我希望构造函数能够以任何数字为例,因此我想使用通用类型,只要它确实实现了num实例

我是我们的gadt s,因为我理解datatypecontexts语言扩展是“ misfeature “即使我认为在这种情况下这是有用的...

无论如何,这是我的代码:

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)

据我所知,这是由于返回所致类型可以解释为不同... 因此,我尝试删除类型的定义,并让GHC对他的魔术进行类型的魔术,但事实证明类型签名是相同的...

有人可以向我解释一下这里有什么问题吗?

I'm trying to define a Complex datatype, and I want the constructors to be able to take any number as instance, so I would like to use a generic type as long as it does implement a Num instance

I'm usind GADTs in order to do so since to my understanding the DataTypeContexts language extension was a "misfeature" even if I think that would have been useful in this case...

In any case this is my code:

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

here the real implementation gives the following error:

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)

which to my understanding is due to the return type do be interpreted as different...
so I tried removing the type definition and letting ghc do his magic with the type but turns out the type signature was the same...

can anyone please explain to me what is wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

平安喜乐 2025-01-30 20:01:50

问题是,这些定义允许您选择不同类型时((1)构建复杂值以及(2)应用real函数时。这两种情况并非以任何方式相互连接,因此没有任何迫使它们之间的类型相同。例如:

c :: Complex
c = Real (42 :: Int)

d :: Double
d = real c

d的定义要求real函数返回double,但没有double包装在c的内部,只有int


至于解决方案有两个可能的方法:(1)在这两个点之间建立连接,强迫类型相同,(2)允许内部类型转换为任何其他数字类型。

要在两个点之间建立类型级的连接,我们需要使用两个点上存在的类型。那是什么类型?显然,这就是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

请注意,我实际上并不需要Gadts。


要允许类型转换,您需要需要num类型的其他类型类。类num可以从任何积分类型中转换,但是没有办法将转换为 任何这样的类型t很有意义: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。

Problem is, these definitions allow you to choose different types when (1) constructing a Complex value and when (2) applying the real function. These two situations are not connected to each other in any way, so there is nothing to force the type to be the same between them. For example:

c :: Complex
c = Real (42 :: Int)

d :: Double
d = real c

The definition of d requires the real function to return a Double, but there is no Double wrapped inside of c, there is only Int.


As for solutions, there are two possible ones: (1) establish a connection between these two points, forcing the type to be the same, and (2) allow the type inside to be converted to any other numeric type.

To establish a type-level connection between two points, we need to use a type that is present at both points. What type would that be? Quite obviously, that's the type of c. So we need to make the type of c somehow convey what's wrapped inside it:

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

Note that I don't actually need GADTs for this.


To allow type conversion, you'll need to require another type class for the num type. The class Num has a way to convert from any integral type, but there is no way to convert to any such type, because it doesn't make sense: 3.1415 can't be meaningfully converted to an integral type.

So you'll have to come up with your own way to convert, and implement it for all allowed types too:

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
...

Just to be clear, I consider the second option quite insane. I only provided it for illustration. Don't do it. Go with option 1.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文