Haskell 单子函数映射

Haskell 单子函数映射,haskell,monads,Haskell,Monads,我有以下代码: type Mapper a k v = a -> [(k,v)] type Reducer k v = k -> [v] -> [v] mapReduce :: Ord k => Mapper a k v -> Reducer k v -> [a] -> [(k,[v])] mapReduce m r = reduce r . shuffleKeys . concatMap (map listifyVal . m) where l

我有以下代码:

type Mapper a k v = a -> [(k,v)]
type Reducer k v = k -> [v] -> [v]


mapReduce :: Ord k => Mapper a k v -> Reducer k v -> [a] -> [(k,[v])]
mapReduce m r = reduce r . shuffleKeys . concatMap (map listifyVal . m)
  where listifyVal (k,v) = (k,[v])
        shuffleKeys = Map.fromListWith (++)
        reduce r = Map.toList . Map.mapWithKey r
我得到了一个
Monad
类型

   type MapperM m a k v = a -> m [(k,v)]
   type ReducerM m k v = k -> [v] -> m [v]
我需要将现有代码转换为使用
Monad

mapReduceM :: (Ord k, Monad m) => MapperM m a k v -> ReducerM m k v -> [a] -> m [(k,[v])]
我一直在转换表达式
concatMap(map listifyVal.m)
我编写了这个helper函数

listifyValM:: (Ord k, Monad m) => m(k,v) ->  m(k,[v])                        
listifyValM mkv = do
                    (k,v) <- mkv
                    return (k,[v])
但是我甚至不能让这个(非常部分的)简单的事情作为一个开始。 我得到:


我可能在
monad
上缺少一些关于
映射的基本信息

从头开始编写
mapReduceM
可能比尝试机械地转换
mapReduce
更容易。你想要:

mapReduceM :: (Monad m, Ord k) => MapperM m a k v -> ReducerM m k v -> [a] -> m [(k,[v])]
mapReduceM m r as = ...
因此,让我们假设以下类型的参数并尝试构建函数:

m :: a -> m [(k,v)]
r :: k -> [v] -> m [v]
as :: [a]
combineByKey :: [(K, V)] -> Map K [V]
combineByKey = Map.fromListWith (++) . map (\(k,v) -> (k,[v]))
创建一个使用具体类型的骨架文件会很有帮助,这样我们就可以在GHCi中键入检查表达式:

import Data.Map (Map)
import qualified Data.Map as Map

data A
data K = K deriving (Eq, Ord)
data V
data M a
instance Functor M
instance Applicative M
instance Monad M

m :: A -> M [(K,V)]
m = undefined
r :: K -> [V] -> M [V]
r = undefined
as :: [A]
as = undefined
显然,我们需要将
的元素作为
传递给唯一可以接受它们的可用函数,即
m
。将一元函数
a->mb
应用于列表
[a]
的标准方法是使用
mapM
遍历执行遍历。(在过去,前者用于monad,后者用于applicative,但现在所有monad都是applicative,
traverse
是首选。)具体来说,加载此骨架后,我们可以在GHCi中键入检查此表达式:

> :t traverse m as
traverse m as :: M [[(K, V)]]
要折叠列表列表,我们需要在“monad”下应用
concat
。幸运的是,单子是函子,因此
fmap
(或其同义词
()
)可以做到:

> :t concat <$> traverse m as
concat <$> traverse m as :: M [(K, V)]
在单子下:

> :t combineByKey . concat <$> traverse m as
combineByKey . concat <$> traverse m as :: M (Map K [V])
> Map.toList <$> (sequence $ Map.mapWithKey r mymap)
Map.toList <$> (sequence $ Map.mapWithKey r mymap) :: M [(K, [V])]
在这里,
do
notation可以帮助我们完成这个过程:

do mymap <- combineByKey . concat <$> traverse m as
   ...
如果我们使用
mapWithKey
应用减速器:

> Map.mapWithKey r mymap
Map.mapWithKey r mymap :: Map K (M [V])
我们有一个一元元素的结构。因为
映射
是可遍历的,所以我们可以使用
序列
将monad拉到外部:

> sequence $ Map.mapWithKey r mymap
sequence $ Map.mapWithKey r mymap :: M (Map K [V])
这几乎就是我们希望从
mapReduceM
得到的返回值。我们只需将地图更改为单子下的列表:

> :t combineByKey . concat <$> traverse m as
combineByKey . concat <$> traverse m as :: M (Map K [V])
> Map.toList <$> (sequence $ Map.mapWithKey r mymap)
Map.toList <$> (sequence $ Map.mapWithKey r mymap) :: M [(K, [V])]
可以将其转换为无点形式,如下所示:

import Control.Monad

