haskell中的Websocket示例使用特殊字符作为函数

时间:2015-04-16 16:17:50

标签: haskell websocket ghc

> type Client = (Text, WS.Connection)
The state kept on the server is simply a list of connected clients. We've added
an alias and some utility functions, so it will be easier to extend this state
later on.
> type ServerState = [Client]
Check if a user already exists (based on username):
> clientExists :: Client -> ServerState -> Bool
> clientExists client = any ((== fst client) . fst)
Remove a client:
> removeClient :: Client -> ServerState -> ServerState
> removeClient client = filter ((/= fst client) . fst)

这是一个取自websockets的文字haskell代码。我不明白clientExists功能如何运作,

clientExists client = any ((== fst client) . fst)

此函数被调用为,

clientExists client clients

那么,该函数如何引用第二个参数clients?以及.运营商做了什么?

再次在removeClient,运营商`/ ='代表什么?

2 个答案:

答案 0 :(得分:2)

.运算符是函数组合运算符,它定义为

f . g = \x -> f (g x)

/=运算符“不等于”,通常在其他语言中写为!=

clientExists函数有两个参数,但第二个参数因为冗余而被关闭。它可以写成

clientExists client clients = all ((== fst client) . fst) clients

但是Haskell允许你在这样的情况下删除最后一个参数。 any函数的类型为(a -> Bool) -> [a] -> Bool,函数any ((== fst client) . fst)的类型为[a] -> Bool。这就是说函数clientExists clientany ((== fst client) . fst)具有相同的功能。

另一种思考方式是Haskell没有多参数函数,只有单个参数函数返回新函数。这是因为->是右关联的,所以类型签名如

a -> b -> c -> d

可以写成

a -> (b -> (c -> d))

没有改变它的含义。使用第二种类型的签名,您可以更清楚地知道,当给定a时,返回类型为b -> (c -> d)的函数。如果下一次给出b,则返回类型为c -> d的函数。最后,如果给出c,则只返回d。由于Haskell中的函数应用程序是如此便宜(只是一个空格),这是透明的,但它派上用场。例如,这意味着您可以编写类似

的代码
incrementAll = map (+1)

onlyPassingStudents = filter ((>= 70) . grade)

在这两种情况下,我还使用了运算符部分,您可以在其中为运算符提供任一参数,只要它包含在parens中就可以运行。在内部看起来更像是

(x +) = \y -> x + y
(+ x) = \y -> y + x

您可以为任何运营商换取+。如果要扩展clientExists的定义以指定所有参数,它将更像

clientExists client clients = any (\c -> fst c == fst client) clients

这个定义与你所拥有的定义完全相同,只是去了解编译器在内部真正使用的内容。

答案 1 :(得分:1)

如有疑问,请使用GHCi解释器找出函数的类型。

首先,/=运算符不等于:

ghci> :t (/=)
(/=) :: Eq a => a -> a -> Bool
ghci> 5 /= 5
False
ghci> 10 /= 5
True

.是两个函数的组合。它将两个函数粘合在一起,就像在数学中一样:

ghci> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
ghci> :t head.tail
head.tail :: [c] -> c
ghci> (head.tail) [1, 2, 3]
2

在涵盖基础知识后,让我们看看它在函数定义中的用法:

ghci> :t (\x -> (== fst x))
(\x-> (== fst x)) :: Eq a => (a, b) -> a -> Bool
ghci> :t (\x-> (== fst x) . fst)
(\x-> (== fst x) . fst) :: Eq b => (b, b1) -> (b, b2) -> Bool
ghci> (\x -> (== fst x) . fst) (1, "a") (1, "b")
True
ghci> (\x -> (== fst x) . fst) (1, "a") (2, "b")
False

正如我们所看到的,(== fst x) . fst用于获取两个元组,并将每个元素的相等性进行比较。现在,这个表达式(让我们称之为fstComp)的类型为fstComp :: Eq b => (b, b1) -> (b, b2) -> Bool,但我们已经将它传递给定义的元组(client :: (Text, WS.Connection)),我们将它计算为{{1 }}

由于我们有(Text, b2) -> Bool,我们可以将第一个参数与前一个类型统一,以使表达式为any :: (a -> Bool) -> [a] -> Bool。实例化(Text, b2) -> [(Text, b2)] -> Bool我们会获得b2 = WS.Connection的类型,或使用类型同义词clientExists :: (Text, WS.Connection) -> [(Text, WS.Connection)] -> Bool