在Haskell中,如何将一个自由单子嵌入到另一个中?

在Haskell中,如何将一个自由单子嵌入到另一个中?,haskell,monads,free-monad,Haskell,Monads,Free Monad,我有两个自由单子,用于不同上下文中的不同操作。但是,如果特定操作在上下文中,则一个(主要)DSL需要包含另一个(操作): import Control.Monad.Free data ActionFunctor next = Wait Timeout next | Read URI next instance Functor ActionFunctor where fmap f (Wait timeout next) = Wait tim

我有两个自由单子,用于不同上下文中的不同操作。但是,如果特定操作在上下文中,则一个(
主要
)DSL需要包含另一个(
操作
):

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将投诉
MajorFunctor
Act-Action-next
中的
操作是一种
(*->*)
,而不仅仅是一种类型。这是因为在
data ActionFunctor
定义中,它应该接受
next
作为类型参数,而在
Act Action
行中,它不包含此类参数。但即使消息对我来说很清楚,我也不确定是否应该在
Major
functor中声明这样的额外类型参数:

data MajorFunctor actionNext next = ...

它看起来很奇怪,因为只有一个数据构造函数会使用该参数,而这样的公开会将每个
MajorFunctor
变成
MajorFunctor actionNext
,而且它看起来完全暴露了太多的细节。所以我看了一下
FreeT
transformer,看看它是否是我想要的。然而,在我的例子中,我只需要在DSL程序有这样的操作时调用
Action
解释器,而不是一元程序中的每个
bind
,因此我也不确定转换器是否是一个好的解决方案。

Act
构造函数中,您可以嵌入
操作a
,然后根据
a
继续执行
下一步。您可以使用存在主义将两者联系在一起:

{-# 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

一个不需要存在主义的简单解决方案是将
next
参数嵌入
Action

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
这表示“当您执行
操作时,它将返回一个
下一个
”,而@Cactus的解决方案表示“当您执行
操作时,它将返回一些(未知(存在)类型的)可以(仅)转换为
下一个


说这两个解是同构的。我的版本更简单,但Cactus的对于某些操作可能更快,例如重复的
fmap
s过大
Action
s.

很好的解决方案!Coyoneda是多么巧妙的应用啊^-^