Haskell 中对不可变数据的可变引用
Mutable references to immutable data in Haskell
我想跟踪一系列不可变值中的一个 "current" 值。
在不为每个新值引入新引用的情况下,在 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
我不确定问题的真正含义。发布的例子
可以重写为使用 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
,或者如果在 ST
monad 中则可以使用 STRef
。但在这种情况下,你必须在一些允许这些引用类型的 monad 中工作。相比之下,StateT Person m
适用于任何 monad m
.
只是总结并向像我这样的其他 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
-}
谢谢大家
我想跟踪一系列不可变值中的一个 "current" 值。 在不为每个新值引入新引用的情况下,在 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
我不确定问题的真正含义。发布的例子
可以重写为使用 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
,或者如果在 ST
monad 中则可以使用 STRef
。但在这种情况下,你必须在一些允许这些引用类型的 monad 中工作。相比之下,StateT Person m
适用于任何 monad m
.
只是总结并向像我这样的其他 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
-}
谢谢大家