gpt4 book ai didi

Haskell IORef - 一个答案与一个获得答案的函数

转载 作者:行者123 更新时间:2023-12-04 23:36:18 25 4
gpt4 key购买 nike

我正在尝试了解如何 IORefs确实用过,我在遵循我在 https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html 上找到的示例代码时遇到了问题

newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v

printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c

printCounts执行“c <- newCounter ”,为什么不执行 c得到在 newCounter 中完成工作的结果“return $ do” block ,它似乎应该在第一次调用时分配给常量“IO 0”,然后永远不会改变?相反,c似乎被分配了那个“return $ do” block 中定义的函数,然后每次都重新执行printCounts到达另一个“print =<< c”。似乎答案不知何故在于 newCounter具有双重嵌套的“IO (IO Int)”类型,但我不明白为什么会这样 c调用时要重新执行的函数,而不是仅计算一次的常量。

最佳答案

可以想到IO作为一种程序。 newCounter :: IO (IO Int)是输出程序的程序。更准确地说,newCounter分配一个新的计数器,并返回一个程序,该程序在运行时递增计数器并返回其旧值。 newCounter不执行它返回的程序。如果您改为这样写:

newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
let p = do -- name the counter program p
v <- readIORef r
writeIORef r (v + 1)
return v
p -- run the counter program once
return p -- you can still return it to run again later

您还可以使用等式推理来展开printCounts成一系列基元。 printCounts 的所有版本以下是等效程序:

-- original definition
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c

-- by definition of newCounter...

printCounts = do
c <- do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c

-- by the monad laws (quite hand-wavy for brevity)
-- do
-- c <- do
-- X
-- Y
-- .....
-- =
-- do
-- X
-- c <-
-- Y
-- .....
--
-- (more formally,
-- ((m >>= \x -> k x) >>= h) = (m >>= (\x -> k x >>= h)))

printCounts = do
r <- newIORef 0
c <-
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c

-- c <- return X
-- =
-- let c = X
--
-- (more formally, ((return X) >>= (\c -> k c)) = (k X)

printCounts = do
r <- newIORef 0
let c = do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c

-- let-substitution

printCounts = do
r <- newIORef 0
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v

-- after many more applications of monad laws and a bit of renaming to avoid shadowing
-- (in particular, one important step is ((return v >>= print) = (print v)))

printCounts = do
r <- newIORef 0
v1 <- readIORef r
writeIORef r (v1 + 1)
print v1
v2 <- readIORef r
writeIORef r (v2 + 1)
print v2
v3 <- readIORef r
writeIORef r (v3 + 1)
print v3

在最终版本中,您可以看到 printCounts毫不夸张地说,分配一个计数器并将其递增 3 次,打印每个中间值。

一个关键步骤是 let-substitution 步骤,其中计数器程序被复制,这就是它运行 3 次的原因。 let x = p; ...不同于x <- p; ... ,它运行 p , 并绑定(bind) x结果而不是程序 p本身。

关于Haskell IORef - 一个答案与一个获得答案的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53487894/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com