Pregunta

Supongamos que en un programa Haskell tengo algunos datos cuyo tipo es algo como:

  • IO [ IO (Int, String, Int) ], o
  • IO [ (Int, String, IO Int) ], o
  • [ (Int, String, IO Int) ]

pero tengo funciones puras que deben operar en [ (Int, String, Int) ]. Parece que tendría que quitar con torpeza los valores dentro de la mónada IO, hasta que llegué algo así como IO [(int, String, int)] y luego (desde el interior de la mónada IO) se aplican las funciones puras. No hay manera predefinida fácil de hacer esto, ¿verdad? Algo que podría levantar una estructura de datos entera en una mónada, convirtiendo todos los dentro de tipos en tipos puros? (Eso sería muy conveniente!)

¿Fue útil?

Solución

Se puede usar la función de la liftM* de Control módulo .Monad , o las funciones liftA* para aplicativos .

liftM le permite levantar una función pura a trabajar dentro de una mónada, por ejemplo:.

ghci> let s = return "Hello" :: IO String
ghci> liftM reverse s
"olleH"

De esta manera usted no tiene que escribir manualmente cosas como "s >>= \x -> return (reverse x)" en todas partes.

A pesar de que esto no le ayudará con su ejemplo [(String, Int, IO Int)], si la función pura que tiene acuerdos con un [(String, Int, Int)]. Dado que el tercer elemento de la tupla en realidad no es un Int.

En ese caso, me gustaría sugerir a escribir primero un [(String, Int, IO Int)] -> IO [(String, Int, Int)] función y que se aplican a la función pura levantado.


Esta es la función más general que podía llegar a hacer esto:

conv :: Monad m => (f (m a) -> m (f a)) -> [f (m a)] -> m [f a]
conv f = sequence . map f

Se le puede llamar así:

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

Esta función sólo funcionará si tiene una sola mónada que está en algún lugar profundo en un tipo. Si usted tiene múltiples, creo que realmente debería estar pensando en el tipo de su trabajo con y ver si no puede hacerlo más sencillo.

Otros consejos

En primer lugar algunos ejemplos de uso de la solución por debajo de llamada reduce (a menos que usted sugiere un nombre mejor):

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

Su ejemplo también se resuelve con él:

complexReduce :: Monad m => m (m (a, b, m [m (c, m d)])) -> m (a, b, [(c, d)])
complexReduce = reduce

Y la ejecución de 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)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top