Suppose that in a Haskell program I have some data whose type is something like:

假設在Haskell程序中我有一些類型如下的數據:

`IO [ IO (Int, String, Int) ]`

, or`IO [ (Int, String, IO Int) ]`

, or`[ (Int, String, IO Int) ]`

IO [IO(Int,String,Int)]或

IO [(Int,String,IO Int)]或

[(Int,String,IO Int)]

but I have pure functions that should operate on `[ (Int, String, Int) ]`

. It seems that I'd have to clumsily remove the inside values from the IO monad, until I got something like IO [ (Int, string, Int) ] and then (from inside the IO monad) apply the pure functions. There is no easy pre-defined way to do this, I suppose? Something that would lift a whole data structure into a monad, turning all *inside* types into pure types? (That would be very convenient!)

但我有純函數,應該在[(Int,String,Int)]上運行。似乎我必須笨拙地從IO monad中刪除內部值,直到我得到類似IO [(Int,string,Int)]然后(從IO monad內部)應用純函數。我想,沒有簡單的預定義方法可以做到這一點?將整個數據結構提升為monad的東西,將所有內部類型轉換為純類型? (那會很方便!)

You could use the `liftM*`

function from the Control.Monad module, or the `liftA*`

functions for applicatives.

您可以使用Control.Monad模塊中的liftM *功能,或者應用程序的liftA *功能。

`liftM`

allows you to lift a pure function to work inside a Monad, e.g.:

liftM允許你舉起一個純函數在Monad中工作,例如:

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

This way you don't have to manually write things like "`s >>= \x -> return (reverse x)`

" everywhere.

這樣你就不必手動編寫像“s >> = \ x - > return(reverse x)”這樣的東西。

Although, this won't help you with your `[(String, Int, IO Int)]`

example, if the pure function you have deals with a `[(String, Int, Int)]`

. Since the third element in the tuple really isn't an `Int`

.

雖然,這對您的[(String,Int,IO Int)]示例沒有幫助,如果您使用的純函數處理[(String,Int,Int)]。由於元組中的第三個元素確實不是Int。

In that case I'd suggest to first write a function `[(String, Int, IO Int)] -> IO [(String, Int, Int)]`

and that apply the lifted pure function.

在那種情況下,我建議先寫一個函數[(String,Int,IO Int)] - > IO [(String,Int,Int)]並應用提升的純函數。

This is the most general function I could come up with to do this:

這是我能做到的最常用的功能:

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

You can call it like so:

你可以像這樣調用它:

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

This function will only work if you have a single monad that's somewhere deep in a type. If you have multiple, I think you should really be thinking about the type your working in with and see if you can't make it simpler.

只有當你有一個類型很深的monad時,這個函數才有效。如果你有多個,我認為你應該考慮你所使用的類型,看看你是否能夠簡化它。

First some usage example for the solution below called `reduce`

(unless you suggest a better name):

首先,下面的解決方案的一些用法示例稱為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')]
```

Your example is also solved with it:

你的例子也用它來解決:

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

And the implementation of `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)
```

本站翻译的文章，版权归属于本站，未经许可禁止转摘，转摘请注明本文地址：https://www.itdaan.com/blog/2009/07/21/725c3134147486ab3e4683f726ad1286.html。

Rounding decimals in nested data structures in Python
Javascript: Access nested values in JSON data using dynamic variable names
How to push data from a nested array, inside an array of objects, to a new array? while maintaining its nested structure
Persistent and Transient Data Structures in Clojure
Performance of Large Data Structures in Python
2016 UESTC Training for Data Structures
2016 UESTC Training for Data Structures (1)
Efficiency of operations on R data structures
Data Structures, Algorithms, & Applications in Java
How to be good in Data Structures and Analysis?