Haskell IORef - ответ против функции, чтобы получить ответ - PullRequest
0 голосов
/ 26 ноября 2018

Я пытаюсь понять, как на самом деле используются 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 ", который, кажется, должен присваиваться константе" IO 0 "при первом вызовеа потом никогда не изменится?Вместо этого c, кажется, получает назначенную функцию, определенную в этом блоке "return $ do", которая затем выполняется заново каждый раз, когда printCounts добирается до другого "print =<< c".Кажется, что ответ каким-то образом заключается в том, что newCounter имеет двойной вложенный тип "IO (IO Int)", но я не могу понять, почему это делает c функцией, которая будет повторно выполняться при вызове вместо константы, вычисляемой только один раз..

1 Ответ

0 голосов
/ 26 ноября 2018

Вы можете думать о 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 буквально выделяет счетчик и увеличивает его три раза, печатая каждое промежуточное значение.

Один ключевой шаг - это этап подстановки, когда счетная программа дублируется, поэтому она запускается три раза.let x = p; ... отличается от x <- p; ..., который запускает p и привязывает x к результату, а не к самой программе p.

...