Haskell 什么';这两个词的区别是什么;“数据”;及;“类型”;关键词?

Haskell 什么';这两个词的区别是什么;“数据”;及;“类型”;关键词?,haskell,keyword,Haskell,Keyword,数据和类型关键字总是让我感到困惑 我想知道数据和类型之间的区别以及如何使用它们 使用数据创建新的数据类型并为其声明构造函数: data NewData = NewDataConstructor 使用type只定义一个别名: type MyChar = Char 在type的情况下,您可以将MyChartype的值传递给需要Char的函数,反之亦然,但是对于data MyChar=MyChar Chartype声明了一个类型同义词,您不能这样做。类型同义词是现有类型的新名称。例如,String

数据
类型
关键字总是让我感到困惑


我想知道
数据
类型
之间的区别以及如何使用它们

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

data NewData = NewDataConstructor
使用
type
只定义一个别名:

type MyChar = Char

type
的情况下,您可以将
MyChar
type的值传递给需要
Char
的函数,反之亦然,但是对于
data MyChar=MyChar Char

type
声明了一个类型同义词,您不能这样做。类型同义词是现有类型的新名称。例如,
String
就是这样定义的:

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

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

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

data
声明一个新的数据类型,与类型同义词不同,它与任何其他类型都不同。数据类型有许多构造函数定义类型的可能情况。例如,
Bool
就是这样定义的:

Bool
值可以是
True
False
。数据类型支持模式匹配,允许您对数据类型的值执行运行时案例分析

yesno :: Bool -> String
yesno True = "yes"
yesno False = "no"
数据
类型可以有多个构造函数(与
Bool
一样),可以由其他类型参数化,可以包含其中的其他类型,并且可以递归地引用它们自己。这里有一个例外的模型来证明这一点;
错误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)
重要的是要认识到,
数据
声明了一个新类型,它与系统中的任何其他类型都不同。如果
String
已声明为包含
Char
s列表(而不是类型同义词)的
data
类型,则无法对其使用任何列表函数

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

还有一种类型声明:
newtype
。这与
数据
声明非常相似-它引入了一种与任何其他类型分离的新数据类型,并且可以进行模式匹配-除非您仅限于一个具有单个字段的单个构造函数。换句话说,
newtype
是封装现有类型的
data
类型

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

newtype
s与类型类交互良好。例如,考虑<代码>幺元< /> >,类型的组合元素(<代码> MppEnt/<代码>)和一个特殊的“空”元素(<代码> MimpTy < /代码>)。code>Int
可以通过多种方式构成
Monoid
,包括与0的加法和与1的乘法。对于
Int
的一个可能的
Monoid
实例,我们如何选择使用哪一个?最好不要表达首选项,使用
newtype
s来启用这两种用法,而无需运行时成本。释义:


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,
data
newtype
都创建了一个不透明的抽象。它们更像是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

一个可能的编辑:“最好不要冻结在隐式首选项中,使用
newtype
s显式表达每个可能的选择,而不需要运行时成本”。
type ℝ = Double

f :: ℝ -> ℝ -> ℝ
f x y = let x2 = x^2
        in x2 + y
f' :: Double -> Double -> Double
f' x y = x^2 + y
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