一个函数中两个多态类的解释器 [英] Interpreters for two polymorphic classes in one function

查看:86
本文介绍了一个函数中两个多态类的解释器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个带有通用monad的多态代码(请参见这个问题)对于模型和客户:

I have this polymorphic code (see this question) with generic monads for model and client:

import Control.Monad.Writer

class Monad m => Model m where
  act :: Client c => String -> c a -> m a

class Monad c => Client c where
  addServer :: String -> c ()

scenario1 :: forall c m. (Client c, Model m) => m ()
scenario1 = do
  act "Alice" $ addServer @c "https://example.com"

这是Client的漂亮印刷解释器,它通过Writer monad解释日志中的操作:

and this is the pretty-print interpreter for the Client that explains the actions in the log via Writer monad:

type Printer = Writer [String]

instance Client Printer where
  addServer :: String -> Printer ()
  addServer srv = tell ["  add server " ++ srv ++ "to the client"]

Model的解释器很困难.我尝试了几件事,每件事都会导致自己的错误:

Interpreter for the Model is difficult. I tried several things, each resulting in its own error:

  1. 无法匹配类型'c'":

instance Model Printer where
  act :: String -> Printer a -> Printer a
  act name action = do
    tell [name ++ ":"]
    action

  1. "不能将类型为打印机a"的表达式应用于可见类型参数(打印机a)""

instance Model Printer where
  act :: forall a. String -> Printer a -> Printer a
  act name action = do
    tell [name ++ ":"]
    action @(Printer a)

  1. 无法将类型"c"与"WriterT [String] Data.Functor.Identity.Identity"相匹配"

instance Model Printer where
  act :: Client c => String -> c a -> Printer a
  act name action = do
    tell [name ++ ":"]
    action

我必须说出act中的c a现在是Printer a.

Somehow I need to tell that what was c a in act is now Printer a.

也许我需要在Model类中有两个参数-Model monad的m和Client monad的c,并且Model类还应该定义函数clientToModel :: c a -> m a?

Maybe I need to have two parameters in the Model class - m for Model monad and c for Client monad, and Model class should also define the function clientToModel :: c a -> m a?

有没有办法使模型和客户端分离?我可能仍需要每对clientToModel :: c a -> m a?

Is there a way to keep Model and Client decoupled? I probably would still need clientToModel :: c a -> m a for each pair?

我非常感谢您的建议.谢谢!

I appreciate the advice. Thank you!

推荐答案

问题是act的类型签名保证它可以在 any 客户端上运行,但是您在这里尝试使其只能在名为Printer的特定客户端上运行.这违反了Model类型类的定义.

The problem is that the type signature of act promises that it would work on any client, but here you're trying to constrain it to work only on the specific client called Printer. This violates the definition of the Model type class.

您显然想遵循的通常模式是在同一monad上同时定义ModelClient,如下所示:

The usual pattern that you're apparently trying to follow is to define both Model and Client on the same monad, like this:

class Monad m => Model m where
  act :: String -> m a -> m a

class Monad m => Client m where
  addServer :: String -> m ()

这具有很好的,易于理解的语义,即actaddServer都是环境上下文"操作,在monad m中可用".它们几乎就像全局函数",但仍然是可模拟的.

This has the nice, easily understood semantics that both act and addServer are "ambient context" operations that are "available in the monad m". They're almost like "global functions", yet still mockable.

然后Printer可能是此类monad的一个示例,同时实现ClientModel.然后您的生产堆栈(如ReaderT Config IO或您拥有的任何东西)可能是此类monad的另一个示例.

Then Printer could be one example of such monad, implementing both Client and Model. And then your production stack - like ReaderT Config IO or whatever you have - could be another example of such monad.

但是,如果您坚持在不同的monad上定义ModelClient,则使类型起作用的唯一方法是将Client c约束从act的签名提升为Model类:

However, if you insist on Model and Client being defined on different monads, the only way to make the types work is to lift the Client c constraint from the signature of act to the signature of the Model class:

class (Monad m, Client c) => Model m c where
  act :: String -> c a -> m a

其含义为"每个模型"单子与一组特定的客户"单子一起工作,而不仅仅是随机的客户"单子".

然后您可以定义Printer实例,如下所示:

Then you could define the Printer instance like this:

instance Model Printer Printer where
  act name action = do
    tell [name ++ ":"]
    action

类型将起作用.

话虽如此,我想再次重申您对不同单子定义ClientModel的决定对我来说是一种嗅觉.我强烈建议您按照上述建议重新考虑您的设计.

Having said that, I want to reiterate once again that your decision to define Client and Model on different monads is a smell to me. I strongly recommend that you reconsider your design as suggested above.

这篇关于一个函数中两个多态类的解释器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