feature image

2021年8月13日 | ブログ記事

モナドを使った設計方法を考察

こんにちは,夜間ならこんばんは.ゆきくらげです.最近,PureScriptでの開発にハマっているのですが,それにあたって大いに参考になっているのが halogen-realworld というプロジェクトです.

このプロジェクトは実用で使えそうな技術をギュッとまとめたすばらしい物であるのですが,今回はその中でも Capability というモジュール群と AppM というモジュールが,どのような設計思想に基づいて作られているのか考察していきたいと思います.実際のところ全部コメントで書かれてますが.

短い記事です.あと衝動で書いているのでコードが間違っているかもしれません.

型クラスの利点

例えば次のような型クラスを作ります.

class C a where
  func1 :: a -> Int

このような型クラスがあれば,次のように具体的な型がわからなくてもfunc1を利用する関数が型の整合性を保ったまま書けます.次に示すのは,クラスCのインスタンスである型の値を受け取り,それをfunc1に通して得た値をshowする関数です.

f :: forall a. C a => a -> String
f = show <<< func1

Cを持つクラスとして,Stringがあるでしょう.文字列の長さを返します.

instance C String where
  func1 str = length str

newtypeで型を作ってもいいですね.

newtype T = Cons String

instance C T where
  func1 (Cons str) = length str

さて,型クラスの利点として,具体的な実装とその利用が切り離せるという点があります.すなわち,fでは利用,具体的な実装はインスタンス定義の際に行うということです.

さらにこの仕組みを何重にも重ねられるでしょう.すなわち利用が新たな実装となって他で利用するという感じです.

このように,型クラスを中間地点のようにして,実装を同時に型の整合性を保ったまま進めることができます.良いですね.

どうも,モナドです

おはようございます.やはりモナドです.とは言っても上の実装をそのままモナドに当てはめるだけです.次のような実装ができます.

class Monad m <= C m where
  func2 :: Int -> m Int

こうすることでどのような利点があるでしょうか.

先程の実装ではある意味,型変数aに囚われています.(func1の型はa -> Intでした.)aが直接入らない機能は制約上導入することができません.この問題は上のmのように型変数を型構築子を表すものとして導入することで解決できます.

次に,モナドの表現力の高さです.世の中には様々な人が開発した様々なモナドがあります.これらをモナド変換子を使って合成することで,僕の考える最強のモナドを作ることができたりします.その僕の考える最強のモナドを使ってインスタンスの実装をすれば,僕の考える最強の実装が可能になります.良いですね.

利用の際は次のようにします.こちらはInt型の値を受け取り,func2で何らかの変換をして,showする関数です.

f :: forall m. C m => Int -> m String
f x = do
  y <- func1 x
  pure $ show y

func2の実装は様々です.簡単なエラー処理をしたい文脈でMaybeを使用したいとします.Int型の値を受け取り,0だったならNothing,それ以外なら100÷その値を返します.

instance C Maybe where
  func1 0 = Nothing
  func1 n = Just $ 100 `div` n

ランダム生成もできます.モナドなので.
どんな値を与えてもそれを無視して0から100までのランダムな値を返します.

instance C Effect where
  func1 _ = randomInt 0 100

組み合わせることもできます.モナドなので.
Int型の値を受け取り,0だったならNothing,それ以外ならランダムな値÷その値を返します.

instance C (MaybeT Effect) where
  func1 0 = MaybeT $ pure Nothing
  func1 n = do
    m <- lift $ randomInt 0 100
    pure $ m `div` n

newtypeで型を作ってもいいですね.

newtype T = Cons (MaybeT Effect)

instance C T where
  func1 0 = Cons $ MaybeT $ pure Nothing
  func1 n = Cons $ do
    m <- lift $ randomInt 0 100
    pure $ m `div` n

良いですね.

ずばり,コレが冒頭の Capability というモジュール群と AppM というモジュールがやっていることです.Capability ではクラス定義を行って,AppM では実装を行っています.

いかが

でしたか

?
yukikurage icon
この記事を書いた人
yukikurage

色々しますよ

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

ERC20トークンを用いた宝探しゲーム(真)の提案【アドベントカレンダー2018 10日目】 feature image
2018年11月3日
ERC20トークンを用いた宝探しゲーム(真)の提案【アドベントカレンダー2018 10日目】
Azon icon Azon
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2023年4月27日
Vulkanのデバイスドライバを自作してみた
kegra icon kegra
2024年4月14日
Spotifyのクライアントを自作しよう
d_etteiu8383 icon d_etteiu8383
2021年12月8日
C++ with JUCEでステレオパンを作ってみた【AdC2021 26日目】
liquid1224 icon liquid1224
2019年4月22日
アセンブリを読んでみよう【新歓ブログリレー2019 45日目】
eiya icon eiya
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記