Inside Parsec


Stream s m t クラス

Stream s m t クラス

Stream s m t クラスはただ一つの多相関数 uncons を持つクラスだ。

class (Monad m) => Stream s m t | s -> t where
    uncons :: s -> m (Maybe (t,s))

コメントによると s は stream type, m は underlying monad, t は token type の意味だ。

s が String 型の場合、uncons s の値は、m (head s, tail s) と同じものになる。

Prelude> :m Text.Parsec
Prelude Text.Parsec> uncons "hello"
Just ('h',"ello")

uncons の値のタイプを :t コマンドでみると、Just ('h', "ello") ではなくモナド m にラッピングされていることが分かる。

Prelude Text.Parsec> :t uncons "hello"
uncons "hello" :: Monad m => m (Maybe (Char, [Char]))

uncons 多相関数

s には文字列以外に uncons 関数をもつ Data.ByteString などのリストではない文字列も使うことができる。Text.Parsec.Prim では次のようなモジュールがインストールされている。

import qualified Data.ByteString.Lazy.Char8 as CL
import qualified Data.ByteString.Char8 as C
import Data.Typeable ( Typeable )
import qualified Data.Text as Text
import qualified Data.Text.Lazy as TextL

これらのデータ構造は次のように Stream クラスのインスタンスに宣言されている。

class (Monad m) => Stream s m t | s -> t where
    uncons :: s -> m (Maybe (t,s))
instance (Monad m) => Stream [tok] m tok where
    uncons []     = return $ Nothing
    uncons (t:ts) = return $ Just (t,ts)
    {-# INLINE uncons #-}
instance (Monad m) => Stream CL.ByteString m Char where
    uncons = return . CL.uncons
instance (Monad m) => Stream C.ByteString m Char where
    uncons = return . C.uncons
instance (Monad m) => Stream Text.Text m Char where
    uncons = return . Text.uncons
    {-# INLINE uncons #-}
instance (Monad m) => Stream TextL.Text m Char where
    uncons = return . TextL.uncons
    {-# INLINE uncons #-}

これらのことは、uncons 関数が String 型だけでなく、ByteString 型や、Text 型、トークンのリストにまで適用できることが分かる。これは、パーサが入力文字列として String 型以外のデータにもそれほどの変更もなく使えることを意味している。

しかし、パーサモナドのコードの解析のためには Stream s m t の s は文字列だと思っても問題ないので、コードのなかに uncons 関数が出てきたら String 型に使われているのだと考えることにする。