mapReduceM :: (Monad m, Ord k) => MapperM m a k v -> ReducerM m k v -> [a] -> m [(k,[v])]
mapReduceM m r =
  traverse m >=>                       -- apply the mapper
  pure . combineByKey . concat >=>     -- combine keys
  sequence . Map.mapWithKey r >=>      -- reduce
  pure . Map.toList                    -- convert to list

  where combineByKey :: (Ord k) => [(k, v)] -> Map k [v]
        combineByKey = Map.fromListWith (++) . map (\(k,v) -> (k,[v]))

从头开始编写
mapReduceM
可能比尝试机械地转换
mapReduce
更容易。你想要:

mapReduceM :: (Monad m, Ord k) => MapperM m a k v -> ReducerM m k v -> [a] -> m [(k,[v])]
mapReduceM m r as = ...
因此,让我们假设以下类型的参数并尝试构建函数:

m :: a -> m [(k,v)]
r :: k -> [v] -> m [v]
as :: [a]
combineByKey :: [(K, V)] -> Map K [V]
combineByKey = Map.fromListWith (++) . map (\(k,v) -> (k,[v]))
创建一个使用具体类型的骨架文件会很有帮助,这样我们就可以在GHCi中键入检查表达式:

import Data.Map (Map)
import qualified Data.Map as Map

data A
data K = K deriving (Eq, Ord)
data V
data M a
instance Functor M
instance Applicative M
instance Monad M

m :: A -> M [(K,V)]
m = undefined
r :: K -> [V] -> M [V]
r = undefined
as :: [A]
as = undefined
显然,我们需要将
的元素作为
传递给唯一可以接受它们的可用函数,即
m
。将一元函数
a->mb
应用于列表
[a]
的标准方法是使用
mapM
遍历执行遍历。(在过去,前者用于monad,后者用于applicative,但现在所有monad都是applicative,
traverse
是首选。)具体来说,加载此骨架后,我们可以在GHCi中键入检查此表达式:

> :t traverse m as
traverse m as :: M [[(K, V)]]
要折叠列表列表,我们需要在“monad”下应用
concat
。幸运的是,单子是函子,因此
fmap
(或其同义词
()
)可以做到:

> :t concat <$> traverse m as
concat <$> traverse m as :: M [(K, V)]
在单子下:

> :t combineByKey . concat <$> traverse m as
combineByKey . concat <$> traverse m as :: M (Map K [V])
> Map.toList <$> (sequence $ Map.mapWithKey r mymap)
Map.toList <$> (sequence $ Map.mapWithKey r mymap) :: M [(K, [V])]
在这里,
do
notation可以帮助我们完成这个过程:

do mymap <- combineByKey . concat <$> traverse m as
   ...
如果我们使用
mapWithKey
应用减速器:

> Map.mapWithKey r mymap
Map.mapWithKey r mymap :: Map K (M [V])
我们有一个一元元素的结构。因为
映射
是可遍历的,所以我们可以使用
序列
将monad拉到外部:

> sequence $ Map.mapWithKey r mymap
sequence $ Map.mapWithKey r mymap :: M (Map K [V])
这几乎就是我们希望从
mapReduceM
得到的返回值。我们只需将地图更改为单子下的列表:

> :t combineByKey . concat <$> traverse m as
combineByKey . concat <$> traverse m as :: M (Map K [V])
> Map.toList <$> (sequence $ Map.mapWithKey r mymap)
Map.toList <$> (sequence $ Map.mapWithKey r mymap) :: M [(K, [V])]
可以将其转换为无点形式,如下所示:

import Control.Monad

mapReduceM :: (Monad m, Ord k) => MapperM m a k v -> ReducerM m k v -> [a] -> m [(k,[v])]
mapReduceM m r =
  traverse m >=>                       -- apply the mapper
  pure . combineByKey . concat >=>     -- combine keys
  sequence . Map.mapWithKey r >=>      -- reduce
  pure . Map.toList                    -- convert to list

  where combineByKey :: (Ord k) => [(k, v)] -> Map k [v]
        combineByKey = Map.fromListWith (++) . map (\(k,v) -> (k,[v]))

您编写了
map listifyValM
,但由于您没有传递要映射的列表,因此它不知道需要“创建”问题函数的
m
k
v
。它与Monad本身无关。谁能给我一个如何处理这个问题的提示吗?对于您遇到的错误,只需给您的helper函数一个带有类约束的类型签名:
step0::(Monad m,Ord k)=>[m(k,v)]->[m(k,[v])
您编写的
map listifyValM
,但是,由于您没有将列表传递给映射,因此它不知道什么是
m
k
v
,它需要“创建”出问题的函数。它与Monad本身无关。有谁能给我一个如何处理这个问题的提示吗?对于您遇到的错误,只需给您的helper函数一个带有类约束的类型签名:
step0::(Monad m,Ord k)=>[m(k,v)]->[m(k,[v])]