2012-12-17 49 views
2

那么,什么将是很好的,如果你可以做类似下面的(不一定是这种格式,只是一般的想法):嵌套/分数据类型

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

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

所以isSub苏巴将报告真(而不是一个错误。) 在你可能会做这样的事情的时刻:

data Super = SubA | SubB | SuperB 

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

它不可怕或任何东西,但它不能很好地扩展(如如果子最多时SubZ这将是可怕的笨重),它不允许你将Sub类型添加到它们自己的type-cl中屁股。为了避免这个问题,你可以用子:

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

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

但现在你必须确保你的包裹替补使用他们作为一个超级......再也不可怕;只是没有真正表达我想要的语义(即Super可以是任何Sub或SuperB)。第一个(合法的)例子是“Super可以是SubA ...”,第二个是“Super可以是SuperA,需要Sub ...”

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

P.S.从技术上讲,当我开始思考如何在Haskell中表示Scheme的数字塔时...但我更感兴趣的是表示“Type1可以是任何Type2加x,y ...”的更一般问题。

+0

基于答案波纹管他们的意见:它看起来像,出口最上面的类型与一些辅助功能,以从下类型创建该类型是要对这个的最佳方式。 –

+0

尽管看起来您需要为您的输入类型设置类型类(所以如果是SubA Int,SubB Float和SuperB String;那么您将需要一个用于Int和Float的SubInput类实例和一个SuperInput类实例对于Int,Float和String)。至少,这些类可以是空的。但是,你可以有mkSub :: SubInput a => a - >超级 –

+0

那么,你的问题是什么? :) –

回答

0

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

,如果你使用一些模板哈斯克尔这将是罚款。我想看看derive工具的makeIs日常指导。

但现在你必须做确保包裹你的潜艇使用他们作为超级

不,类型系统会告诉你,如果你忘了。举例来说,如果你有

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

然后在其中使用Sub而是奢望一段Super会被抓住的任何背景。我猜你已经知道你的意思是什么?

+0

不,这就是我的意思。类型系统会告诉你,但你仍然必须这样做(只是一个措辞的差异。)这是一种令人讨厌的,你必须在香草哈斯克尔包装Sub类型,但我想如果我这样做更多我不会介意使用模板Haskell ... –

+0

您可以使用'mkSuper'方法创建'CanBeSuper'类。例如''实例CanBeSuper超级其中mkSuper = id'和'实例CanBeSuper Sub其中mkSuper = Sub'。然后你需要做的就是将'mkSuper'应用于参数 - where子句或者'ViewPatterns'扩展将是最好的。这些函数会看起来像'someFunc(mkSuper - > s)= ...'。 –

+0

我通常对使用扩展犹豫不决,但对于类型类的想法很有趣。我打算玩这个想法一会儿,看看当我去3或4个嵌套子类型时会发生什么。 –

0

你不能有像

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

任何假设上面的语法是允许的,那么问题就给出的值构造SubA你不能告诉它的类型是SubSuper。这就是为什么你需要在构造函数中包装你的类型。

对于你的第二个例子,你所做的事情应该是默认的做法,但你可以做一些简单的操作,尽管我不建议这样做,因为使用show会慢很多。你可以尝试一些类似于此的其他黑客攻击。

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)。

+0

从技术上讲,SubA既是Sub也是Super。但是,这使我们更多地进入了一个基于集合的类型系统... 使用类型类似的1个问题将是从深度2到深度3结束了很多额外的工作(包装isn尽管如此,还是好多了。) –

+0

技术上,haskell语义不允许SubA属于这两种类型。你可以检查使用相同的构造函数声明两种数据类型。我没有看到任何包装问题。如果您害怕进行更深层次的修改,您可以使用'lens'。 – Satvik

+0

我知道,我只是说你需要一个基于集合的类型系统来表达SubA作为Sub和Super(Haskell没有)。包装也没什么问题,这是我认为的最好的做法。我只是好奇其他方式来做到这一点(例如,我正在努力想出一个类型级的实现;所以感谢拼写出来......仍想着如何很好地扩展它,而不必结束每种类型和类型组合) –

0

您可能想使用镜头(Control.Lens)库及其实例的Data.Data和Data.Typleable。镜头是尝试在列表,元组和所有其他数据类型中解决这些类型的多级问题。

>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,那么它就是真的。应该可以将其限制为仅限于您的用例。

镜头库的优点是现在我可以添加另一个层到类型层次结构。

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

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

>isSub tmp3 
True 
>isSub tmp4 
True