문제

Haskell 프로그램에는 유형이 다음과 같은 데이터가 있다고 가정합니다.

  • IO [ IO (Int, String, Int) ], 또는
  • IO [ (Int, String, IO Int) ], 또는
  • [ (Int, String, IO Int) ]

그러나 작동해야 할 순수한 기능이 있습니다 [ (Int, String, Int) ]. io [(int, string, int)]와 같은 것을 얻을 때까지 IO 모나드에서 내부 값을 서투르게 제거하고 (IO Monad 내부에서) 순수한 기능을 적용해야 할 것 같습니다. 이 작업을 수행 할 수있는 쉽게 사전 정의 된 방법이 없다고 생각합니다. 전체 데이터 구조를 모나드로 들어 올려 모든 것을 돌리는 것 내부에 순수한 유형으로 유형? (매우 편리 할 것입니다!)

도움이 되었습니까?

해결책

당신은 사용할 수 있습니다 liftM* 기능 컨트롤. 모나드 모듈 또는 liftA* 기능 적용.

liftM 순수한 기능을 들어 올리면 모나드 내부에서 작동합니다.

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)]

이 기능은 유형의 깊은 곳에있는 단일 모나드가있는 경우에만 작동합니다. 여러분이 다수가 있다면, 나는 당신이 당신과 함께 일하는 유형에 대해 실제로 생각하고 더 간단하게 만들 수 없는지 확인해야한다고 생각합니다.

다른 팁

먼저 아래 솔루션에 대한 일부 사용 예제가 호출되었습니다 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)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top