Help us understand the problem. What is going on with this article?

スーパーモナドについて

More than 1 year has passed since last update.

この記事は Haskell Advent Calendar 2017 (その1) の 10 日目の記事です。

スーパーモナド (supermonad) という気になる概念を見かけたので、ちょっと調べてみました。

スーパーモナドとは

世の中には、私たちがよく知っている普通のモナド1 以外にも、モナド的概念がいろいろと存在します。たとえば、こんなのがあります。2

スーパーモナドは、こうしたモナド的概念をすべて包含する概念 として提案されました。

スーパーモナドを提案した論文が、次の論文です。実装の話や、Agda で定式化した話なんかも載っています。

  • J. Bracker and H. Nilsson. Supermonads: one notion to bind them all. In Proceedings of the 9th International Symposium on Haskell, pages 258-269, 2016.

実装は、supermonad パッケージ として提供されています。

スーパーモナドの定義

まずは普通のモナドのおさらいから。普通のモナドは次のように定義されているのでした。3

class Monad m where
  return :: a -> m a
  (>>=) :: m a -> (a -> m b) -> m b

指標付きモナドの定義も覗いてみます。指標付きモナドは次のように定義されています。3

class IxMonad m where
  ireturn :: a -> m i i a
  (>>>=) :: m i j a -> (a -> m j k b) -> m i k b 

そして、これらを一般化した概念であるスーパーモナドは、次のように定義されています。3

class Return m where
  return :: a -> m a

class Bind m n p where
  (>>=) :: m a -> (a -> n b) -> p b

クラスが ReturnBind の 2 つに分かれているのが特徴的です。また、バインド演算 (>>=) も一般化されています。

スーパーモナドが普通のモナドの一般化であることは明らかです。スーパーモナドが指標付きモナドの一般化になっていることも、次のような対応がとれることからわかります。

  • Return クラスの mIxMonad クラスの m i i に対応
  • Bind クラスの m, n, p がそれぞれ IxMonad クラスの m i j, m j k, m i k に対応

なお、普通のモナドがモナド則を満たす必要があるように、スーパーモナドもスーパーモナド則を満たす必要があります。スーパーモナド則は、普通のモナド則の自然な一般化です。

スーパーモナドを使ってみる

スーパーモナドを使うには、supermonad パッケージ をインストールします。モジュールファイルの冒頭には、最低限、次のような宣言を書く必要があります。

{-# LANGUAGE RebindableSyntax #-}
{-# OPTIONS_GHC -fplugin Control.Supermonad.Plugin #-}

import Control.Supermonad.Prelude

また、コンパイルは GHC に -dynamic オプションを指定して行います。4

ここから先は、Maybe モナドと IxState モナドをスーパーモナドとして扱うプログラムの例を紹介します。

例 1: Maybe モナド

Maybe モナドのような普通のモナドについては、Control.Supermonad.Prelude 内に次のような instance 宣言が書かれています。

import qualified Prelude as P

instance Bind Maybe Maybe Maybe where
  (>>=) = (P.>>=)

instance Return Maybe where
  return = P.return

そのため、Maybe モナドなどをスーパーモナドとして扱う際は、私たちは instance 宣言を書く必要はありません。

例として、(スーパーモナドを使わない)次のプログラムを、スーパーモナドで書き直してみます。

foo :: Maybe Int -> Maybe String
foo x = x >>= (\x -> Just (succ x))
          >>= (\x -> Just (show x))

main = print $ foo (Just 0)

これをスーパーモナドで書き直すと、次のようになります。

{-# LANGUAGE RebindableSyntax #-}
{-# OPTIONS_GHC -fplugin Control.Supermonad.Plugin #-}

import Prelude hiding ((>>=), return)
import Control.Supermonad.Prelude

foo :: Maybe Int -> Maybe String
foo x = x >>= (\x -> Just (succ x))
          >>= (\x -> Just (show x))

main = print $ foo (Just 0)
$ runghc -dynamic test.hs
[SM] Invoke (super) plugin...
[SM] Unification solve constraints...
Just "1"

例 2: IxState モナド

指標付きモナドである IxState をスーパーモナドとして扱ってみます。IxState モナドは、indexed-extras パッケージ に定義されています。

まずは、IxState モナドを普通に使うバージョンから。このプログラムをコンパイルするには、indexed-extras パッケージ が必要です。

import Control.Monad.Indexed
import Control.Monad.Indexed.State

foo :: IxState Int String ()
foo = imodify succ >>>= \_ -> imodify show

main = print $ runIxState foo 0

スーパーモナドを使うようにこれを書き換えると、次のようになります。

{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_GHC -fplugin Control.Supermonad.Plugin #-}

import Prelude hiding ((>>=), return)
import Control.Supermonad.Prelude
import Control.Monad.Indexed
import Control.Monad.Indexed.State

instance Bind (IxState i j) (IxState j k) (IxState i k) where
  (>>=) = (>>>=)

instance Return (IxState i i) where
  return = ireturn

foo :: IxState Int String ()
foo = imodify succ >>= \_ -> imodify show

main = print $ runIxState foo 0
$ runghc -dynamic test1.hs
[SM] Invoke (super) plugin...
[SM] Unification solve constraints...
((),"1")

instance 宣言を書くことで、指標モナドもスーパーモナドとして扱えることが確認できました。


  1. ここでは、Haskell の Control.Monad に定義されているモナドのことを「普通のモナド」と呼んでいます。Haskell のモナドはクライスリトリプルがベースだから云々、というツッコミはご容赦を…。 

  2. [Bracker and Nilsson 2016] で紹介されているものを載せています。 

  3. 実際の定義とは少し異なります。 

  4. 詳しいことは supermonad パッケージの Readme を見てください。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away