多态性类型:使用相同类型的同义词表示多种类型

发布于 2025-01-31 07:17:30 字数 1191 浏览 1 评论 0原文

我的目标是给出两种数据类型相同的同义词。 我将我的问题最小化了以下问题:

{-# LANGUAGE KindSignatures, Rank2Types #-}

class Things (h :: * -> *) where

newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a

instance Things Thing1 where
instance Things Thing2 where

type OneOfTwo a = forall h. Things h => h a

foo :: OneOfTwo String -> String
foo (Thing1 name) = name
foo (Thing2 name) = name

我的目标是能够制作类型的同义词OneOftwo代表Thing> Thing> Thing> Thing> Thing> Thing> thite> thits> thite2 thing2 (如果我愿意的话,甚至更多)。但是,当我这样做时,除了foo中的第一个模式外,任何匹配的模式都将被视为多余的:

    Pattern match is redundant
   |
15 | foo (Thing2 name) = name
   |

我知道我可以将其重新编写为以下内容:

newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a

data OneOfTwo a = One (Thing1 a)
                | Two (Thing2 a)

foo :: OneOfTwo String -> String
foo (One (Thing1 name)) = name
foo (Two (Thing2 name)) = name

但是我想避免创建创建新的数据类型OneOftwo

是否有解决方法可以实现这一目标而不创建新的拳击数据类型?

My objective is giving two data types the same synonym.
I've minimized my question to the following question:

{-# LANGUAGE KindSignatures, Rank2Types #-}

class Things (h :: * -> *) where

newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a

instance Things Thing1 where
instance Things Thing2 where

type OneOfTwo a = forall h. Things h => h a

foo :: OneOfTwo String -> String
foo (Thing1 name) = name
foo (Thing2 name) = name

My goal is to be able to make the type synonym oneOfTwo stand for either Thing1 and Thing2 (or even more if I want to). But when I do so, any pattern matching aside from the first one in foo will be seen as redundant:

    Pattern match is redundant
   |
15 | foo (Thing2 name) = name
   |

I'm aware that I can re-write it as the following:

newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a

data OneOfTwo a = One (Thing1 a)
                | Two (Thing2 a)

foo :: OneOfTwo String -> String
foo (One (Thing1 name)) = name
foo (Two (Thing2 name)) = name

But I want to avoid creating a new data type OneOfTwo.

Is there a workaround to make this happen without creating a new boxing data type?

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

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

发布评论

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

评论(2

孤者何惧 2025-02-07 07:17:30

您已经尝试构建存在类型

type OneOfTwoE a = ∃ h. Things h => h a

的意思是“使用Value V的人Oneoftwo a可以确保有一些具体类型构造函数h,它是things的实例,因此v具有类型h a”。但这不是 您写的类型是什么意思;那是通用类型

type OneOfTwoA a = ∀ h. Things h => h a

这意味着对于 act 类型构造函数h v的用户可能会想到,<代码> v 具有类型h a。即单个值v一般具有多种不同类型!

这意味着,尽管在不同的构造函数上匹配,但您实际上可以编写

fooA :: OneOfTwoA String -> String
fooA v = (case v of {Thing1 name -> name})
       ++ (case v of {Thing2 name -> name})

案例匹配的两个将成功!之所以起作用,是因为在每个案例表达式中,v专门针对不同的h实例化,这是可以的,因为类型是通用的。

Haskell实际上没有存在类型。正如您所指出的那样,它确实具有歧视总量类型。它还可以模拟具有普遍量化的数据构造函数的存在类型,可优先用GADT语法表达:

data OneOfTwoG a where
  OneOfTwo :: ∀ h a . Things h => h a -> OneOfTwoG a

这使得在概念上可以将类似于( 此代码不起作用)的东西成为

fooG :: OneOfTwoG String -> String
fooG (OneOfTwo (Thing1 name)) = name
fooG (OneOfTwo (Thing2 name)) = name

可能实际上获得该功能,您需要将案例区别放在课堂实例中,就像

class Things (h :: * -> *) where
  gname :: h a -> a

newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a

instance Things Thing1 where gname (Thing1 n) = n
instance Things Thing2 where gname (Thing2 n) = n
fooG :: OneOfTwoG String -> String
fooG (OneOfTwo ϑ) = gname ϑ

You've tried to build an existential type

type OneOfTwoE a = ∃ h. Things h => h a

meaning “whoever uses a value v of type OneOfTwo a can be sure that there is some concrete type constructor h, which is an instance of Things, such that v has type h a”. But that's not what the type you wrote means; that is a universal type

type OneOfTwoA a = ∀ h. Things h => h a

meaning that for all type constructors h that the user of v might think of, v has type h a. I.e. the single value v has in general multiple different types!

That means you can actually write

fooA :: OneOfTwoA String -> String
fooA v = (case v of {Thing1 name -> name})
       ++ (case v of {Thing2 name -> name})

and both of the case matches will succeed, despite matching on different constructors! This works because in each of the case expressions, v is specialised to a different h instantiation, which is ok because the type is universal.

Haskell doesn't really have existential types. It does, as you've noted, have discriminated sum types. It also can simulated existential types with universally quantified data constructors, preferrably expressed with GADT syntax:

data OneOfTwoG a where
  OneOfTwo :: ∀ h a . Things h => h a -> OneOfTwoG a

which makes it conceptually possible to have something like (this code doesn't work)

fooG :: OneOfTwoG String -> String
fooG (OneOfTwo (Thing1 name)) = name
fooG (OneOfTwo (Thing2 name)) = name

To actually get that functionality, you need to put the case distinction into the class instances, like

class Things (h :: * -> *) where
  gname :: h a -> a

newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a

instance Things Thing1 where gname (Thing1 n) = n
instance Things Thing2 where gname (Thing2 n) = n
fooG :: OneOfTwoG String -> String
fooG (OneOfTwo ϑ) = gname ϑ
余生再见 2025-02-07 07:17:30

出于明显指出的风险,类型类的全部目的是允许您定义可以应用于多种类型的多态性功能,因此在这种情况下您假定要做的是写:

class Things h where
  foo :: h String -> String

newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a

instance Things Thing1 where
  foo (Thing1 name) = name
instance Things Thing2 where
  foo (Thing2 name) = name

也就是说,当您要用单个名称表示多种类型时,要使用的工具是类型类,而不是类型的同义词。

At the risk of pointing out the obvious, the whole purpose of type classes is to allow you to define polymorphic functions that can be applied to multiple types, so what you're supposed to do in this situation is write:

class Things h where
  foo :: h String -> String

newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a

instance Things Thing1 where
  foo (Thing1 name) = name
instance Things Thing2 where
  foo (Thing2 name) = name

That is, when you want to represent multiple types with a single name, the tool you want to use is a type class, not a type synonym.

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