2014-11-01 3 views
2

Так что есть функция в Haskell, что я упростил с целью задать этот вопрос:Подъемные функции с другой функцией в качестве аргумента в Haskell

import Data.Foldable 
import Data.Set 

myFn :: Int -> Set Int 
myFn a 
    | a <= 0 = singleton 1 
    | otherwise = foldMap helper (myFn (a - 1)) 

helper :: Int -> Set Int 
helper a = insert (a + 2) (singleton a) 

main :: IO() 
main = print . Data.Set.toList $ myFn 5 

Я хочу иметь myFn «ю.ш. зависимость от helper быть положенным в Reader, так как инверсия управления позволяет мне переключаться реализации в моих тестах:

import Control.Monad.Reader 
import Data.Foldable 
import Data.Set 

data MyEnv = MyEnv { helper' :: Int -> Set Int } 
type MyReader = Reader MyEnv 

myFn :: Int -> MyReader (Set Int) 
myFn a 
    | a <= 0 = return $ singleton 1 
    | otherwise = do 
     myFn' <- myFn (a - 1) 
     helper'' <- asks helper' 
     return (foldMap helper'' myFn') 

helper :: Int -> Set Int 
helper a = insert (a + 2) (singleton a) 

main :: IO() 
main = 
    let 
    myEnv = MyEnv helper 
    in 
    print . Data.Set.toList $ runReader (myFn 5) myEnv 

Это работает отлично, за исключением того, я не люблю эти три линии, в частности:

myFn' <- myFn (a - 1) 
helper'' <- asks helper' 
return (foldMap helper'' myFn') 

Я чувствую, что должен быть способ поднять foldMap таким же образом, как mapM является поднятая версия map по своему составу с sequence. В идеале я хотел бы эти три линии, чтобы свернуть вниз к одному:

foldMapM helper'' (partitions (n - 1)) 

Предполагая, что: helper'' :: Int -> MyReader (Set Int)

Это, конечно, потребует foldMapM функции с подписью аналогичной:

foldMapM 
    :: (Monad m, Foldable t, Monoid n) 
    => (a -> m n) 
    -> m (t a) 
    -> m n 

Я пробовал так много всего, но я просто не могу реализовать эту функцию! Может ли кто-нибудь помочь?

ответ

4

В принципе, вы хотели бы создать Monad m => m a -> m b -> m c от a -> b -> c. Это именно то, что liftM2 (от Control.Monad) делает:

liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r 

Стимулирует функцию монады, сканируя монадических аргументы слева направо. Например,

liftM2 (+) [0,1] [0,2] = [0,2,1,3] 
liftM2 (+) (Just 1) Nothing = Nothing 

Таким образом, это так же просто, как с помощью liftM2 foldMap:

myFn :: Int -> MyReader (Set Int) 
myFn a 
    | a <= 0 = return $ singleton 1 
    | otherwise = liftM2 foldMap (asks helper') (myFn (a - 1)) 

В качестве альтернативы вы можете использовать <$> и <*> из Control.Applicative, если вам не нравится, дополнительные круглые скобки:

myFn :: Int -> MyReader (Set Int) 
myFn a 
    | a <= 0 = return $ singleton 1 
    | otherwise = foldMap <$> asks helper' <*> myFn (a - 1) 

Для получения дополнительной информации, взгляните на Typeclassopedia.

+1

На самом деле, 'foldMapM f xs = liftM2 foldMap f xs' имеет подпись' foldMapM :: (Monad m, Foldable t, Monoid n) => m (a -> n) -> m (ta) -> mn' , Я пробовал это безуспешно раньше, так как моя функция была типа '(a -> m n)'. Я не думал, что нужно сделать 'asks helper'' inline, чтобы получить' m (a -> n) '. Это потрясающе! – arussell84