> 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,运营商`/ ='代表什么?
答案 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 client
与any ((== 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
。