()在Haskell中究竟是什么? [英] What is () in Haskell, exactly?

查看:85
本文介绍了()在Haskell中究竟是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在读学习你一个Haskell ,并且在monad章节中,在我看来,()被视为一个每种类型都有空。当我检查GHCi中()的类型时,我得到

  >> :t()
()::()

这是一个非常令人困惑的陈述。看起来,()是一种自身的类型。我很困惑它是如何适应语言的,以及它如何能够支持任何类型。

解决方案

tl; dr ()不会为每个类型添加一个null值,hell no; ()在它自己的类型中是一个无趣值:()



让我从这个问题中退一步,解决一个常见的混淆来源。学习Haskell时要注意的一个关键是它的 expression 语言和它的 type 语言之间的区别。你可能知道两者是分开的。但是,这允许在两者中使用相同的符号,这就是发生在这里的事情。有简单的文字提示来告诉你你正在看哪种语言。您不需要解析整个语言来检测这些线索。



默认情况下,Haskell模块的顶层存在于表达式语言中。通过写入表达式之间的方程来定义函数。但是当您在表达式语言中看到 foo :: bar 时,这意味着 foo 是表达式和​​ bar 是它的类型。所以当你阅读()::()时,你会看到一条语句,它与()表达式语言与类型语言中的()。两个()符号表示不同的东西,因为它们不是相同的语言。这种重复通常会给初学者造成混淆,直到表达式/类型语言分离将其自身安装在他们的潜意识中,此时它变得有助于助记符。

关键字 data 引入了一个新的数据类型声明,它涉及到表达式和类型语言的仔细混合,因为它首先说明新类型是什么,其次是它的值是什么。

 
data TyCon tyvar ... tyvar = ValCon1 type ... type | ... | ValConn type ... type

在这样的声明中,输入构造函数 TyCon 正在被添加到类型语言中,并且将 ValCon 值构造函数添加到表达式语言(及其模式子语言)中。在 data 声明中,valCon 的参数位置中的内容告诉您当时给定参数的类型用于表达式中。例如,

 数据树a = Leaf | Node(Tree a)a(Tree a)

声明一个类型构造函数 Tree 用于存储节点元素的二叉树类型,其值由值构造函数 Leaf Node 。我喜欢颜色类型构造函数(Tree)蓝色和值构造函数(Leaf,Node)红色。在表达式中不应该有蓝色,并且(除非您使用高级功能)类型中不会有红色。可以声明内置类型 Bool

  data Bool = True | False 

在类型语言中添加蓝色 Bool ,红色 True False 添加到表达式语言中。不幸的是,我的markdown-fu不足以将颜色添加到本文中,因此您只需要学习在头上添加颜色即可。



unit类型使用()作为特殊符号,但它的工作方式与声明一样

  data()=() -  left()是蓝色的; ()是红色的

