2016-12-07 5 views
2

型クラスの無モナドとの表記を行う再バインドモナドは、モナドアクションのすべての使用において、MonadDictモナドインスタンス引数(constを使用)を無視することを避けることができますか?(>> =)再バインドし、このように渡す明示的な辞書を使用してモナドのために戻すことができる

+1

きっとあなたはより多くの '(>> =):: MonadDict mのような何かしたいです - あなたは3を取得する場合> mb'、または何をする予定ですか - >ミリアンペアを - >( - >メガバイト)別の辞書? –

+0

@DanielWagner私はそれを一度しか使用しません。最後に辞書を渡す 'usage'部分を参照してください。 – coubeatczech

+0

通常、「私は1つしか渡しません」のような単純な不変量は、あなたにチェックするために型チェッカーに尋ねるのに良いことです。私が提案したタイプはそうするように求める一つの方法です。 –

答えて

6

あなたはこのように行うことができます。

{-# LANGUAGE RankNTypes #-} 
{-# LANGUAGE RebindableSyntax #-} 
{-# LANGUAGE RecordWildCards #-} 

module Lib where 
import Prelude hiding(return, fail, (>>=), (>>)) 

data MonadDict m = MonadDict 
    { (>>=) :: forall a b. m a -> (a -> m b) -> m b 
    , (>>) :: forall a b. m a -> m b -> m b 
    , return :: forall a. a -> m a 
    , fail :: forall a. String -> m a 
    } 

monadDictIO :: MonadDict IO 
monadDictIO = ... 

foo :: MonadDict m -> String -> m() 
foo = ... 

usage = let 
    monadicCode [email protected]{..} = do 
     ln <- getLine 
     putStrLn ln 
     foo m ln 
    in monadicCode monadDictIO 
+1

興味深い。これは、 '-XRecordWildCards'でも動作します:それぞれ' '= 'と' '' 'の' bind'フィールドを呼び出すと、 'monadicCode MonadDict {..} = do ...'だけ使うことができます。これが良いアイデアかどうか私は確信していませんが、これは間違いなく興味深いものです。 – leftaroundabout

+0

@leftaroundaboutはい、あなたは正しいです。 – freestyle

1

短いと間違った答えは(>>=)に第二引数の戻り値の型からMonadDict m引数をドロップです:

(>>=) :: (MonadDict m -> m a) -> (a -> m b) -> (MonadDict m -> m b) 

しかし、それはありません本当にすべての構文問題を解決します。ある人がタイプMonad m => a -> m bの既存の矢印を持っていて、明示的な辞書を渡すとタイプa -> (MonadDict m -> m b)になり、(>>=)の2番目の引数として使用できなくなります。関数drop :: (MonadDict m -> m b) -> m bを2番目の引数と互換性を持たせるには、MonadDictを渡す理由はありません。


あなたはMonadDict mを読むためReaderT変圧器を再発明しています。

newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a } 

あなたはそれがm aReaderT (MonadDict m) m aへのINGのliftの同等だconstを使用しているすべての時間。あなたの例は、constの代わりにliftと書かれていると、それほどよく知られていないように見えます。

usage = let 
    monadicCode = do 
    ln <- lift getLine 
    lift . putStrLn $ ln 
    in monadicCode monadDictIO 

ReaderTを使用した完全な例です。おそらくReaderT (MonadDict m) mの新しいタイプを作成し、liftの別のタイプを作成する方が良いでしょう。 (>>=)returnの実装は、のbindまたはretを使用することを除いて、ReaderTと同じです。あなたはそれを独自のタイプを与える場合

{-# LANGUAGE RankNTypes #-} 
{-# LANGUAGE RebindableSyntax #-} 

module Lib 
    (usage 
    ) where 

import Prelude hiding ((>>=), return) 
import qualified Prelude as P ((>>=), return) 
import Control.Monad.Trans.Reader 

data MonadDict m = MonadDict { 
    bind :: forall a b. m a -> (a -> m b) -> m b , 
    ret :: forall a. a -> m a } 

type ReadM m a = ReaderT (MonadDict m) m a 

(>>=) :: ReadM m a -> (a -> ReadM m b) -> ReadM m b 
m >>= k = ReaderT $ \[email protected] { bind = bind } -> bind (runReaderT m d) (\a -> runReaderT (k a) d) 

return :: a -> ReadM m a 
return a = ReaderT $ \[email protected] { ret = ret } -> ret a 

lift :: m a -> ReadM m a 
lift m = ReaderT $ \_ -> m 

monadDict :: Monad m => MonadDict m 
monadDict = MonadDict { 
    bind = (P.>>=), 
    ret = P.return 
} 

example1 :: String -> ReadM IO() 
example1 a = do 
    lift . putStrLn $ a 
    lift . putStrLn $ a 

example2 :: ReadM IO() 
example2 = do 
    example1 "Hello" 
    ln <- lift getLine 
    lift . putStrLn $ ln 

usage :: IO() 
usage = runReaderT example2 monadDict 

あなたは独立基礎となるmMonadインスタンスとそれを装備し、RebindableSyntaxで済ますことができます。

newtype ReadMD m a = ReadMD {runReadMD :: MonadDict m -> m a} 

instance Functor (ReadMD f) where 
    fmap = liftM 

instance Applicative (ReadMD f) where 
    pure = return 
    (<*>) = ap 

instance Monad (ReadMD m) where 
    m >>= k = ReadMD $ \[email protected] { bind = bind } -> bind (runReadMD m d) (\a -> runReadMD (k a) d) 
    return a = ReadMD $ \[email protected] { ret = ret } -> ret a 
関連する問題

 関連する問題