Haskell(数据)构造函数构造什么?
Haskell使人们能够使用类型构造函数和数据构造函数构造代数数据类型。比如说,Haskell(数据)构造函数构造什么?,haskell,functional-programming,algebraic-data-types,Haskell,Functional Programming,Algebraic Data Types,Haskell使人们能够使用类型构造函数和数据构造函数构造代数数据类型。比如说, data Circle = Circle Float Float Float 我们被告知这个数据构造器(右边的圆)是一个函数,它在给定数据时构造一个圆,例如x,y,半径 Circle :: Float -> Float -> Float -> Circle 我的问题是: 这个函数具体构造了什么 我们可以定义构造函数吗 我见过智能构造函数,但它们似乎只是最终调用常规构造函数的额外函数 当然,来
data Circle = Circle Float Float Float
我们被告知这个数据构造器(右边的圆)是一个函数,它在给定数据时构造一个圆,例如x,y,半径
Circle :: Float -> Float -> Float -> Circle
我的问题是:
当然,来自OO背景的构造函数有必要的规范。在Haskell中,它们似乎是系统定义的。在Haskell中,在不考虑底层实现的情况下,数据构造函数创建一个值,基本上是通过fiat创建的。“ ‘程序员说,让有一个
圈
”,有一个圈
“问圈1 2 3
创建了什么类似于问1
在Python或Java中创建了什么
空构造函数更接近您通常认为的文本。Boolean
类型字面上定义为
data Boolean = True | False
其中True
和False
是数据构造函数,而不是Haskell语法定义的文本
数据类型也是构造函数的定义;因为除了构造函数名和它的参数之外,实际上没有任何值,所以只需声明它就是定义。通过使用3个参数调用数据构造函数Circle
,可以创建一个Circle
类型的值,就是这样
所谓的“智能构造函数”只是一个调用数据构造函数的函数,可能还有一些其他逻辑来限制可以创建哪些实例。例如,考虑一个围绕<代码>整数< /> >的简单包装:
newtype PosInteger = PosInt Integer
构造函数是PosInt
;智能构造函数可能看起来像
mkPosInt :: Integer -> PosInteger
mkPosInt n | n > 0 = PosInt n
| otherwise = error "Argument must be positive"
使用
mkPosInt
,无法使用非正参数创建PosInteger
值,因为实际上只有正参数调用数据构造函数。智能构造函数(而不是数据构造函数)由模块导出时最有意义,因此典型用户无法创建任意实例(因为数据构造函数不存在于模块之外)。问得好。如你所知,根据定义:
data Foo = A | B Int
这定义了一个具有(空)类型构造函数Foo
和两个数据构造函数a
和B
的类型
这些数据构造函数中的每一个在完全应用时(在A
的情况下不适用于任何参数,在B
的情况下适用于单个Int
参数)都构造了Foo
类型的值。因此,当我写作时:
a :: Foo
a = A
b :: Foo
b = B 10
名称a
和b
绑定到类型为Foo
的两个值
So,类型Foo
的数据构造函数构造类型Foo
的值
什么是Foo
类型的值?首先,它们不同于任何其他类型的值。其次,它们完全由它们的数据构造函数定义。对于数据构造函数的每个组合,都有一个类型为Foo
的不同值,不同于Foo
的所有其他值,该数据构造函数具有一组传递给该数据构造函数的不同参数。也就是说,Foo
类型的两个值是相同的,当且仅当它们是由给定相同参数集的相同数据构造函数构造时。(“相同”在这里意味着与“相等”不同的东西,相等可能不一定是为给定类型定义的,但我们不要讨论它。)
这也是数据构造函数不同于Haskell中函数的原因。如果我有一个函数:
bar :: Int -> Bool
bar 1
和bar 2
可能是完全相同的值。例如,如果bar
由以下内容定义:
bar n = n > 0
很明显,bar 1
和bar 2
(和bar 3
)是相同的正确的。对于参数的不同值,bar
的值是否相同取决于函数定义
相反,如果Bar
是构造函数:
data BarType = Bar Int
那就永远不会出现Bar 1
和Bar 2
是同一个值的情况。根据定义,它们将是不同的值(类型为BarType
)
顺便说一句,构造函数只是一种特殊的函数的想法是一种常见的观点。我个人认为这是不准确的,会引起混乱。虽然构造函数通常可以被当作函数来使用(特别是在表达式中使用时,它们的行为非常类似于函数),但我认为这一观点不值得仔细研究——构造函数在语言的表面语法中有不同的表示(使用大写标识符),可以在不能使用函数的上下文(如模式匹配)中使用,在编译代码中以不同的方式表示,等等
所以,当你问“我们能定义构造函数吗”,答案是“不”,因为没有构造函数。相反,像a
或B
或Bar
或Circle
这样的构造函数就是它——它不同于函数(有时表现为具有一些特殊附加属性的函数),函数能够构造数据构造函数所属的任何类型的值
这使得Haskell构造函数与OO构造函数非常不同,但这并不奇怪,因为Haskell值与OO对象非常不同。在OO语言中,您通常可以提供一个构造函数,在构建对象时进行一些处理,因此在Python中您可以编写:
class Bar:
def __init__(self, n):
self.value = n > 0
然后:
bar1 = Bar(1)
bar2 = Bar(2)
我们有两个不同的对象bar1<
<Circle tag>
<Float value 10>
<Float value 20>
<Float value 5>
<Circle tag>
<some Float value>
<another Float value>
<a final Float value>
class Person {
private int id;
private String name;
public Person(int id, String name) {
if (id == 0)
throw new InvalidIdException();
if (name == "")
throw new InvalidNameException();
this.name = name;
this.id = id;
}
public int getId() { return this.id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
module Person
( Person
, mkPerson
, getId
, getName
, setName
) where
data Person = Person
{ personId :: Int
, personName :: String
}
mkPerson :: Int -> String -> Either String Person
mkPerson id name
| id == 0 = Left "invalid id"
| name == "" = Left "invalid name"
| otherwise = Right (Person id name)
getId :: Person -> Int
getId = personId
getName :: Person -> String
getName = personName
setName :: String -> Person -> Either String Person
setName name person = mkPerson (personId person) name