【问题标题】:Returning an instance of a class in a Haskell Function [duplicate]在Haskell函数中返回类的实例[重复]
【发布时间】:2014-04-03 17:21:26
【问题描述】:

如果函数的返回是class ClassA,是否可以在这样的函数中返回ClassA 的任何实例?例如:someFunction :: (ClassA a) => String -> a

那么,为什么下面这个功能不起作用呢?注意StringEq 的一个实例

getAnyEq :: (Eq a) => String -> a
getAnyEq input  |input == "1" = "something"
                |otherwise = "other"

出现的错误是:

Could not deduce (a ~ [Char])
from the context (Eq a)
  bound by the type signature for getAnyEq :: Eq a => String -> a
  at src/InterceptorRegistry.hs:11:13-33
  `a' is a rigid type variable bound by
      the type signature for getAnyEq :: Eq a => String -> a
      at src/InterceptorRegistry.hs:11:13

我试图在 Internet 资源上找到这个确切的解释,但我没有找到...你能给我看一些吗?

【问题讨论】:

  • A 类型 Eq a => a 意味着 所有 我们所知道的 a 是它是 Eq 类的一个实例。在这种情况下,我们知道的更多(特别是 String 类型)。
  • 事实上它是重复的,但这些新答案帮助我理解了为什么getAnyEq 函数不起作用。

标签: haskell return instance


【解决方案1】:

Eq a => a 类型不是指“实现Eq 的类型”,而是“实现Eq 的任何类型。例如,如果您使用 undefined 实现函数:

getAnyEq :: (Eq a) => String -> a
getAnyEq str = undefined

以下函数编译正确(尽管在运行时会因未定义的错误而崩溃):

x,y,z :: Bool
x = getAnyEq "test" == "hello"
y = getAnyEq "test" == [Just (Right True)]
z = getAnyEq "test" == ("this", "sss")

不可能给出一个体面的函数实现,因为没有办法为结果生成值。

返回类型变量的函数仅在类型变量具有包含返回值的函数的类的实例时才有意义。例如考虑 Num 类:

class (Eq a, Show a) => Num a where
  (+) :: a -> a -> a
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a

(注意我是在一个相当旧的 ghc 版本上测试这个,你的 Num 可能没有 Eq 或 Show 约束)。

函数fromInteger返回一个a(不需要a作为输入),所以我们可以从那个类型类中得到一个a。一旦有了值,就可以使用其他函数。所以下面的函数起作用了:

getANum:: (Num a) => String -> a
getANum "zero" = fromInteger 0
getANum "asdf" = fromInteger 46
getANum _ = fromInteger 1

> getANum "asdf"
46

请注意,由于文字整数被有效地解析为fromInteger <num>,因此上述函数中的fromInteger 函数调用实际上并不是必需的。我只是将它们包括在内以展示它是如何工作的。

可用于检索值的其他常见类型类有:

  • Monad(使用return
  • Applicative(使用pure
  • Monoid(使用mempty
  • Read(使用read或其任何其他功能)

【讨论】:

  • 即使在报告中,GHC 已经从Num a 中删除了所有超类(Eq a, Show a)
【解决方案2】:

除了@David Miani 的精彩回答,我还要补充一点,标准 Haskell 类型系统中的每个函数类型声明都暗示了一个 forall(或 ∀)量词:

getAnyEq :: (Eq a) => String -> a

在语义上等价于

getAnyEq :: forall a . (Eq a) => String -> a

您可以尝试使用 {-# LANGUAGE ExplicitForall #-} 扩展名。这意味着,从字面上看,对于每个类型a 受类型类Eq 约束,都有一个具有给定类型的函数getAnyEq。但是,您只提出了单一类型(String)的定义,而不是forall

我建议你的定义对另一个量词有效,∃:

getAnyEq :: exists a . (Eq a) => String -> a

它不是由 GHC 实现的,但例如过时的 UHC(Utrecht Haskell 编译器)支持它。很遗憾,我目前无法尝试。

【讨论】:

    【解决方案3】:

    在阅读了上面的答案和本页顶部链接的相关主题后,我得出结论,解决方案是使用存在量化类型。 所以,我的getAnyEq 函数的解决方案是:

    {-# LANGUAGE ExistentialQuantification #-}
    data ShowEq = forall s. Eq s => SE s 
    
    getAnyEq :: String -> ShowEq
    getAnyEq input  |input == "1" = SE "ds"
                    |otherwise = SE "ss"
    

    解释这些类型的一个非常有用的链接是:http://en.wikibooks.org/wiki/Haskell/Existentially_quantified_types

    【讨论】:

    • 起初我想在我的评论中说exists 可以通过forall 和更高级别的类型替代表达,但无法转换这个特定的例子:) 谢谢。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-04
    • 1970-01-01
    • 2012-12-10
    • 1970-01-01
    • 2017-11-15
    相关资源
    最近更新 更多