2017-08-04 5 views
1

La fonction bind (>>=) a la signature:façon idiomatiques à mettre en œuvre "m (ta) -> (a -> m (tb)) -> m (tb)"

m a -> (a -> m b) -> m b 

Cependant, je veulent une fonction avec la signature:

m (t a) -> (a -> m (t b)) -> m (t b) 

Plus précisément, j'ai une fonction qui donne un nombre entier, elle retourne une liste d'entiers à l'intérieur d'un IO:

f :: Int -> IO [Int] 

mais je souhaite l'appliquer à un IO of list of Integers et je ne peux pas utiliser la fonction de liaison régulière car elle est enveloppée dans deux conteneurs, c'est-à-dire une liste contenue dans un IO. Searching on hoogle n'aide pas.

J'utilise l'approche suivante pour mettre en œuvre cette:

Supposons que la mise en œuvre de la fonction est:

f :: Int -> IO [Int] 
f x = do 
    return $ [x-10, x+10] 

J'utilise deux fonctions d'aide pour obtenir ce que je veux:

f' :: [Int] -> IO [Int] 
f' xs = do 
    list <- traverse f xs 
    return $ join list 

f'' :: IO [Int] -> IO [Int] 
f'' xs = do 
    ys <- xs 
    f' ys 

Ce qui précède fonctionne mais je voudrais savoir s'il existe une meilleure façon de l'implémenter en haskell?

+0

Je suis ai pas la recherche d'une solution puisque je déjà. Je veux un moyen idiomatique de résoudre cela. – mandark

+0

Je ne sais pas si vous avez déjà lu les réponses, mais * "Plus généralement, quelle est la fonction m1 m2 a -> (a -> m1 m2 b) -> m1 m2 b?" * Fait partie de cette question en double. La réponse: c'est impossible dans le cas général. Voir la deuxième réponse. – 4castle

+1

Si vous supprimez les signatures de type sur 'f'' et' f''' et absentez 'f' dans un paramètre, vous obtiendrez le type le plus général déduit par le typechecker. Je dirais que ces implémentations sont idiomatiques, sauf peut-être l'utilisation de la notation 'do' au lieu de' >> = 'et' fmap' mais ce sont des problèmes purement stylistiques. En l'état, cette question est entièrement basée sur l'opinion - pouvez-vous clarifier ce que votre code n'est pas assez bon, c'est-à-dire ce que vous aimeriez voir accomplir? – user2407038

Répondre

1

La solution idiomatiques serait d'utiliser Data.Functor.Compose:

data Compose f g a = Compose { getCompose :: f (g a) } 

Depuis la fonction que vous cherchez est trivial à mettre en œuvre lorsque Compose f g est une monade:

bibind :: Monad (Compose f g) => f (g a) -> (a -> f (g b)) -> f (g b) 
bibind m h = getCompose (Compose m >>= Compose . h) 

Comme bien expliqué in this answer, il ne suffit pas pour f et g pour être Monad s, ils ont également besoin de faire la navette:

class Commute f g where 
    commute :: g (f x) -> f (g x) 

instance (Monad f, Monad g, Commute f g) => Monad (Compose f g) where 
    return = Compose . return . return 
    join = Compose . fmap join . join . fmap commute . getCompose . fmap getCompose 

(En général, it's not sufficient for f to be a monad and g to be Traversable)