2015-10-24 54 views

回答

14

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

type String = [Char] 

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

要清楚,String字面上是列表Char s。这只是一个别名。您可以使用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 } 
type ErrorWithMessage = Error String 

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

重要的是要认识到,data声明了一个新类型,是除了系统中的任何其它类型很重要。如果String已被声明为data类型包含列表Char(而不是类型同义词),您将无法使用任何列表功能。

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

还有一个更有多种类型声明:newtype。这很像一个data声明 - 它引入了一种独立于任何其他类型的新数据类型,并且可以进行模式匹配 - 除了您仅限于具有单个字段的单个构造函数。换句话说,newtype是一个data类型,它包装了一个现有的类型。

重要的区别在于成本newtype:编译器承诺newtype以与其包装类型相同的方式表示。打包或解包newtype没有运行时间成本。这使得newtype可用于制定管理(而不是结构)值之间的区别。

newtype s与类型类很好地交互。例如,考虑Monoid,这是一种将元素(mappend)和特殊“空”元素(mempty)组合在一起的类型类。 Int可以通过多种方式制作为Monoid,包括加0和加1.我们如何选择哪一个用于IntMonoid实例?最好不要表达偏好,并使用newtype s来启用或不使用运行时成本。复述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

随着data创建数据类型和声明构造它:

data NewData = NewDataConstructor 

随着type定义只是一个别名:

type MyChar = Char 

type情况下,你可以传递的价值MyChar类型功能期望Char,反之亦然,但您不能这样做data MyChar = MyChar Char

2

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',反之亦然更换f;没有什么会改变。

OTOH,均为datanewtype创建不透明抽象。它们更像是一个OO中的类构造函数:即使一些值是实施仅仅根据单个数字,它不一定行为就像这样一个数字。例如,

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数,它清楚地表现从Double不同。

相关问题