2016-04-26 4 views
5

開発中のドメイン固有の言語用に2つのモナドを書きました。最初はLangです。これは、行ごとに言語を解析するために必要なものがすべて含まれているはずです。私はリーダー、ライター、および状態にしたいことを知っていたので、私はRWSモナド使用:2つのモナド・トランススタックを組み合わせた場合、Applicativeを派生させることができません。

newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) a } 
    deriving 
    (Functor 
    , Applicative 
    , Monad 
    , MonadIO 
    ) 

の両方がように見える:ユーザーと対話するために

type LangLog = [String] 
type LangState = [(String, String)] 
type LangConfig = [(String, String)] 

newtype Lang a = Lang { unLang :: RWS LangConfig LangLog LangState a } 
    deriving 
    (Functor 
    , Applicative 
    , Monad 
    , MonadReader LangConfig 
    , MonadWriter LangLog 
    , MonadState LangState 
    ) 

Replで、Haskelineを使用しています(GHCiで振る舞いながら遊んだことがありますが)LangReplに埋め込むことができず、ユーザーからの行を解析できませんでした。主な質問は、どうすればいいのですか?

具体的には、私は本来Lang方法が含まれるようにReplを書いた場合:それは主にtypechecks

newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) } 
    deriving 
    (Functor 
    , Applicative 
    , Monad 
    , MonadIO 
    , MonadReader LangConfig 
    , MonadWriter LangLog 
    , MonadState LangState 
    ) 

が、私は(Monadとすべての残りのために必要な)Applicativeを導出することはできません。

