在Haskell函数中返回类的实例

时间:2014-03-02 21:16:08

标签: haskell return instance

如果函数的返回是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

我试图在互联网资源上找到这个确切的解释,但我没有找到......你能告诉我一些吗?

3 个答案:

答案 0 :(得分:11)

类型Eq a => a并不意味着“实现Eq的类型”,而是“实现Eq的任何类型。”例如,如果使用undefined实现函数:< / p>

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或其他任何其他功能)

答案 1 :(得分:3)

除了@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编译器)支持它。不幸的是,我目前无法尝试。

答案 2 :(得分:1)

在阅读了上面的答案以及本页顶部链接的相关主题后,我得出结论,解决方案是使用存在量化类型。 因此,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