首页 > 解决方案 > 为什么haskell不能推导出这个多参数类型类?

问题描述

我有两个类型类

class Concatable a where
  empty :: a
  (<+>) :: a -> a -> a

class Concatable b => Output a b where
  out :: a -> b

和以下功能

nl :: (Output a AnsiDark) => [a] -> AnsiDark
nl a = foldr addNl empty a
  where
    addNl :: a -> AnsiDark -> AnsiDark
    addNl ast org = doAddIf ast <+> org
    doAddIf :: a -> AnsiDark
    doAddIf ast = if out ast == sDedent
                      then out ast
                      else out ast <+> sNewline

( AnsiDarkimplements Concatable,sDedent是一个类型的常量AnsiDark)

以及启用了以下语言扩展(这甚至可能与问题无关,我对这些复杂类型相关的问题相当陌生)

{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}

我收到以下错误:

../src-hs/ANSITerm.hs:65:22: error:
    • Could not deduce (Output a1 AnsiDark) arising from a use of ‘out’
      from the context: Output a AnsiDark
        bound by the type signature for:
                   nl :: forall a. Output a AnsiDark => [a] -> AnsiDark
        at ../src-hs/ANSITerm.hs:59:1-44
    • In the first argument of ‘(==)’, namely ‘out ast’
      In the expression: out ast == sDedent
      In the expression:
        if out ast == sDedent then out ast else out ast <+> sNewline

我不太明白为什么 haskell 无法推断a......我会求助于使用类型注释,out就像这样

out @a @AnsiDark

但是类型注释似乎不适用于类型变量。那么......我的问题到底是什么?我该如何解决?

标签: haskell

解决方案


nl :: (Output a AnsiDark) => [a] -> AnsiDark
...
    where
    doAddIf :: a -> AnsiDark
    ...

a出现在这两行的s 是不一样的。就好像你写过:

nl :: (Output x AnsiDark) => [x] -> AnsiDark
...
    where
    doAddIf :: y -> AnsiDark
    ...

由于您正在使用outindoAddif您需要将Output约束添加到其签名中(我相信如果您删除签名它将起作用,因为将推断出正确的签名)。

您可能还对ScopedTypeVariables扩展感兴趣。启用此功能后,如果您编写

nl :: forall a. (Output a AnsiDark) => [a] -> AnsiDark

然后您可以在子句中的签名以及您尝试过的类型应用程序中引用它。 awhereout @a @AnsiDark


推荐阅读