クラスをエミュレートするためにモナドを使用することはState
できません。実行中のコードに「固執する」状態をモデル化するために使用され、「独立した」オブジェクト指向クラスに存在する状態ではありません。
メソッドのオーバーライドと継承が必要ない場合、Haskell で OOP クラスに最も近い方法は、関数が関連付けられたレコードを使用することです。その場合に注意する必要がある唯一の違いは、すべての「クラス メソッド」が新しい「オブジェクト」を返し、古い「オブジェクト」を変更しないことです。
例えば:
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
新しいバージョンのandを格納するために新しい変数を作成する必要があることに注意してください(毎回最後a1
に追加しました)。'
Haskell には言語レベルの変更可能な変数がないため、そのために言語を使用しようとするべきではありません。
IO 参照を使用して可変変数を作成することができます。
ただし、次のコードは、Haskeller の間で非常に悪いコーディング スタイルと見なされていることに注意してください。もし私が教師で、このようなコードを書いた生徒がいたら、私は課題で合格点を与えません。もし私がこのようなコードを書いた 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)
このプログラムは、上記のプログラムと同じことを行いますが、可変変数を使用します。繰り返しますが、非常にまれな状況を除いて、このようなコードを記述しないでください。