Haskell中一般函数变换的一个案例

cib*_*en1 3 haskell

我(试图)在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 CarsetCar :: 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).


Mat*_*hid 8

是的你可以.

例如:

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)

哪能做到你想要的.

  • 因为你可以使用部分应用,所以`Car`来到最后总是一场胜利,即使是`T1 - > T2 - > Tn - > Car - > Car`. (2认同)