【问题标题】:Nested/Sub data types in haskellhaskell 中的嵌套/子数据类型
【发布时间】:2012-12-17 05:01:12
【问题描述】:

如果您可以执行以下操作(不一定使用这种格式,只是一般的想法),那就太好了:

data Sub = SubA | SubB
data Super = Sub | SuperB

isSub :: Super -> Bool
isSub Sub = True
isSub _ = False

所以 isSub SubA 会报告 True(而不是错误。) 目前您可能会执行以下操作:

data Super = SubA | SubB | SuperB

isSub :: Super -> Bool
isSub SubA = True
isSub SubB = True
isSub _ = False

它并不可怕或任何东西,但它不能很好地扩展(就像 Sub 到 SubZ 时这将非常笨重)并且它不允许您将 Sub 类型添加到它们自己的类型类中。为了避免这个问题,你可以包装 Sub:

data Sub = SubA | SubB
data Super = SuperA Sub | SuperB

isSub :: Super -> Bool
isSub (SuperA _) = True
isSub _ = False

但是现在您必须确保将您的 Subs 包装起来以将它们用作超级...再次不可怕;只是并没有真正表达出我想要的语义(即 Super 可以是任何 Sub 或 SuperB)。第一个(合法)示例是“Super 可以是 SubA...”,第二个是“Super 可以是采用 Sub...的 SuperA”

EDTI:更改一些名称以避免与音乐内容混为一谈。

附:从技术上讲,这开始于我考虑如何在 Haskell 中表示 Scheme 的数字塔......但我真的对表示“Type1 可以是 Type2 中的任何一个加上 x,y,......)的更一般的问题更感兴趣

【问题讨论】:

  • 基于下面的答案和他们的 cmets:看起来,使用一些辅助函数导出最顶层的类型以从较低的类型创建该类型是解决此问题的最佳方法。
  • 虽然看起来你需要为你的输入类型提供类型类(所以如果 SubA Int、SubB Float 和 SuperB String;那么你需要一个 Int 的 SubInput 类实例和 Float 以及 Int、Float 和 String 的 SuperInput 类实例。)至少,这些类可以是空的。但是你可以有 mkSub :: SubInput a => a -> Super
  • 那么,您的问题是什么? :)
  • 没有了。我得到了我想要的反馈......现在很难弄清楚2个答案中的哪一个更像是一个答案......

标签: haskell abstract-data-type


【解决方案1】:

它并不可怕或任何东西,但它不能很好地扩展

如果你使用一些 Template Haskell 就好了。我会查看derive 工具的makeIs 例程以获得指导。

但现在你必须确保包装你的 Subs 以将它们用作 Super

不,如果您忘记了,类型系统会告诉您。例如,如果您有

data Super = Sub Sub | Super
data Sub = SubA | SubB

然后,您使用Sub 但期望Super 的任何上下文都将被捕获。我猜你已经知道了,所以你的意思是别的吗?

【讨论】:

  • 不,我就是这个意思。类型系统会告诉你,但你仍然必须这样做(只是措辞不同。)你必须将 Sub 类型包装在 vanilla Haskell 中,这有点烦人,但我想如果我做的更多,我不会介意使用 Haskell 模板...
  • 您可以使用mkSuper 方法创建一个CanBeSuper 类。例如instance CanBeSuper Super where mkSuper = idinstance CanBeSuper Sub where mkSuper = Sub。然后您需要做的就是将mkSuper 应用于参数——最好使用where 子句或ViewPatterns 扩展。然后这些函数看起来像someFunc (mkSuper -> s) = ...
  • 我通常对使用扩展犹豫不决,但类型类的想法很有趣。我将尝试一下这个想法,看看当我使用 3 或 4 个嵌套子类型时会发生什么。
  • 我怀疑类解决方案在这种情况下会变得笨拙(但我并不是要阻止您进行实验)。这将是最干净/最有用的情况下,你有很多调用站点和许多不同的输入类型的功能很少。
【解决方案2】:

你不能拥有类似的东西

data Sub = SubA | SubB
data Super = Sub | SuperB

