Haskell新手的类型

时间:2011-01-12 01:01:46

标签: haskell

我对Haskell完全不熟悉(更常见的是函数式编程),所以请原谅我这是非常基本的东西。为了获得更多的品味,我尝试在Haskell中实现一些我正在研究的算法。我有一个简单的模块Interval,可以在线上实现间隔。它包含类型

data Interval t = Interval t t

辅助函数

makeInterval :: (Ord t) => t -> t -> Interval t  
makeInterval l r  | l <= r    = Interval l r  
                  | otherwise = error "bad interval"  

和一些关于间隔的效用函数。

在这里,我的兴趣在于多维间隔(d间隔),那些由d间隔组成的对象。我想分别考虑d-区间,它是线上d个不相交区间(多个区间)与d个单独线条(轨道区间)上d区间的并集的联合。考虑到不同的算法处理,我认为有两种不同的类型(即使两者都是间隔列表)也很好,例如

import qualified Interval as I

-- Multilple interval  
newtype MInterval t = MInterval [I.Interval t]  

   -- Track interval  
newtype TInterval t = TInterval [I.Interval t]    

允许进行不同的健全性检查,例如

makeMInterval :: (Ord t) => [I.Interval t] -> MInterval t
makeMInterval is = if foldr (&&) True [I.precedes i i' | (i, i') <- zip is (tail is)]  
                   then (MInterval is)
                   else error "bad multiple interval"


makeTInterval :: (Ord t) => [I.Interval t] -> TInterval t
makeTInterval  = TInterval 

我现在终于明白了!但是一些功能自然与多个间隔和轨道间隔有关。例如,函数order将返回多个间隔或轨道间隔中的间隔数。我能做什么?添加

-- Dimensional interval
data DInterval t = MIntervalStuff (MInterval t) | TIntervalStuff (TInterval t)

没有多大帮助,因为,如果我理解得很好(如果我错了就纠正我),我将不得不写

order :: DInterval t -> Int
order (MIntervalStuff (MInterval is)) = length is
order (TIntervalStuff (TInterval is)) = length is

并在orderorder (MIntervalStuff is)order (TIntervalStuff is)时将is称为MIntervalTInterval。不是很好,看起来很奇怪。我都不想复制该函数(我有许多与多个和轨道间隔有关的函数,以及一些其他d区间定义,例如等长多个和轨道间隔)。

我感觉我完全错了,并且错过了关于Haskell类型的一些重要观点(和/或在这里不能忘记OO编程)。所以,这是一个新手问题,Haskell处理这种情况的最佳方法是什么?我是否必须忘记介绍MIntervalTInterval并仅使用一种类型?

非常感谢你的帮助,

Garulfo

4 个答案:

答案 0 :(得分:5)

编辑:这与sclv的回答是一样的;他的链接提供了有关此技术的更多信息。

这种做法怎么样?

data MInterval = MInterval --multiple interval
data TInterval = TInterval --track interval

data DInterval s t = DInterval [I.Interval t]

makeMInterval :: (Ord t) => [I.Interval t] -> Maybe (DInterval MInterval t)
makeMInterval is = if foldr (&&) True [I.precedes i i' | (i, i') <- zip is (tail is)]
  then Just (DInterval is)
  else Nothing

order :: DInterval s t -> Int
order (DInterval is) = length is

equalOrder :: DInterval s1 t -> DInterval s2 t -> Bool
equalOrder i1 i2 = order i1 == order i2

addToMInterval :: DInterval MInterval t -> Interval t -> Maybe (DInterval MInterval t)
addToMInterval = ..

此处类型DInterval表示多维间隔,但它将一个额外的类型参数作为幻像类型。这种额外的类型信息允许类型检查器区分不同类型的区间,即使它们具有完全相同的表示。

您可以获得原始设计的类型安全性,但所有结构都共享相同的实现。至关重要的是,当间隔的类型无关紧要时,您可以不加指定。

我还改变了makeMInterval函数的实现;为这样的函数返回Maybe比调用错误更加惯用。

关于Maybe的更多解释:

让我们检查你的函数makeInterval。该函数应该采用Interval列表,如果符合条件,则返回多个间隔,否则返回轨道间隔。这种解释导致类型:

makeInterval :: (Ord t) =>
  [I.Interval t] ->
  Either (DInterval TInterval t) (DInterval MInterval t)

现在我们有了类型,我们如何实现它?我们想重新使用我们的makeMInterval函数。

makeInterval is = maybe
                   (Left $ DInterval TInterval is)
                   Right
                   (makeMInterval is)

函数maybe有三个参数:如果Maybe是b则使用默认Nothing,如果Maybe是a -> b则使用函数Just a,和Maybe a。它返回默认值或将函数应用于Maybe值的结果。

我们的默认值是轨道间隔,因此我们为第一个参数创建一个左轨道间隔。如果可能是Just (DInterval MInterval t),则多重间隔已经存在,所以所有必要的是将它粘在两者的右侧。最后,makeMInterval用于创建多个间隔。

答案 1 :(得分:3)

您想要的是具有相同结构和常用操作但可以区分的类型,并且某些操作仅对一种或另一种类型有意义。 Haskell中的这个成语是Phantom Types。

  1. http://www.haskell.org/haskellwiki/Phantom_type
  2. http://www.haskell.org/haskellwiki/Wrapper_types
  3. http://blog.malde.org/index.php/2009/05/14/using-a-phantom-type-to-label-different-kinds-of-sequences/
  4. http://neilmitchell.blogspot.com/2007/04/phantom-types-for-real-problems.html

答案 2 :(得分:3)

你想要的是一个类型类。类型类是在Haskell中完成重载的方式。类型类是由许多类型共享的公共结构。在您的情况下,您想要创建一个具有order函数的所有类型的类:

 class Dimensional a where
     order :: a -> Int

这就是说有一类名为a的{​​{1}}类型,对于属于这个类的每种类型,都有一个名为Dimensional的函数a -> Int。现在,您需要将所有类型声明为属于该类:

order

等等。您还可以通过以下方式声明对来自给定类的内容起作用的函数:

 instance Dimensional MInterval where
     order (MInterval is) = length is

对于属于类型 isOneDimensional :: (Dimensional a) => a -> Bool isOneDimensional interval = (order interval == 1) 的所有类型a,可以读取类型声明,此函数需要Dimensional并返回a“。

我喜欢把类看作数学中的代数结构(它与你不能强制执行某些逻辑约束的东西不完全相同,但是......):你说明一个类型属于某些类型的要求是什么class(在类比中,必须在一个集合上定义操作以使其属于某个代数结构),然后,对于每个特定类型,您说明所需函数的具体实现是什么(在类比中,什么是每个特定集合的具体功能)。

以一个小组为例。组必须具有标识,每个元素的反转和乘法:

Bool

和整数组成了一个小组:

 class Group a where:
        identity :: a
        inverse  :: a -> a 
        (<*>)      :: a -> a -> a

请注意,这不会解决重复问题,因为您必须实例化每种类型。

答案 3 :(得分:0)

考虑使用类型类来显示不同类型之间的相似性。有关类和重载的信息,请参阅haskell教程[1]以获取帮助。

[1]:http://www.haskell.org/tutorial/classes.html haskell教程