2016-07-05 15 views
4

私は、さまざまな状況で異なる操作のための2つのフリーモナドを持っています。しかし、1(major)DSLは、特定の操作は、コンテキストにある場合、別の1(action)が含まれている必要があります:ハスケルでは、フリーモナドを別のものにどのように埋め込むことができますか?

import Control.Monad.Free 

data ActionFunctor next = Wait Timeout next 
         | Read URI next 

instance Functor ActionFunctor where 
    fmap f (Wait timeout next) = Wait timeout (f next) 
    fmap f (Read uri next)  = Read uri (f next) 

type Action = Free ActionFunctor 


data MajorFunctor next = Log LogText next 
         | Act Action next 
         | Send Message 

instance Functor MajorFunctor where 
    fmap f (Log text next) = Log text (f next) 
    fmap f (Act action next) = Act action (f next) 
    fmap f (Send message)  = Send message 

type Major = Free MajorFunctor 

問題があり、GHCは、Act Action nextAction(* -> *)の一種であることをMajorFunctorを文句を言うでしょうタイプだけではありません。これは、data ActionFunctor定義ではタイプパラメータとしてnextを受け入れる必要があり、Act Action行にはこのようなパラメータが含まれていないためです。しかし、たとえメッセージは、私は私もMajorファンクタで、このような余分の型パラメータを宣言する必要があるかどうかわからないんだけど、私には明らかである:

data MajorFunctor actionNext next = ... 

一つだけのデータコンストラクタは、パラメータを使用しますので、それは奇妙に見えますが、しばらくこのような暴露はMajorFunctorになり、MajorFunctor actionNextになり、見た目はあまりにも多くの詳細を明らかにします。そこで私はFreeT変圧器を見て、それが私が望むものかどうかを見ました。しかし、私の場合は、Actionインタプリタに通話する必要があります.DIMプログラムにそのような操作がある場合は、bindがモナドプログラムに含まれているわけではありません。したがって、変圧器が良い解決策であるかどうかもわかりません。

答えて

5

ActコンストラクタでAction aを埋め込み、aに応じてnextの手順を続行できます。あなたが一緒に2を結びつけるために実存を使用してこれを行うことができます。

{-# LANGUAGE ExistentialQuantification #-} 
data MajorFunctor next = Log LogText next 
         | forall a. Act (Action a) (a -> next) 
         | Send Message 

instance Functor MajorFunctor where 
    fmap f (Log text next) = Log text (f next) 
    fmap f (Act action next) = Act action (fmap f next) 
    fmap f (Send message)  = Send message 
+0

魔法の解決策!本当にありがとう。 – snowmantw

7

実存を必要としない簡単な解決策は、Actionnextパラメータを埋め込むことです。

data MajorFunctor next = Log LogText next 
         | Act (Action next) 
         | Send Message 

instance Functor MajorFunctor where 
    fmap f (Log text next) = Log text (f next) 
    fmap f (Act action) = Act (fmap f action) 
    fmap f (Send message) = Send message 

これはサボテンのソリューション@あなたがActionを実行すると、それは(タイプ実存不明の())何かを返すよ」と言うのに対し、「あなたはActionを実行すると、それはnextを返すよ」と言うたことができます(のみ) a next "になります。

The co-Yoneda lemmaは、これらの2つのソリューションが同形であると言います。私のバージョンはシンプルですが、Action秒を超えるくり返しfmap秒のようないくつかの操作では、Cactusのほうが高速かもしれません。

+0

ニースのソリューション!どのような賢いアプリケーションのCoyoneda。^- ^ – Lynn