2012-03-13 32 views
13

Monad mMonad nという2つのモナドがある場合、m (n a)n (m a)に変換したいと思います。しかし、(>>=)returnの両方が1つのモナドタイプしか扱っていないため、(>>=)はモナドからコンテンツを抽出することができますが、同じモナドタイプに戻して結果値にすることができます。モナドの再パック - 一般的な方法?

ただし、mを固定タイプに設定すると、ジョブが簡単になります。例としてMaybeを取る:

reorder :: (Monad n) => Maybe (n a) -> n (Maybe a) 
reorder Nothing = return Nothing 
reorder (Just x) = do 
    x' <- x 
    return $ Just x' 

またはリスト:

reorder :: (Monad n) => [n a] -> n [a] 
reorder [] = return [] 
reorder (x:xs) = do 
    x' <- x 
    xs' <- reorder xs 
    return (x':xs') 

ない見にくい、私たちはここにパターンを持っています。より明白にするために、Applicative方法でそれを書き、それが各要素にデータコンストラクタを適用するよりも、よりません:

reorder (Just x) = Just <$> x 
reorder (x:xs) = (:) <$> x <*> (reorder xs) 

私の質問です:Haskellの型クラスは、すでに私はそのような動作を記述するために存在する、または実行ん自分自身で車輪を発明しなければなりませんか?

私はGHCのドキュメントで簡単な検索を行いましたが、このトピックでは役に立たないものは見つかりませんでした。

答えて

14

Data.Traversableあなたが探しているものを提供しています。

sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a) 

GHCも自動的にインスタンスを導出するためのサポートを提供します。

{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-} 
import Data.Foldable 
import Data.Traversable 

data List a = Nil | Cons a (List a) 
    deriving(Functor, Foldable, Traversable) 
+0

これはもちろん、外側の型のコンストラクタが 'Traversable'(すべての' Monad'sではない)である場合にのみ機能します。ボーナスは、内側のものが 'Applicative'でなければならないということです。 –

5

(Monad m, Monad n) => m (n a) -> n (m a)ためhoogle上のクイック検索は、いくつかの機能があることを教えてくれましたあなたが探している署名に(大まかに)準拠しています:

  1. Data.Traversable.sequence :: (Traversable t, Monad m) => t (m a) -> m (t a);
  2. Data.Traversable.sequenceA :: Applicative f => t (f a) -> f (t a);
  3. ​​(Preludeによってもエクスポートされます)。

両方[a]Maybe aは、一筆書きのインスタンスであるので、あなたのリオーダー機能はData.Traversable.sequenceのアプリケーションだけです。一つは、一例では、書くことができます:

ghci> (Data.Traversable.sequence $ Just (return 1)) :: IO (Maybe Int) 
Just 1 
it :: Maybe Int 

ghci> (Data.Traversable.sequence $ Just ([1])) :: [Maybe Int] 
[Just 1] 
it :: [Maybe Int] 

ghci> (Data.Traversable.sequence $ [Just 1]) :: Maybe [Int] 
Just [1] 
it :: Maybe [Int] 

しかし、特定のクラス宣言がclass (Functor t, Foldable t) => Traversable tであることに注意してください、あなたは他の2つの関数の種類でも見れば、それはあなたが探しているもののように思えるしません。すべてのモナドmnに制限/前提条件なしで一般的な方法で行われる可能性があります。

1

すべてのモナドがこのように通勤するわけではありません。エドワードKmettのdistributiveパッケージは、あなたが望むものに似ているタイプのコンストラクタのための型クラスDistributiveを提供(簡体字):

class Functor g => Distributive g where 
    distribute :: Functor f => f (g a) -> g (f a) 
    collect  :: Functor f => (a -> g b) -> f a -> g (f b) 

デフォルトの定義は、互いの観点で書かれ、distributecollectために設けられています。このパッケージはsearching hayoo for the desired type signatureで見つけました。

4

これは一般的ではありません。これを行うことができないモナドの良い例は、読者(または関数)のモナドです。それは定義可能であることを、次の機能が必要になります。

impossible :: (r -> IO a) -> IO (r -> a) 

機能を実現することができないことを証明することは簡単ではありません。しかし、直感的には、IOが返される値で行われなければならないものは、パラメータがであることを知る前に、を実行しなければならないという問題があります。したがって、impossible readFileは、どのファイルを開くかを知る前に純粋な関数FilePath -> Stringを生成する必要があります。明らかに、少なくとも、impossibleはあなたが望むことができません。

関連する問題