问题 1: lambda 如何将 s ( for state ) 作为参数?它是如何传入的?
get
让我们使用and的经典定义put
,定义为:
put :: Int -> SimpleState ()
put n = SimpleState (\_ -> ((), n))
get :: SimpleState Int
get = SimpleState (\s -> (s, s))
当您调用时applySimple
,您打开SimpleState
,它公开了一个类型的函数Int -> (a, Int)
。然后将该函数应用于初始状态。让我们用一些具体的例子来尝试一下。
首先,我们将运行命令put 1
,初始状态为0
:
applySimple (put 1) 0
-- Substitute in definition of 'put'
= applySimple (SimpleState (\_ -> ((), 1))) 0
-- applySimple (Simple f) = f
(\_ -> ((), 1)) 0
-- Apply the function
= ((), 1)
请注意如何put
忽略初始状态并仅将右侧状态槽替换为1
,将左侧返回值槽留在后面()
。
现在让我们尝试运行 get 命令,使用的起始状态为0
:
applySimple get 0
-- Substitute in definition of 'get'
= applySimple (SimpleState (\s -> (s, s))) 0
-- applySimple (SimpleState f) = f
= (\s -> (s, s)) 0
-- Apply the function
= (0, 0)
get
只是复制0
到左边的返回值槽,而右边的状态槽保持不变。
因此,您将初始状态传递给该 lambda 函数的方式只是展开新SimpleState
类型以公开底层 lambda 函数并将 lambda 函数直接应用于初始状态。
问题 2:如果 applySimple 在其函数签名中接受一个参数,为什么我在 lambda 中有 applySimple st s?为什么 applySimple 应用了两次?
那是因为applySimple
's 类型不是Int -> (a, Int)
. 其实是:
applySimple :: SimpleState -> Int -> (a, Int)
这是 Haskell 记录语法的一个令人困惑的方面。每当您有如下记录字段时:
data SomeType { field :: FieldType }
... thenfield
的类型实际上是:
field :: SomeType -> FieldType
我知道这很奇怪。
问题 3. 这是什么?为什么它对 SimpleState 执行某种操作,但它的签名不是函数?
newtypeSimpleState
隐藏了它包装的函数。 newtypes
可以隐藏任何东西,直到你打开它们。你SimpleState
里面确实有函数,但是编译器看到的只是外部的新类型,直到你用applySimple
.
问题 4:我可以/我如何将 tic 与 >>= 一起使用?
你在定义中犯了一个错误incr
。正确的使用tic
方法是这样的:
ticTwice :: SimpleState ()
ticTwice = do
tic
tic
...编译器将其转换为:
ticTwice =
tic >>= \_ ->
tic
使用(>>=)
和 tic 的定义,您可以证明这会将值增加 2:
tic >>= \_ -> tic
-- Substitute in definition of `(>>=)`
SimpleState (\s ->
let (x, s') = applySimple tic s
in applySimple ((\_ -> tic) x) s')
-- Apply the (\_ -> tic) function
SimpleState (\s ->
let (x, s') = applySimple tic s
in applySimple tic s')
-- Substitute in definition of `tic`
SimpleState (\s ->
let (x, s') = applySimple (SimpleState (\s -> (s, s + 1))) s
in applySimple (SimpleState (\s -> (s, s + 1))) s'
-- applySimple (SimpleState f) = f
SimpleState (\s ->
let (x, s') = (\s -> (s, s + 1)) s
in (\s -> (s, s + 1)) s'
-- Apply both functions
SimpleState (\s ->
let (x, s') = (s, s + 1)
in (s', s' + 1)
-- Simplify by getting rid of unused 'x'
SimpleState (\s ->
let s' = s + 1
in (s', s' + 1)
-- Simplify some more:
SimpleState (\s -> s + 1, s + 2)
所以你会看到,当你链接两个tic
s using(>>=)
时,它会将它们组合成一个有状态的函数,该函数将状态递增2
,并返回状态 plus 1
。