在Haskell中,我怎么能在另一个monad中嵌入一个免费monad?

时间:2016-07-05 07:53:39

标签: haskell monads free-monad

我有两个免费的monad用于不同的上下文中的不同操作。但是,如果特定操作在上下文中,则一个(major)DSL需要包含另一个(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会抱怨MajorFunctor Action中的Act Action next是一种(* -> *),而不仅仅是一种类型。这是因为在data ActionFunctor定义中它应该接受next作为类型参数,并且在Act Action行中它不包含这样的参数。但即使是这条信息我也很清楚,我也不确定是否应该在Major仿函数中声明这样的额外类型参数:

data MajorFunctor actionNext next = ...

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

2 个答案:

答案 0 :(得分:7)

一个不需要存在性的简单解决方案是将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

这表示"当您执行Action时,它会返回next"而@ Cactus的解决方案会说"当您执行时执行Action它会返回一些(未知(存在)类型的东西),它可以(仅)变成 a next"。

The co-Yoneda lemma说这两个解决方案是同构的。我的版本更简单,但Cactus的某些操作可能更快,例如重复fmap s大Action秒。

答案 1 :(得分:5)

Act构造函数中,您可以嵌入Action a,然后根据next继续执行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