“数据”和“类型”关键字之间有什么区别?

时间:2015-10-24 09:21:25

标签: haskell keyword

datatype关键字总是让我感到困惑。

我想知道datatype之间的区别以及如何使用它们。

3 个答案:

答案 0 :(得分:18)

type声明类型同义词。类型同义词是现有类型的新名称。例如,这就是String的定义in the standard library

type String = [Char]

StringChar列表的另一个名称。 GHC将在编译时用String替换程序中[Char]的所有用法。

要明确的是,String 字面上Char的列表。这只是一个别名。您可以使用String值上的所有标准列表函数:

-- length :: [a] -> Int
ghci> length "haskell"
7
-- reverse :: [a] -> [a]
ghci> reverse "functional"
"lanoitcnuf"

data声明新数据类型,与类型同义词不同,它与任何其他类型不同。数据类型有许多构造函数,用于定义类型的可能情况。例如,这就是Bool的定义in the standard library

data Bool = False | True

Bool值可以是TrueFalse。数据类型支持模式匹配,允许您对数据类型的值执行运行时大小写分析。

yesno :: Bool -> String
yesno True = "yes"
yesno False = "no"

data类型可以有多个构造函数(与Bool一样),可以通过其他类型进行参数化,可以包含其中的其他类型,并且可以递归地引用自己。这是一个例外模型,证明了这一点; Error a包含a类型的错误消息,可能还有导致它的错误。

data Error a = Error { value :: a, cause :: Maybe (Error a) }
type ErrorWithMessage = Error String

myError1, myError2 :: ErrorWithMessage
myError1 = Error "woops" Nothing
myError2 = Error "myError1 was thrown" (Just myError1)

重要的是要意识到data声明了一种与系统中任何其他类型不同的新类型。如果String已被声明为data类型包含 Char s列表(而非类型同义词),则您将无法使用任何列表都在其上运行。

data String = MkString [Char]
myString = MkString ['h', 'e', 'l', 'l', 'o']
myReversedString = reverse myString  -- type error

还有一种类型声明:newtype。这类似于data声明 - 它引入了与任何其他类型分开的新数据类型,并且可以进行模式匹配 - 除了您被限制为具有单个字段的单个构造函数。换句话说,newtypedata类型,它包含现有类型。

重要的区别是newtype cost :编译器承诺newtype的表示方式与它包装的类型相同。打包或解包newtype没有运行时成本。这使得newtype对于在值之间进行管理(而非结构)区分非常有用。

newtype与类型类很好地互动。例如,考虑Monoid,类型类,其中包含组合元素(mappend)和特殊“空”元素(mempty)的方法。 Int可以通过多种方式设置为Monoid,包括添加0和乘以1.我们如何选择使用哪一个Monoid Int实例}?最好不要表达首选项,并使用newtype来启用任何使用而不需要运行时成本。释义the standard library

-- introduce a type Sum with a constructor Sum which wraps an Int, and an extractor getSum which gives you back the Int
newtype Sum = Sum { getSum :: Int }
instance Monoid Sum where
    (Sum x) `mappend` (Sum y) = Sum (x + y)
    mempty = Sum 0

newtype Product = Product { getProduct :: Int }
instance Monoid Product where
    (Product x) `mappend` (Product y) = Product (x * y)
    mempty = Product 1

答案 1 :(得分:2)

readDouble()的工作原理与type类似:它允许您为某些内容提供可重复使用的名称,但这样的内容将始终有效,就像您已经内联定义一样。所以

let

的行为与

完全相同
type ℝ = Double

f :: ℝ -> ℝ -> ℝ
f x y = let x2 = x^2
        in x2 + y

如下:您可以在代码中的任何位置将f' :: Double -> Double -> Double f' x y = x^2 + y 替换为f,反之亦然;什么都不会改变。

OTOH,f'data都会创建一个不透明的抽象。它们更像是OO中的类构造函数:即使某个值实现只是单个数字,它也不一定表现就像这样的数字。例如,

newtype

虽然newtype Logscaledℝ = LogScaledℝ { getLogscaled :: Double } instance Num LogScaledℝ where LogScaledℝ a + LogScaledℝ b = LogScaledℝ $ a*b LogScaledℝ a - LogScaledℝ b = LogScaledℝ $ a/b LogScaledℝ a * LogScaledℝ b = LogScaledℝ $ a**b 在数据方面仍属于Logscaledℝ个数字,但它明显与Double不同。

答案 2 :(得分:1)

使用data创建 new 数据类型并为其声明构造函数:

data NewData = NewDataConstructor

使用type,您只需定义一个别名:

type MyChar = Char

type案例中,您可以将MyChar类型的值传递给期望Char的函数,反之亦然,但不能为data MyChar = MyChar Char执行此操作。< / p>