这意味着一个概念上的蓝色() code>是类型语言中的一个类型构造函数,但是概念上的红色()是表达式语言中的一个值构造函数,事实上()::()。 [这不是这种双关语的唯一例子。更大的元组的类型遵循相同的模式:对语法如同给出的一样

  data(a,b)=(a ,b)

将(,)添加到类型和表达式语言中。但是我离题了。

因此,通常发音为Unit的类型()是一个包含一个值的类型值得一提的是:该值被写为(),但是在表达式语言中,并且有时发音为void。只有一个值的类型不是很有趣。类型()的值会贡献零位信息:您已经知道它必须是什么。所以,尽管()类型没有什么特别的地方可以表明副作用,但它通常显示为一元类型中的值组件。一元操作的类型看起来像

 
val-in-type-1 - > ... - > val-in-type-n - > effect-monad val-out-type

返回类型是一个类型应用程序:该函数告诉您哪些效果是可能的,并且参数告诉您该操作产生了什么样的值。例如

  put :: s  - >状态s()

这是读取的(因为应用程序会关联到左侧[六十年代,Roger Hindley))作为

  put :: s  - > (州)()

有一个值输入类型 s ,效果monad 状态s 和值输出类型()。当您看到()作为值输出类型时,意味着此操作仅用于其效果;所提供的值不感兴趣 。同样地

  putStr :: String  - > IO()

将字符串传递给 stdout 但不会返回任何令人兴奋的东西。
$ b

()类型也可用作类似容器的元素类型结构,它表示数据只包含一个形状,没有有趣的有效载荷。例如,如果 Tree 如上所述被声明,那么 Tree()是二叉树形状的类型,节点感兴趣。类似地, [()] 是无聊元素列表的类型,如果列表元素中没有任何兴趣,那么它贡献的唯一信息就是其长度。 / p>

综上所述,()是一个类型。它的一个值()恰巧具有相同的名称,但没关系,因为类型和表达式语言是分开的。有一个代表无信息的类型是有用的,因为在上下文中(例如,monad或容器),它告诉你只有上下文很有趣。


I'm reading Learn You a Haskell, and in the monad chapters, it seems to me that () is being treated as a sort of "null" for every type. When I check the type of () in GHCi, I get

>> :t ()
() :: ()

which is an extremely confusing statement. It seems that () is a type all to itself. I'm confused as to how it fits into the language, and how it seems to be able to stand for any type.

解决方案

tl;dr () does not add a "null" value to every type, hell no; () is a "dull" value in a type of its own: ().

Let me step back from the question a moment and address a common source of confusion. A key thing to absorb when learning Haskell is the distinction between its expression language and its type language. You're probably aware that the two are kept separate. But that allows the same symbol to be used in both, and that is what is going on here. There are simple textual cues to tell you which language you're looking at. You don't need to parse the whole language to detect these cues.

The top level of a Haskell module lives, by default, in the expression language. You define functions by writing equations between expressions. But when you see foo :: bar in the expression language, it means that foo is an expression and bar is its type. So when you read () :: (), you're seeing a statement which relates the () in the expression language with the () in the type language. The two () symbols mean different things, because they are not in the same language. This repetition often causes confusion for beginners, until the expression/type language separation installs itself in their subconscious, at which point it becomes helpfully mnemonic.

The keyword data introduces a new datatype declaration, involving a careful mixture of the expression and type languages, as it says first what the new type is, and secondly what its values are.

data TyCon tyvar ... tyvar = ValCon1 type ... type |  ...  | ValConn type ... type

In such a declaration, type constructor TyCon is being added to the type language and the ValCon value constructors are being added to the expression language (and its pattern sublanguage). In a data declaration, the things which stand in argument places for the ValCon s tell you the types given to the arguments when that ValCon is used in expressions. For example,

data Tree a = Leaf | Node (Tree a) a (Tree a)

declares a type constructor Tree for binary tree types storing elements at nodes, whose values are given by value constructors Leaf and Node. I like to colour type constructors (Tree) blue and value constructors (Leaf, Node) red. There should be no blue in expressions and (unless you're using advanced features) no red in types. The built-in type Bool could be declared,

data Bool = True | False

adding blue Bool to the type language, and red True and False to the expression language. Sadly, my markdown-fu is inadequate to the task of adding the colours to this post, so you'll just have to learn to add the colours in your head.

The "unit" type uses () as a special symbol, but it works as if declared

data () = ()  -- the left () is blue; the right () is red

meaning that a notionally blue () is a type constructor in the type language, but that a notionally red () is a value constructor in the expression language, and indeed () :: (). [It is not the only example of such a pun. The types of larger tuples follow the same pattern: pair syntax is as if given by

data (a, b) = (a, b)

adding (,) to both type and expression languages. But I digress.

So the type (), often pronounced "Unit", is a type containing one value worth speaking of: that value is written () but in the expression language, and is sometimes pronounced "void". A type with only one value is not very interesting. A value of type () contributes zero bits of information: you already know what it must be. So, while there is nothing special about type () to indicate side effects, it often shows up as the value component in a monadic type. Monadic operations tend to have types which look like

val-in-type-1 -> ... -> val-in-type-n -> effect-monad val-out-type

where the return type is a type application: the function tells you which effects are possible and the argument tells you what sort of value is produced by the operation. For example

put :: s -> State s ()

which is read (because application associates to the left ["as we all did in the sixties", Roger Hindley]) as

put :: s -> (State s) ()

has one value input type s, the effect-monad State s, and the value output type (). When you see () as a value output type, that just means "this operation is used only for its effect; the value delivered is uninteresting". Similarly

putStr :: String -> IO ()

delivers a string to stdout but does not return anything exciting.

The () type is also useful as an element type for container-like structures, where it indicates that the data consists just of a shape, with no interesting payload. For example, if Tree is declared as above, then Tree () is the type of binary tree shapes, storing nothing of interest at nodes. Similarly [()] is the type of lists of dull elements, and if there is nothing of interest in a list's elements, then the only information it contributes is its length.

To sum up, () is a type. Its one value, (), happens to have the same name, but that's ok because the type and expression languages are separate. It's useful to have a type representing "no information" because, in context (e.g., of a monad or a container), it tells you that only the context is interesting.

这篇关于()在Haskell中究竟是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