Монаду State
нельзя использовать для эмуляции классов. Он используется для моделирования состояния, которое «прилипает» к исполняемому коду, а не состояния, которое «является независимым» и находится в объектно-ориентированных классах.
Если вы не хотите переопределения и наследования методов, ближе всего к классам ООП в Haskell вы можете использовать записи со связанными функциями. Единственное отличие, о котором вы должны знать в этом случае, состоит в том, что все «методы класса» возвращают новые «объекты», они не изменяют старый «объект».
Например:
data A =
A
{ p :: Int
, q :: Bool
}
deriving (Show)
-- Your "A" constructor
newA :: A
newA = A { p = 0, q = False }
-- Your "y" method
y :: A -> (Int, A)
y a =
let newP = if q a then p a + 1 else p a - 1
newA = a { p = newP }
in (newP, newA)
-- Your "z" method
z :: A -> Bool
z = not . q
-- Your "main" procedure
main :: IO ()
main =
print (m', n, p a1'', q a1'', p a2, q a2)
where
a1 = newA
a2 = newA
(m, a1') = y a1
(temp, a1'') = y a1'
m' = m + temp
n = z a2
Эта программа печатает:
(-3,True,-2,False,0,False)
Обратите внимание, что мы должны были создать новые переменные для хранения новых версий m
и a1
(я просто добавлял '
в конце каждый раз). На Haskell нет изменяемых переменных на уровне языка, поэтому вам не следует пытаться использовать язык для этого.
В можно создавать изменяемые переменные с помощью ссылок ввода-вывода.
Обратите внимание, однако, что следующий код считается крайне плохим стилем кодирования среди Haskellers. Если бы я был учителем и у меня был ученик, который писал такой код, я бы не сдал проходной балл по заданию; если бы я использовал программиста на Haskell, который писал такой код, я бы подумал уволить его, если у него не было ОЧЕНЬ веской причины для написания такого кода.
import Data.IORef -- IO References
data A =
A
{ p :: IORef Int
, q :: IORef Bool
}
newA :: IO A
newA = do
p' <- newIORef 0
q' <- newIORef False
return $ A p' q'
y :: A -> IO Int
y a = do
q' <- readIORef $ q a
if q'
then modifyIORef (p a) (+ 1)
else modifyIORef (p a) (subtract 1)
readIORef $ p a
z :: A -> IO Bool
z = fmap not . readIORef . q
main :: IO ()
main = do
a1 <- newA
a2 <- newA
m <- newIORef =<< y a1
modifyIORef m . (+) =<< y a1
n <- z a2
m' <- readIORef m
pa1 <- readIORef $ p a1
qa1 <- readIORef $ q a1
pa2 <- readIORef $ p a2
qa2 <- readIORef $ q a2
print (m', n, pa1, qa1, pa2, qa2)
Эта программа делает то же самое, что и вышеприведенная программа, но с изменяемыми переменными. Опять же, не пишите такой код, за исключением очень редких обстоятельств.