Haskell 如何不对使用更通用函数的受限函数应用实例约束?
假设我有一个函数:Haskell 如何不对使用更通用函数的受限函数应用实例约束?,haskell,typeclass,Haskell,Typeclass,假设我有一个函数: logResult::(MonadIO m,ToJSON a)=>MyError(响应a)->m() 日志结果=。。。 在此函数中,如果我得到: Right(Response a)-我调用toJSON来记录结果 左侧我的错误-我也会记录它MyError已经定义了一个ToJSON实例 现在我想写一个助手函数: logError:(MonadIO m)::MyError->m() logError err=logResult(左错误) 但GHC的抱怨大致如下: • C
logResult::(MonadIO m,ToJSON a)=>MyError(响应a)->m()
日志结果=。。。
在此函数中,如果我得到:
Right(Response a)
-我调用toJSON
来记录结果李>
左侧我的错误
-我也会记录它MyError
已经定义了一个ToJSON
实例logError:(MonadIO m)::MyError->m()
logError err=logResult(左错误)
但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
我理解错误是因为logResult
需要保证Response a
中的a
必须定义ToJSON
实例。但是在logError
中,我显式地传递了Left MyError
。这不应该消除歧义吗
是否有任何方法可以编写logError
helper函数
PS:我简化了示例中的类型签名。错误消息包含血淋淋的详细信息。为什么这是一个功能?如果此函数的行为如此清晰地拆分为两个,那么它应该是两个函数。也就是说,您已经编写了一个单片函数,并试图将一个更简单的函数定义为使用它的实用程序。相反,编写一个简单函数,并将单片函数作为它与另一个函数的组合来编写。这个类型非常需要它:
要么a b->c
同构于(a->c,b->c)
logResult
仍有其用途;如果您从某个库中得到一个MyError(Response a)
,那么logResult
可以毫不费力地处理它。但是,否则,您不应该经常编写logResult(左)
或logResult(右)
;它本质上处理logResult。左
和日志结果。右键
作为它们自己的函数,这会让您回到实际将它们作为单独的函数编写
但是在logError
中,我显式地传递了Left MyError
。这不应该消除歧义吗
不,不应该。问题的结束和开始是logResult
如下所示:
logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
当您调用它时,实现并不重要。类型表示您需要
ToJSON a
——您需要提供ToJSON a
。就这样。如果您知道Left
值不需要ToJSON a
,那么您就拥有了类型中没有反映的有用信息。您应该将该信息添加到类型中,在本例中,这意味着将其拆分为两部分。事实上(依我看),如果允许你所想的内容,那将是糟糕的语言设计,因为停顿的问题将使它不可能正确表达。我也无法正确表达标题。我想这表明我对这个概念/想法缺乏理解。你为什么在这里使用Left
,你不应该使用logError=logResult
?事实上,像logError MonadIO m::MyError a->m()
那样编写它更有意义,这样就可以解压它,如果Left
记录错误,否则什么都不做。乍一看,它看起来像是在where
子句中定义的logError
?在这种情况下,很可能是由于。不。我已经有了这方面的logResult
。我需要logError
以防我想使用任何一个logError返回结果
啊,我得到了它,通过使用Left err
,你知道类型是MyError a
,但是a
是什么,不清楚。您也许可以通过强制一个类型来解决这个问题,您知道存在一个ToJSON
,比如:logError err=logResult(Left err::any MyError Int)
。感谢您提供的详细答案。我现在明白了。把它分成两个函数是有意义的。
logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()