haskell - 如何不对使用更通用函数的受限函数应用实例约束?

标签 haskell typeclass

假设我有一个功能:

logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
logResult = ...

在这个函数中,如果我得到:
  • Right (Response a) - 我调用 toJSON记录结果。
  • Left MyError - 我也记录下来。 MyError已有 ToJSON实例定义。

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

    logError :: (MonadIO m) :: MyError -> m ()
    logError err = logResult (Left err)
    

    但 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需要保证aResponse a必须有 ToJSON实例定义。但是在 logError我明确通过 Left MyError .这不应该消除歧义吗?

    有什么办法可以写logError辅助功能?

    PS:我已经简化了示例中的类型签名。错误消息有血淋淋的细节。

    最佳答案

    为什么这是一个功能?如果这个函数的行为如此干净地分成两个,那么它应该是两个函数。也就是说,您已经编写了一个整体函数,并试图将一个更简单的函数定义为使用它的实用程序。相反,编写一个简单的函数并将单体函数编写为它与另一个函数的组合。该类型非常需要它: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
    
    logResult仍然有它的用途;如果您收到 Either MyError (Response a)来自某个图书馆,然后是 logResult可以毫不费力地处理它。但是,否则,你不应该写 logResult (Left _)logResult (Right _)常常;本质上对待logResult . LeftlogResult . Right作为它们自己的函数,这使您回到实际将它们编写为单独的函数。

    But in logError I am explicitly passing Left MyError. Shouldn't this disambiguate?



    不,不应该。问题的结束和开始是logResult看起来像这样:
    logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
    

    当你调用它时,实现并不重要。类型说你需要ToJSON a —您需要提供ToJSON a .而已。如果你知道你不需要ToJSON a对于 Left值,然后您拥有未反射(reflect)在类型中的有用信息。您应该将该信息添加到类型中,在这种情况下,这意味着将其分成两部分。 (IMO)实际上是错误的语言设计来允许您的想法,因为停止问题应该使其无法正确执行。

    关于haskell - 如何不对使用更通用函数的受限函数应用实例约束?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56283441/

    相关文章:

    haskell - 'many' 类型类中的“some”和 'Alternative' 函数

    haskell - 在 Haskell 中导入时隐藏类型类实例声明

    haskell - 具有多个值的 optparse-applicative 选项

    haskell - Haskell中的参数数量和无点

    optimization - 如何正确使用GHC的SPECIALIZE pragma? (例如 : specializing pure function from monadic ones using Identity. )

    xml - 在 Arrow proc 上下文中提取多个元素

    scala - 一元类型构造函数的类型类模式

    haskell - 如何向 Cabal 包添加变更日志?

    haskell - 无法将预期类型 ‘r’ 与实际类型 ‘Horse’ 匹配

    haskell - 从手指树文章中找到丢失的 'Reduce' 类型类