Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/reporting-services/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell(数据)构造函数构造什么?_Haskell_Functional Programming_Algebraic Data Types - Fatal编程技术网

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 我的问题是: 这个函数具体构造了什么 我们可以定义构造函数吗 我见过智能构造函数,但它们似乎只是最终调用常规构造函数的额外函数 当然,来

Haskell使人们能够使用类型构造函数和数据构造函数构造代数数据类型。比如说,

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