Isoの使い方
lambda > import Control.Lens lambda > :t iso iso :: (Functor f, Profunctor p) => (s1 -> a1) -> (b1 -> t1) -> p a2 (f b2) -> p s2 (f t2)
lambda > let foo = iso (\((a,b),c)->(a,(b,c)) (\(a,(b,c))->((a,b),c))) lambda > let foo = iso (\((a,b),c)->(a,(b,c))) (\(a,(b,c))->((a,b),c)) lambda > ((1,2),3) ^. foo (1,(2,3)) lambda > (1,(2,3)) ^. from foo ((1,2),3)
lambda > import qualified Data.ByteString.Lazy as BL lambda > import qualified Data.ByteString as B lambda > let bar = iso BL.fromStrict BL.toStrict lambda > :set -XOverloadedStrings lambda > ("aaaa" :: B.ByteString) ^. bar "aaaa" lambda > :t ("aaaa" :: B.ByteString) ^. bar ("aaaa" :: B.ByteString) ^. bar :: BL.ByteString lambda > ("bbbb" :: BL.ByteString) ^. from bar "bbbb" lambda > :t ("bbbb" :: BL.ByteString) ^. from bar ("bbbb" :: BL.ByteString) ^. from bar :: B.ByteString lambda >
安全なhead
Prelude
にあるhead
は安全ではない
lambda > import Control.Lens.Cons lambda > head [1,2,3] 1 lambda > head [] *** Exception: Prelude.head: empty list
Safe
パッケージがある。
lambda > import Safe lambda > headMay [1,2,3] Just 1 lambda > headMay [] Nothing
Lens
でもおなじ機能がある。
lambda > import Control.Lens.Cons lambda > [1,2,3] ^? _head Just 1 lambda > [] ^? _head Nothing lambda >
Megaparsecで状態を扱う
paser
で状態を管理したい場合があります。
Attoparsec
attoparsec
では、ライブラリ自体で、parser
利用者側の状態を管理する仕組みがありません(間違っているかも)。なので、下記にあるとおり、StateT
を使うことになります。
haskell - Does Attoparsec support saving and modifying user state? - Stack Overflow
これでできるのですが、コンビネータにlift
が必要になり煩雑です。stateT
の内側にParser
を置くことになるので、外側でコンビネータを使う場合、lifting
が必要になるからです。
Megaparsec
Megaparsec
には、ライブラリ内に、状態を管理する仕組みが備わっています。サンプルは下記です。
Using Megaparsec with state monad
Megaparsec
の場合は、parser
の内側にstate
を置くことになります。外側でstate
の関数を使う場合、ユーザはlift
する必要はありません。
lifting
はライブラリ側で行ってくれています。具体的には、ParsecT
がMonadState
クラスのインスタンスになっているからです。ライブラリの該当箇所は下記です。
https://hackage.haskell.org/package/megaparsec-6.5.0/docs/src/Text-Megaparsec-Internal.html#line-237
実行するときは、外側のparser
をrunParserT
で剥がしてから、runStateT
を使う順になります。