假设上面的语法是允许的,那么问题是给定值构造函数SubA你无法分辨它的类型是Sub还是Super。这就是为什么您需要将您的类型包装在构造函数中。

对于你的第二个例子,你正在做的方式应该是默认的方式,但你可以做一个 hack 来使它更容易,虽然我不建议这样做,因为使用 show 慢得多。您可以尝试其他类似的 hack。

import Data.List
data Super = SubA | SubB | SuperB deriving Show
isSub :: Super -> Bool
isSub m = isPrefixOf "Sub" (show m)

如果你真的想要上面那样的东西,最好像你一样定义函数。使用 TH 可能会为您节省一些时间。

你的第三种情况是我真正建议做的。由于我上面所说的原因,您需要有一个像 SuperA 这样的包装构造函数。这就是为什么你不能完全确定 type1 是 type2 加上 x,y,z 的原因。您可以拥有的最接近的方法是将元素包装在构造函数中或使用类型类。

data Sub = SubA | SubB
data Sup = SuperA | SuperB

class Super a where
  isSub :: a -> Bool
  isSub _ = True

instance Super Sup where
    isSub _ = False

instance Super Sub

data SupSup = SuperSuperA | SuperSuperB

class SuperSuper a where
    isSuper :: a -> Bool
    isSuper _ = True


instance SuperSuper SupSup where
    isSuper _ = False

instance SuperSuper Sup
instance SuperSuper Sub

你可以在这里想到Super(这是一个类型类而不是一个类型)包含Sub和一些额外的东西(Sup)。

【讨论】:

  • 从技术上讲,SubA 既是 Sub 又是 Super。但这让我们更多地进入了一个基于集合的类型系统......使用这样的类型类的一个问题是从 2 的深度到 3 的深度最终需要做很多额外的工作(包装不是不过好多了。)
  • 从技术上讲,haskell 语义不允许 SubA 同时属于这两种类型。您可以检查使用相同构造函数声明两种数据类型。我认为包装没有任何问题。如果你害怕更深层次的修改,你可以使用lenses
  • 我知道,我只是说你需要一个基于集合的类型系统来将 SubA 表示为 Sub 和 Super(Haskell 没有。)包装也没有任何问题,这是我认为最好的方法。我只是对其他方法感到好奇(例如,我一直在努力想出一个类型类的实现;所以感谢您详细说明...仍在考虑如何很好地扩展它而不必最终做类型和类型类的每种组合。)
  • @TomCarstens 我已将我的 typeclass 示例扩展到其他级别。但我本人不喜欢这种解决方案,因为它不允许您对类型进行分组。所以你应该更好地使用包装器。
【解决方案3】:

您可能希望研究使用镜头 (Control.Lens) 库及其 Data.Data 和 Data.Typleable 实例。 Lens 试图解决这些类型的列表、元组和所有其他数据类型中的多层次问题。

>data Sub =  SubA | SubB deriving (Show, Data, Typeable, Eq)
>data Super =  SuperA Sub | SuperB deriving (Show, Data, Typeable, Eq)

-- A little bit of a hack, there is probably a better way of doing this
>isSub' :: Sub -> Bool
>isSub' x = typeOf x == typeOf SubA

>tmp1 = SuperA SubA
>tmp2 = SuperA SubB

>isSub x = anyOf biplate (isSub') x

>isSub tmp1
True
>issub tmp2
True

isSub 实在是太笼统了,它会检查所提供数据类型的任何子项是否属于 Sub 类型。因此,如果您有一棵树,并且树中有一个 Sub,那么它就是 True。但是,应该可以将其限制为仅用于您的用例。

Lens 库的优点是现在我可以在类型层次结构中添加另一层。

>data SuperSuper =  SuperSuperA Super | SuperSuperB | SuperSuperC Sub deriving (Show,Data,Typeable)

>tmp3 = SuperSuperA (SuperA SubA)
>tmp4 = SuperSuperC SubB

>isSub tmp3
True
>isSub tmp4
True

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-26
    • 1970-01-01
    • 2013-12-04
    • 2015-09-08
    • 1970-01-01
    • 2018-11-29
    相关资源
    最近更新 更多