Haskell中对不可变数据的可变引用

时间:2015-03-14 20:41:09

标签: haskell immutability frege

我想跟踪一系列不可变值中的“当前”值。 在没有为每个新值引入新引用的情况下,在Haskell中执行此操作的最佳方法是什么? 这是一个例子:

data Person = Person {name, level, topic :: String }
    deriving(Show)

dierk :: Person
dierk = Person "Dierk" "confident" "Java"

works :: Person -> String
works person = name person ++ " is " ++ level person ++ " in " ++ topic person


main _ = do
    putStrLn $ works dierk
    -- do more with "current" topic
    putStrLn $ works dierk {level= "proficient", topic="Groovy"}
    -- do more with "current" topic
    putStrLn $ works dierk {level= "dabbling", topic="Haskell"}
    -- do more with "current" topic

2 个答案:

答案 0 :(得分:3)

我不确定这个问题究竟是什么问题。发布的例子 可以重写以使用StateT Person IO monad,如下所示。

import Control.Monad.State

data Person = Person {name, level, topic :: String }
   deriving Show

dierk :: Person
dierk = Person "Dierk" "confident" "Java"

works :: Person -> String
works person = name person ++ " is " ++ level person ++ " in " ++ topic person

main :: IO ()
main = flip evalStateT dierk $ do
   -- use the current topic
   lift . putStrLn . works =<< get
   -- change the current topic
   modify (\t -> t{level= "proficient", topic="Groovy"})
   lift . putStrLn . works =<< get
   -- change the current topic
   modify (\t -> t{level= "dabbling", topic="Haskell"})
   lift . putStrLn . works =<< get

{- Output:
Dierk is confident in Java
Dierk is proficient in Groovy
Dierk is dabbling in Haskell
-}

如果需要真正的引用类型,可以在IORef Person monad中使用STRefST。但在这种情况下,你必须在一些monad中工作,允许这些引用类型。相比之下,StateT Person m适用于任何monad m

答案 1 :(得分:0)

只是结束并向我这样的其他Haskell新手提示 - 这是我最终解决的解决方案。这不是伪代码:-)而是Frege(JVM的Haskell),它有一些小的符号差异。

module Person where

import frege.control.monad.State

data Person = Person {name, level, topic :: String }

derive Show Person

dierk = Person "Dierk" "confident" "Java"

works :: Person -> String
works person = person.name ++ " is " ++ person.level ++ " in " ++ person.topic

printCurrentPerson :: StateT Person IO ()
printCurrentPerson = do
    person <- StateT.get            -- independent of any particular person reference
    StateT.lift $ println $ works person

updateCurrentPerson :: Monad m => String -> String -> StateT Person m ()
updateCurrentPerson level topic = do
    StateT.modify (\person -> Person.{level= level, topic=topic} person)

usingMutableRefsToImmutableState :: Person -> IO ((),Person)
usingMutableRefsToImmutableState start =
    flip StateT.run start $ do
        printCurrentPerson
        updateCurrentPerson "proficient" "Groovy"
        printCurrentPerson
        StateT.lift $ println "-- user input could influence which selection is 'current' "
        updateCurrentPerson "dabbling" "Haskell"
        printCurrentPerson

main = do -- using the StateT transformer to work in combination with any monad (here: IO)
    (_, lastPerson) <- usingMutableRefsToImmutableState dierk
    println "-- a second round with relaying the last person"
    _ <- usingMutableRefsToImmutableState lastPerson
    return ()

{-  output
    Dierk is confident in Java
    Dierk is proficient in Groovy
    -- user input could influence which selection is 'current' 
    Dierk is dabbling in Haskell
    -- a second round with relaying the last person
    Dierk is dabbling in Haskell
    Dierk is proficient in Groovy
    -- user input could influence which selection is 'current' 
    Dierk is dabbling in Haskell
-}

谢谢大家。