Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.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_Types - Fatal编程技术网

Haskell 自定义数据类型的用途?

Haskell 自定义数据类型的用途?,haskell,types,Haskell,Types,这是一个简单的问题,但我仍然不确定。在Haskell中定制类型的主要好处是什么,例如: data Users a = User a a a data Towns a = TownA Int | TownB String | Nill 这些自定义类型的主要用途是充当命令式语言中的对象吗?比如 template<typename U> class User { U a1; U a2; U a3; }; int main() { User<std::strin

这是一个简单的问题,但我仍然不确定。在Haskell中定制类型的主要好处是什么,例如:

data Users a = User a a a 
data Towns a = TownA Int | TownB String | Nill
这些自定义类型的主要用途是充当命令式语言中的对象吗?比如

template<typename U>
class User 
{
  U a1;
  U a2;
  U a3;
};

int main()
{
  User<std::string> UserObj; UserObj.a1="a1"; //etc
}
有没有人有一个简短而笼统的解释


注意:我理解二进制类型的有用性,例如,我的问题更多的是关于自定义类型的使用。

如果不考虑您具体引用的类型[1],我认为我们可以讨论三种主要的好处:

限制你的权力 作为一个任意示例,选择您的用户类型。与其定义一个单独的数据类型,不如尝试使用列表。也许您甚至可以使用type来提供一个类型同义词,并使其更漂亮一些:

type Users a = [a]
makeUsers u1 u2 u3 = [u1, u2, u3]
但是,如果原始定义为data Users a=Users a,则可以合理地假设数据类型中有三个字段不是任意选择的结果,而是因为每个集合中有三个用户是您要强制执行的相关条件。但是,如果用户是一个列表,则通过更改列表中元素的数量来打破这一点很简单:

us1 = makeUsers "Jack" "Eric" "Ginger"
us2 = drop 1 us1 -- Oops!
但是,如果按照最初建议的方式定义类型,则不可能发生这种意外,因为无法创建具有三个或三个以下字段的用户。通常,自定义类型可以让您更好地控制您或其他人可以对该类型的值执行的操作。其他类似的做法也包括不为自定义类型导出构造函数和字段名,这与在C++之类的字段中生成字段的效果大致相同,而不为不实际使用的类编写实例。 增加你的力量 考虑到我们上面所说的,有人可能会考虑让用户成为一个3元组——毕竟,这保证包含三个元素:

type Users a = (a, a, a)
makeUsers u1 u2 u3 = (u1, u2, u3)
这当然是一个进步。但是,假设您出于任何原因希望将函数应用于用户中的所有三个值。如果用户是函子,您可以简单地使用fmap:


然而,同构3元组没有函子实例,当类和类型都在其他库中定义时,在您自己的代码中编写类的实例(行话称为孤立实例)几乎总是一个糟糕的想法。因此,您必须编写一个无类映射函数,并使用它自己的名称:

mapUsers :: (a -> b) -> Users a -> Users b
mapUsers f (Users u1 u2 u3) = Users (f u1) (f u2) (f u3)
但是,一直这样做会很快变老,用大量不必要的名称污染名称空间,最糟糕的是,会阻止您使用其他库中需要Functor实例的所有机制,而不是为其定义类似fmap函数的任意类型。但是,如果您有自定义类型,则可以很好地提供实例:

instance Functor Users where
    fmap f (Users u1 u2 u3) = Users (f u1) (f u2) (f u3)
顺便说一句,我应该提到该语言中有一个工具,它精确地涵盖了您希望使用一个先前存在的类型,但有新的或不同的实例的情况:关键字newtype。但这是另一个未来的问题

澄清你的代码 最后,使用自定义名称和自定义字段名的自定义类型还有一个好处,如果您使用记录语法,则会使您使用的每个值的实际用途更加明显,这无疑是一个非常显著的好处

正如chi在评论中指出的,这些好处适用于任何类型的语言。然而,考虑到Haskell的类型系统是多么强大,很好地定义自己的类型所带来的好处比大多数其他语言都要大

[1] :虽然如果您实际使用的是Towns a,您可能应该删除缺少的值占位符Nill


。。。在需要Nill的情况下,可以使用Town而不是Town。

如果不考虑您具体引用的类型[1],我认为我们可以讨论三种主要的好处:

限制你的权力 作为一个任意示例,选择您的用户类型。与其定义一个单独的数据类型,不如尝试使用列表。也许您甚至可以使用type来提供一个类型同义词,并使其更漂亮一些:

type Users a = [a]
makeUsers u1 u2 u3 = [u1, u2, u3]
但是,如果原始定义为data Users a=Users a,则可以合理地假设数据类型中有三个字段不是任意选择的结果,而是因为每个集合中有三个用户是您要强制执行的相关条件。但是,如果用户是一个列表,则通过更改列表中元素的数量来打破这一点很简单:

us1 = makeUsers "Jack" "Eric" "Ginger"
us2 = drop 1 us1 -- Oops!
但是,如果按照最初建议的方式定义类型,则不可能发生这种意外,因为无法创建具有三个或三个以下字段的用户。通常,自定义类型可以让您更好地控制您或其他人可以对该类型的值执行的操作。其他类似的动作包括不导出具有粗糙属性的自定义类型的构造函数和字段名 同样的效果,使得字段在C++之类的东西中是私有的,而不是为你不想实际使用的类编写实例。 增加你的力量 考虑到我们上面所说的,有人可能会考虑让用户成为一个3元组——毕竟,这保证包含三个元素:

type Users a = (a, a, a)
makeUsers u1 u2 u3 = (u1, u2, u3)
这当然是一个进步。但是,假设您出于任何原因希望将函数应用于用户中的所有三个值。如果用户是函子,您可以简单地使用fmap:


然而,同构3元组没有函子实例,当类和类型都在其他库中定义时,在您自己的代码中编写类的实例(行话称为孤立实例)几乎总是一个糟糕的想法。因此,您必须编写一个无类映射函数,并使用它自己的名称:

mapUsers :: (a -> b) -> Users a -> Users b
mapUsers f (Users u1 u2 u3) = Users (f u1) (f u2) (f u3)
但是,一直这样做会很快变老,用大量不必要的名称污染名称空间,最糟糕的是,会阻止您使用其他库中需要Functor实例的所有机制,而不是为其定义类似fmap函数的任意类型。但是,如果您有自定义类型,则可以很好地提供实例:

instance Functor Users where
    fmap f (Users u1 u2 u3) = Users (f u1) (f u2) (f u3)
顺便说一句,我应该提到该语言中有一个工具,它精确地涵盖了您希望使用一个先前存在的类型,但有新的或不同的实例的情况:关键字newtype。但这是另一个未来的问题

澄清你的代码 最后,使用自定义名称和自定义字段名的自定义类型还有一个好处,如果您使用记录语法,则会使您使用的每个值的实际用途更加明显,这无疑是一个非常显著的好处

正如chi在评论中指出的,这些好处适用于任何类型的语言。然而,考虑到Haskell的类型系统是多么强大,很好地定义自己的类型所带来的好处比大多数其他语言都要大

[1] :虽然如果您实际使用的是Towns a,您可能应该删除缺少的值占位符Nill


。。。在你需要NILL的情况下使用城镇而不是城镇。< /P>你的C++示例不显示任何对象。虽然用户不是正式的类型,但用户是类型而不是对象。所以,如果你的问题是用Haskell类型是否使用C++类型,那么答案是大体上是的。你的用户在Haskell中的类型大致对应于C++中的用户类型。我想知道为什么自定义类型会让你吃惊,它们可以用很多编程语言来声明。你的C++例子没有显示任何对象。虽然用户不是正式的类型,但用户是类型而不是对象。所以,如果你的问题是用Haskell类型是否使用C++类型,那么答案是大体上是的。你的用户在Haskell中的类型大致对应于C++中的用户类型。我想知道为什么定制类型会让你感到惊讶,它们可以在许多编程语言中声明。用自己的代码编写在其他库中定义的类型的实例这一术语“孤立实例”几乎总是一个可怕的想法。您忽略了这样一个条件,即类也必须在其他库中定义。@duplode您能用Maybe to进一步解释吗替换零?我不太明白…@BabraCunningham如果你用Nill作为一个失踪城镇的占位符,最好不要让它成为城镇类型的一部分,因为一个不存在的城镇不是城镇。这样做之后,当可能缺少值时,您可以使用Maybe Town而不是Town,例如Just Cheltenham vs Nothing,当值不存在时,您可以使用Town。这一点更为精确,因为您仅从类型中了解到,当值可能丢失时,这一点也更为方便。如果值不可能丢失,则无需担心Nill情况。我已经更新了答案。在上面的评论中,应该是TownA Cheltenham而不仅仅是Cheltenham,当然。在您自己的代码中为其他库中定义的类型编写一个实例,这个术语称为孤立实例,这几乎总是一个可怕的想法。您忽略了这样一个条件,即类也必须在其他库中定义。@duplode您能解释一下,用Maybe替换Nil可以吗?我不太明白…@BabraCunningham如果你用Nill作为一个失踪城镇的占位符,最好不要让它成为城镇类型的一部分,因为一个不存在的城镇不是城镇。这样做之后,当可能缺少值时,您可以使用Maybe Town而不是Town,例如Just Cheltenham vs Nothing,当值不存在时,您可以使用Town。这一点更为精确,因为您仅从类型中了解到,当值可能丢失时,这一点也更为方便。如果值不可能丢失,则无需担心Nill情况。我已经更新了答案。当然,在上面的评论中,应该是汤娜·切尔滕纳姆,而不仅仅是切尔滕纳姆。