[英]Is there an inject equivalent for Haskell in the context of free monads
我正试图翻译这个Scala的cats
关于组成免费monad的例子 。
该示例的要点似乎是将单独的关注点分解为单独的数据类型:
data Interact a = Ask (String -> a) | Tell String a deriving (Functor)
data DataOp = AddCat String | GetAllCats [String] deriving (Functor)
type CatsApp = Sum Interact DataOp
如果没有这两个单独的问题,我将为Interact
操作构建“语言”,如下所示:
ask :: Free Interact String
ask = liftF $ Ask id
tell :: String -> Free Interact ()
tell str = liftF $ Tell str ()
但是,如果我想在一个也使用DataOp
的程序中使用ask
和tell
,我无法用上面的类型定义它们,因为这样的程序将具有类型:
program :: Free CatsApp a
在cats
,为的定义tell
,并ask
操作,他们使用InjectK
类,以及inject
从方法Free
:
class Interacts[F[_]](implicit I: InjectK[Interact, F]) {
def tell(msg: String): Free[F, Unit] = Free.inject[Interact, F](Tell(msg))
def ask(prompt: String): Free[F, String] = Free.inject[Interact, F](Ask(prompt))
}
令我困惑的是,不知何故, DataOp
函数的位置信息( DataOp
位于左侧, Interact
位于右侧)似乎无关紧要(这是非常好的)。
是否有类似的类型和函数可以用Haskell以优雅的方式解决这个问题?
这在“ 数据类型单点”中有所介绍。 您演示的Scala库看起来像是对原始Haskell的相当忠实的翻译。 它的要点是,你写了一个代表仿函数sup
之间关系的类,它包含一个仿函数sub
:
class (Functor sub, Functor sup) => sub :-<: sup where
inj :: sub a -> sup a
(这些天你可能会使用Prism
,因为它们都可以注入和投射。)然后你可以使用functor coproduct组成你的免费monad的基本函子的常用技巧,实现:-<:
for the two case of Sum
,
instance Functor f => f :-<: f where
inj = id
instance (Functor f, Functor g) => f :-<: (Sum f g) where
inj = InL
instance (Functor f, Functor g, Functor h, f :-<: g) => f :-<: (Sum h g) where
inj = InR . inj
并通过抽象基础仿函数使指令集可组合。
ask :: Interact :-<: f => Free f String
ask = liftF $ inj $ Ask id
tell :: Interact :-<: f => String -> Free f ()
tell str = liftF $ inj $ Tell str ()
addCat :: DataOp :-<: f => String -> Free f ()
addCat cat = liftF $ inj $ AddCat cat ()
getCats :: DataOp :-<: f => Free f [String]
getCats = liftF $ inj $ GetCats id
现在,您可以编写一个既使用Interact
又使用DataOp
的程序,而无需引用特定的和类型。
myProgram :: (Interact :-< f, DataOp :-< f) => Free f ()
myProgram = ask >>= addCat
然而,这些都不比标准MTL方法特别好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.