IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

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 >

Haskell Patterns

Reader Pattern

The ReaderT Design Pattern

MichaelXavier.net - Enterprise Haskell Pattern: Lensed Reader

MichaelXavier.net - Enterprise Haskell Pattern: Lensed Reader

Has Pattern

The Has Type Class Pattern – Hacker Noon

Handle Pattern

jaspervdj - Haskell Design Patterns: The Handle Pattern

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はライブラリ側で行ってくれています。具体的には、ParsecTMonadStateクラスのインスタンスになっているからです。ライブラリの該当箇所は下記です。

https://hackage.haskell.org/package/megaparsec-6.5.0/docs/src/Text-Megaparsec-Internal.html#line-237

実行するときは、外側のparserrunParserTで剥がしてから、runStateTを使う順になります。