Значения в монадах, вложенных в структурах данных?

Одна интересная вещь, которую я изучил, состоит в том, что различные части платформы и языка C# были записаны в разное время, следовательно несоответствия. Например, сама платформа нарушает много правил FxCop, потому что правила существовали не все, когда платформа была записана.

кроме того, оператор использования был предназначен для delinieating "объемы" а не специально для расположения ресурсов. Это было записано после оператора блокировки. Eric Gunnerson когда-то упомянул что-то вроде того, что, если оператор использования был на первом месте, они могли бы иметь не нужный для записи оператора блокировки (хотя, кто знает, возможно, они имели бы так или иначе), потому что оператор использования, возможно, был достаточен.

5
задан Jay 21 July 2009 в 18:02
поделиться

2 ответа

Вы можете использовать функцию liftM * из модуля Control.Monad или функции 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)]

Эта функция будет работать, только если у вас есть единственная монада, которая находится где-то глубоко в типе. Если у вас их несколько, я думаю, вам действительно стоит подумать о типе, с которым вы работаете, и посмотреть, не сможете ли вы сделать это проще.

6
ответ дан 13 December 2019 в 22:13
поделиться

Сначала несколько примеров использования решения ниже под названием 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)
4
ответ дан 13 December 2019 в 22:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: