我(试图)在Haskell中学习可变状态.为了简单起见,我定义了
data Car = Car { position :: Int }
Run Code Online (Sandbox Code Playgroud)
和
move :: Car -> Int -> Car
move c n = Car { position = n + (position c) }
Run Code Online (Sandbox Code Playgroud)
因此,move是一个"纯粹"的功能,代表了汽车向另一个汽车的过渡.
我认为我需要将一辆Car放在一个可变的变量中,这样我才能拥有当前位置(在对汽车进行一些动作之后).因此,我定义(我希望这是方法,否则纠正我)以下
type Parking = IORef Car -- holds a car
newParking :: Car -> IO Parking
newParking = newIORef
Run Code Online (Sandbox Code Playgroud)
以及琐碎getCar :: Parking -> IO Car和setCar :: Parking -> Car -> IO ()功能.
上面的代码似乎没问题.
问题:
我可以定义一个将任何纯函数move :: Car -> Int -> Car转换为Parking -> Int -> ()适用move于停放的Car 的函数并将其替换为新函数的函数吗?
结合接受的答案后的例子
import Data.IORef
import Control.Concurrent
-- -----------------------------------------------------
timeGoesBy place = do
moveTheCar place
threadDelay 1000000
timeGoesBy place
moveTheCar place = do
car <- getCar place
print $ getPos car
modifyCar place (move 7)
-- -----------------------------------------------------
main = do
place <- newParking (newCar 1000)
timeGoesBy place
print "end"
-- -----------------------------------------------------
type Parking = IORef Car -- mutable var for holding a car (the car may be replaced)
newParking :: Car -> IO Parking
newParking = newIORef
getCar :: Parking -> IO Car
getCar = readIORef
setCar :: Parking -> Car -> IO ()
setCar = writeIORef
modifyCar :: Parking -> (Car -> Car) -> IO ()
modifyCar = modifyIORef
-- -----------------------------------------------------
data Car = Car { position :: Int } -- Car data type ("pure")
-- create a car
newCar :: Int -> Car
newCar v = Car { position = v}
-- get the position of a car
getPos :: Car -> Int
getPos c = (position c)
-- move : transform a given car into a new "moved car"
move :: Int -> Car -> Car -- first the int so that we can curry (i.e. move 7)
move n car = Car { position = n + (position car) }
Run Code Online (Sandbox Code Playgroud)
Att*_*tic 11
或者,我们可以使用State Monad来避免/实际/可变性.
import Control.Monad.State
Run Code Online (Sandbox Code Playgroud)
定义汽车结构
data Car = Car { position :: Int } deriving Show
Run Code Online (Sandbox Code Playgroud)
启动状态,我们使用execState,给它一个保持状态和初始状态的函数(Car 0).
start :: Car
start = execState myState (Car 0)
Run Code Online (Sandbox Code Playgroud)
移动会移动你的车
move :: Int -> Car -> Car
move n c = c { position = n + position c }
Run Code Online (Sandbox Code Playgroud)
doStuff将帮助更容易将函数应用于State Monad'get'让我们获得当前状态(Car 0)和'put'将新版本置于状态.我们首先得到它,对它应用f,然后将其置于新状态.
doStuff :: MonadState a m => (a -> a) -> m ()
doStuff f = get >>= put . f
Run Code Online (Sandbox Code Playgroud)
这是状态函数,这里我们简单地用移动1调用doStuff并且它将修改我们的汽车(Car 0)以移动1 Int,因此新结果将是Car 1.然后我们说移动3并且它将改为车4
myState :: State Car ()
myState = do
doStuff $ move 1
doStuff $ move 3
Run Code Online (Sandbox Code Playgroud)
有了这个,我们可以运行start函数并接收我们修改后的初始值(Car 0).
是的你可以.
例如:
doCarStuff :: Parking -> (Car -> Car) -> IO ()
doCarStuff = modifyIORef
Run Code Online (Sandbox Code Playgroud)
如果重新排列move函数以使Car参数最后,那么你可以这样做
doCarStuff myParking (move 5)
Run Code Online (Sandbox Code Playgroud)
哪能做到你想要的.