通常、例として「コレクションのような」モナドから始めると、モナドの内容を把握しやすくなります。2 点の距離を計算するとします。
data Point = Point Double Double
distance :: Point -> Point -> Double
distance p1 p2 = undefined
今、あなたは特定の文脈を持っているかもしれません。たとえば、ポイントの 1 つが、いくつかの境界 (画面上など) の外にあるため、「不正」である可能性があります。Maybe
したがって、既存の計算をモナドでラップします。
distance :: Maybe Point -> Maybe Point -> Maybe Double
distance p1 p2 = undefined
まったく同じ計算を行いますが、「結果がない」可能性があるという追加機能があります (としてエンコードされますNothing
)。
または、「可能な」点の 2 つのグループがあり、相互の距離が必要な場合 (たとえば、後で最短の接続を使用する場合)。リストモナドはあなたの「文脈」です:
distance :: [Point] -> [Point] -> [Double]
distance p1 p2 = undefined
または、ポイントがユーザーによって入力され、計算が「非決定論的」になります (変化する可能性のある外界のものに依存するという意味で) 場合、IO
モナドはあなたの友達です:
distance :: IO Point -> IO Point -> IO Double
distance p1 p2 = undefined
計算は常に同じままですが、たまたま特定の「コンテキスト」で行われるため、いくつかの有用な側面 (失敗、多値、非決定論) が追加されます。これらのコンテキストを組み合わせることもできます (モナド変換子)。
上記の定義を統一し、どのモナドでも機能する定義を書くことができます:
distance :: Monad m => m Point -> m Point -> m Double
distance p1 p2 = do
Point x1 y1 <- p1
Point x2 y2 <- p2
return $ sqrt ((x1-x2)^2 + (y1-y2)^2)
これは、計算が実際のモナドから本当に独立していることを証明し、「x は (-side) y モナドで計算される」という定式化につながります。