如何不将实例约束应用于使用通用函数的受限函数?

ect*_*der 10 haskell typeclass

假设我有一个函数:

logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
logResult = ...
Run Code Online (Sandbox Code Playgroud)

在此功能中,如果得到:

  1. Right (Response a)-我打电话toJSON记录结果。
  2. Left MyError-我也记录了。MyError已经ToJSON定义了一个实例。

现在,我想编写一个辅助函数:

logError :: (MonadIO m) :: MyError -> m ()
logError err = logResult (Left err)
Run Code Online (Sandbox Code Playgroud)

但是GHC抱怨以下几方面:

    • Could not deduce (ToJSON a0) arising from a use of ‘logResult’                                                                    
      from the context: MonadIO m                                                                                                       
        bound by the type signature for:                                                                                                
                   logError :: forall (m :: * -> *).                                                                                    
                               MonadIO m =>                                                                                             
                               L.Logger                                                                                                 
                               -> Wai.Request
                               -> MyError
                               -> m ()

...
...
The type variable ‘a0’ is ambiguous 
Run Code Online (Sandbox Code Playgroud)

我了解该错误是因为logResult需要保证ain中Response a必须ToJSON定义了一个实例。但是logError我明确地过去了Left MyError。这不应该消除歧义吗?

有什么办法可以编写logError辅助函数?

PS:我已经简化了示例中的类型签名。错误消息中包含详细信息。

HTN*_*TNW 11

为什么这是一个功能?如果此函数的行为如此清晰地分为两个,则应两个函数。也就是说,您已经编写了一个整体函数,并尝试使用它来将一个简单的函数定义为实用程序。而是编写一个简单的函数,然后将整体函数与另一个函数一起编写。该类型几乎要求它:与Either a b -> c是同构的(a -> c, b -> c)

-- you may need to factor out some common utility stuff, too
logError :: (MonadIO m) :: MyError -> m ()
logResponse :: (MonadIO m, ToJSON a) => Response a -> m ()

logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
logResult = either logError logResponse
Run Code Online (Sandbox Code Playgroud)

logResult仍然有其用途;如果您是Either MyError (Response a)从某个图书馆获得的,则logResult可以轻松处理。但是,否则,您不应该写作logResult (Left _)logResult (Right _)经常写作;基本上对待logResult . Left,并logResult . Right作为自己的功能,你回实际编写它们作为独立的功能导致。

但是logError我明确地过去了Left MyError。这不应该消除歧义吗?

不,不应该。问题的结尾和开头logResult看起来像这样:

logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
Run Code Online (Sandbox Code Playgroud)

调用它时,只需单击一下即可实现。类型表明您需要ToJSON a-您需要提供ToJSON a。而已。如果你知道你并不需要做ToJSON aLeft值,那么你就拥有未体现在类型有用的信息。您应该将该信息添加到类型中,在这种情况下,这意味着将其分成两个部分。IMO(IMO)实际上不会允许您考虑所想的语言设计,因为停顿问题将使其无法正确执行。