私はモナドトランスの新人でREPLを設計しているので、GlambdaRepl.hsMonad.hsから貨物を研究しています。もともと、私はGADTを私の表現にも使用しようとしていたので、それを選んだのです。 mzero でREPLを終了できるようにする(これは危険なのですか?)

  • MaybeT

    • newtype + GeneralizedNewtypeDeriving:それは私が採用したが変更に完全に開いていました夫婦不慣れな慣行を含み、

      はここで、これまでの私の作業コードです:

      {- LANGUAGE GeneralizedNewtypeDeriving #-} 
      
      module Main where 
      
      import Control.Monad.RWS.Lazy 
      import Control.Monad.Trans.Maybe 
      import System.Console.Haskeline 
      
      -- Lang monad for parsing language line by line 
      
      type LangLog = [String] 
      type LangState = [(String, String)] 
      type LangConfig = [(String, String)] 
      
      newtype Lang a = Lang { unLang :: RWS LangConfig LangLog LangState a } 
          deriving 
          (Functor 
          , Applicative 
          , Monad 
          , MonadReader LangConfig 
          , MonadWriter LangLog 
          , MonadState LangState 
          ) 
      
      -- Repl monad for responding to user input 
      
      newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) } 
          deriving 
          (Functor 
          , Applicative 
          , Monad 
          , MonadIO 
          ) 
      

      そしてカップルは、それを拡張しようとします。 、

      newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) } 
      deriving 
          (Functor 
          , Applicative 
          ) 
      
      --  Can't make a derived instance of ‘Functor Repl’ 
      --  (even with cunning newtype deriving): 
      --  You need DeriveFunctor to derive an instance for this class 
      --  In the newtype declaration for ‘Repl’ 
      -- 
      -- After :set -XDeriveFunctor, it still complains: 
      -- 
      --  Can't make a derived instance of ‘Applicative Repl’ 
      --  (even with cunning newtype deriving): 
      --  cannot eta-reduce the representation type enough 
      --  In the newtype declaration for ‘Repl’ 
      

      次だけ一度それらの両方を使用しようとしている:まず、前述したようにReplLang含む

      -- Repl around Lang: 
      -- can't access Lang operations (get, put, ask, tell) 
      type ReplLang a = Repl (Lang a) 
      
      test1 :: ReplLang() 
      test1 = do 
          liftIO $ putStrLn "can do liftIO here" 
          -- but not ask 
          return $ return() 
      
      -- Lang around Repl: 
      -- can't access Repl operations (liftIO, getInputLine) 
      type LangRepl a = Lang (Repl a) 
      
      test2 :: LangRepl() 
      test2 = do 
          _ <- ask -- can do ask 
          -- but not liftIO 
          return $ return() 
      

      が示されていません:私はまたaskliftの様々な順列を試してみましたが、 putStrLnが呼び出されます。

      newtype Lang2 a = Lang2 
          { unLang2 :: ReaderT LangConfig (WriterT LangLog (State LangState)) a 
          } 
          deriving 
          (Functor 
          , Applicative 
          ) 
      

      同じETA-削減エラーを与える:最後に、必ずすることは、これは、私はそれなしでLangを書いてみましたRWS固有の問題ではありません。

      要するに、私が知りたいことは、これら2つのモナドをどのように組み合わせるかということです。 liftの明白な組み合わせがないか、または変圧器スタックを間違って配置しているか、深刻な問題が発生していますか?ここで

      私が見夫婦おそらく関連の質問です:

      更新:モナド変圧器の私の手波状の理解がメインでした問題。そうLangTはほとんどそれを解決ReplIOの間に挿入することができるRWST代わりにRWSを使用:

      newtype LangT m a = LangT { unLangT :: RWST LangConfig LangLog LangState m a } 
          deriving 
          (Functor 
          , Applicative 
          , Monad 
          , MonadReader LangConfig 
          , MonadWriter LangLog 
          , MonadState LangState 
          ) 
      
      type Lang2 a = LangT Identity a 
      
      newtype Repl2 a = Repl2 { unRepl2 :: MaybeT (LangT (InputT IO)) a } 
          deriving 
          (Functor 
          , Applicative 
          , Monad 
          -- , MonadIO -- ghc: No instance for (MonadIO (LangT (InputT IO))) 
          , MonadReader LangConfig 
          , MonadWriter LangLog 
          , MonadState LangState 
          ) 
      

      唯一残っている問題は、私はMonadIO ioのRepl2インスタンスを作成する方法を理解する必要があります。

      更新2:今すぐすべて良いです! MonadTransLangTのために派生したインスタンスのリストに追加するだけでした。

  • +0

    ' IO'はhttp://stackoverflow.com/questions/13056663/why-is-there-no-io-(あなたのモナド変圧器スタックの一番下にある必要がありますトランスフォーマー・イン・ハスケル)。 'newtype LangT m a = LangT(RWST .. .. .. m a)のようなもの。 newtypeのREPLのA = REPL(MaybeT(InputT(LangT IO))a)は 'あなたのために働くかもしれません。 – user2407038

    +0

    あなたは正しいです、ありがとう!私はIO'が下になければならない '知っていたが、何らかの理由では、スタック全体が直線的であるように私には発生していませんでした。私は、あなたが別のタイプの「オフサイド」を置くことができると思った。質問が更新されます。 – Jeff

    +0

    'LangT'は、' MonadIO m => MonadIO(MaybeT m) 'インスタンスがそれを必要とするため、' MonadIO m => MonadIO(LangT m) 'インスタンス(これはおそらく導出可能です)を必要とします。 – user2407038

    答えて

    4

    2つのモナドを別のモナドの上に作成しようとしています。しかし、一般的にmonads don't compose this wayです。あなたのケースの簡略版を見てみましょう。 ではなく、MaybeT ...Readerの代わりに、ちょうどMaybeと仮定しましょう。どのような場合:だからあなたのモナドの種類は、これがモナドだったら今、私たちはここにタイプ

    join :: Maybe (LangConfig -> Maybe (LangConfig -> a)) -> Maybe (LangConfig -> a) 
    

    し、問題が来るのだろう総join機能を、持っているでしょう

    Maybe (LangConfig -> a) 
    

    だろう引数は

    f :: LangConfig -> Maybe (LangConfig -> a) 
    

    Nothingを返しf、いくつかの入力の値Just fありますか?意味のある値Maybe (LangConfig -> a)Just fからどのように構築するのか合理的な方法はありません。私たちは、その出力がNothingまたはJust somethingになる場合fが決めることができますが、Maybe (LangConfig -> a)以内に、我々はNothingを返すかLangConfigを読んで、両方ではなくどちらかようにLangConfigを読む必要があります!だから我々はそのようなjoin機能を持つことはできません。

    あなたは慎重にモナド変圧器を見れば、あなたは時々2つのモナドを結合する方法をただ一つの方法があることがわかり、それが彼らの素朴な構図ではありません。特に、ReaderT r Maybe aおよびは、r -> Maybe aと同形である。先に見たように、逆はモナドではありません。

    したがって、問題の解決策は、モナドの代わりにモナド変圧器を構築することです。あなたは、どちらかのモナド変圧器としての両方を持つことができます。

    newtype LangT m a = Lang { unLang :: RWST LangConfig LangLog LangState m a } 
    newtype ReplT m a = Repl { unRepl :: MaybeT (InputT m) a } 
    

    LangT (ReplT IO) aまたはReplT (LangT IO) aとしてそれらを使用する(コメントの1で説明したように、IO常にスタックの一番下になければなりません)。あるいは、それらのうちの1つ(外側のもの)をトランスとして、もう1つをモナドとして持つことができます。しかし、IOを使用しているときは、内側のモナドには内部的にIOが含まれていなければなりません。LangT (ReplT IO) aReplT (LangT IO) aの間に差があること

    は注意してください。それはStateT s Maybe aMaybeT (State s) aの違いに似ています:前者はmzeroで失敗した場合、どちらの結果も出力状態がproduedされます。後者でmzeroで失敗した。しかし、そこには結果がありませんが、状態は引き続き使用できます。そこに[なし `IOT`のモナド変換子ではありません]ので

    +1

    ありがとう!私は(ゆっくりと、ついに)このものの直感を得るようになっていると思う。 – Jeff

    関連する問題