価値観の中monads、入れ子構造データ構造
-
18-09-2019 - |
質問
のか、ウプログラムしているのか教えてくださいデータタイプをクライアントに通知するようなもの:
IO [ IO (Int, String, Int) ]
, やIO [ (Int, String, IO Int) ]
, や[ (Int, String, IO Int) ]
しかし純粋な機能がするべきだ [ (Int, String, Int) ]
.この思いclumsily削除の値からIO monadまったようなものIO[(Int,string,Int)]、それらのIO monad)の純ます。あることは容易ではありませ事前に定義されないということですよね...?このリフト全体のデータ構造へのmonad、すべての 内部 種類純粋なのか。(それはすごく便利!)
解決
ご利用可能に liftM*
機能から ます。Monad モジュール、または liftA*
機能 applicatives.
liftM
できるリフト、純粋な機能を内での作業Monad、例えば:
ghci> let s = return "Hello" :: IO String
ghci> liftM reverse s
"olleH"
このような手動で作ろう"s >>= \x -> return (reverse x)
"で取り扱います。
こんなお手伝をさせていただき [(String, Int, IO Int)]
たとえば、純粋に機能する [(String, Int, Int)]
.以降、第三元素のタプルかな Int
.
その場合っ果に関数を書く [(String, Int, IO Int)] -> IO [(String, Int, Int)]
この解除の純粋な機能です。
これは最も一般的な関数ようこ:
conv :: Monad m => (f (m a) -> m (f a)) -> [f (m a)] -> m [f a]
conv f = sequence . map f
ることができない:
liftTrd :: Monad m => (a, b, m c) -> m (a, b, c)
liftTrd (x, y, mz) = mz >>= \z -> return (x, y, z)
conv liftTrd [("hi", 4, return 2)] :: IO [(String, Int, Int)]
この機能は使用できました場合にのみ動作の単一monadことになる深いところから、タイプです。が複数ある場合には、私は考えるべきかについて考えることになるでのインターネットにアクセスで働くと見えてくるのであるかのように映像化しているのでより簡単になります。
他のヒント
と呼ばれるreduce
以下の解決のためにまず、いくつかの使用例(あなたがより良い名前を示唆していない場合):
> reduce [(["ab", "c"], "12")] :: [(String, String)]
[("ab","12"),("c","12")]
> reduce [(["ab", "c"], "12")] :: [(Char, Char)]
[('a','1'),('a','2'),('b','1'),('b','2'),('c','1'),('c','2')]
> reduce [("ab", "12"), ("cd", "3")] :: [(Char, Char)]
[('a','1'),('a','2'),('b','1'),('b','2'),('c','3'),('d','3')]
あなたの例もそれで解決されます:
complexReduce :: Monad m => m (m (a, b, m [m (c, m d)])) -> m (a, b, [(c, d)])
complexReduce = reduce
そしてreduce
の実装ます:
{-# LANGUAGE FlexibleContexts, FlexibleInstances, IncoherentInstances, MultiParamTypeClasses, UndecidableInstances #-}
import Control.Monad
-- reduce reduces types to simpler types,
-- when the reduction is in one of the following forms:
-- * make a Monad disappear, like join
-- * move a Monad out, like sequence
-- the whole magic of Reduce is all in its instances
class Reduce s d where
reduce :: s -> d
-- Box is used only for DRY in Reduce instance definitions.
-- Without it we, a Reduce instance would need
-- to be tripled for each variable:
-- Once for a pure value, once for a monadic value,
-- and once for a reducable value
newtype Box a = Box { runBox :: a }
instance Monad m => Reduce (Box a) (m a) where
reduce = return . runBox
instance Reduce a b => Reduce (Box a) b where
reduce = reduce . runBox
redBox :: Reduce (Box a) b => a -> b
redBox = reduce . Box
-- we can join
instance (Monad m
, Reduce (Box a) (m b)
) => Reduce (m a) (m b) where
reduce = join . liftM redBox
-- we can sequence
-- * instance isnt "Reduce [a] (m [b])" so type is always reduced,
-- and thus we avoid overlapping instances.
-- * we cant make it general for any Traversable because then
-- the type system wont find the right patterns.
instance (Monad m
, Reduce (Box a) (m b)
) => Reduce (m [a]) (m [b]) where
reduce = join . liftM (sequence . fmap redBox)
instance (Monad m
, Reduce (Box a) (m c)
, Reduce (Box b) (m d)
) => Reduce (a, b) (m (c, d)) where
reduce (a, b) = liftM2 (,) (redBox a) (redBox b)
instance (Monad m
, Reduce (Box a) (m d)
, Reduce (Box b) (m e)
, Reduce (Box c) (m f)
) => Reduce (a, b, c) (m (d, e, f)) where
reduce (a, b, c) =
liftM3 (,,) (redBox a) (redBox b) (redBox c)