Haskell 如何在实例方法中应用函数约束?

How do you apply function constraints in instance methods in Haskell?

我正在学习如何在 Haskell 中使用 typeclasses

考虑以下带有类型约束 class 函数 f.

的类型class T 的实现
class T t where
    f :: (Eq u) => t -> u 

data T_Impl = T_Impl_Bool Bool | T_Impl_Int Int | T_Impl_Float Float 
instance T T_Impl where
    f (T_Impl_Bool x) = x
    f (T_Impl_Int x) = x
    f (T_Impl_Float x) = x

当我将其加载到 GHCI 7.10.2 中时,出现以下错误:

Couldn't match expected type ‘u’ with actual type ‘Float’
      ‘u’ is a rigid type variable bound by
          the type signature for f :: Eq u => T_Impl -> u 
          at generics.hs:6:5

Relevant bindings include
  f :: T_Impl -> u (bound at generics.hs:6:5)
In the expression: x
In an equation for ‘f’: f (T_Impl_Float x) = x

我doing/understanding哪里错了?在我看来,人们希望通过提供伴随的数据构造函数和函数实现来专门化实例中的类型 class 似乎是合理的。部分

Couldn't match expected type 'u' with actual type 'Float'

特别令人困惑。为什么 u 不匹配 Float 如果 u 仅具有它必须符合 Eq 类型的约束(Floats 做那个 afaik)?

签名

f :: (Eq u) => t -> u 

意味着 caller 可以根据需要选择 tu,唯一的负担是确保 u 是 class Eq(和 class Tt -- 在 class 方法中有一个隐含的 T t 约束)。

不代表实现可以任意选择u.

因此,调用者可以通过以下任何方式使用 f:(在 class T 中使用 t

f :: t -> Bool 
f :: t -> Char
f :: t -> Int
... 

编译器抱怨您的实现不够通用,无法涵盖所有​​这些情况。

Couldn't match expected type ‘u’ with actual type ‘Float’ 

表示"You gave me a Float, but you must provide a value of the general type u (where u will be chosen by the caller)"

Chi 已经指出了您的代码无法编译的原因。但这甚至不是 typeclasses 的问题;实际上,您的示例只有一个实例,因此它也可能是一个普通函数而不是 class.

从根本上说,问题在于您正在尝试做类似

的事情
foobar :: Show x => Either Int Bool -> x
foobar (Left  x) = x
foobar (Right x) = x

这行不通。它试图根据您在 运行 时输入的值使 foobar return 成为不同的类型。但在 Haskell 中,所有类型都必须在 编译时 100% 确定。所以这行不通。

但是,您可以做几件事。

首先,你可以这样做:

foo :: Either Int Bool -> String
foo (Left  x) = show x
foo (Right x) = show x

换句话说,不是 return 可以展示的东西,而是 实际展示它 。这意味着结果类型总是 String。这意味着调用哪个版本的 show 会在 运行 时发生变化,但这没关系。代码路径可以在 运行 时间变化,它是 types 不能。

可以做的另一件事是:

toInt :: Either Int Bool -> Maybe Int
toInt (Left  x) = Just x
toInt (Right x) = Nothing

toBool :: Either Int Bool -> Maybe Bool
toBool (Left  x) = Nothing
toBool (Right x) = Just x

再说一遍,效果很好。

您还可以做其他事情;不知道你为什么想要这个,很难推荐其他人。

附带说明一下,您要停止将其视为面向对象编程。它不是。它需要一种新的思维方式。特别是,除非您确实需要,否则不要使用 typeclass。 (我意识到这个特殊的例子可能只是一个学习类型的学习练习class当然......)

可以这样做:

class Eq u => T t u | t -> u where
    f :: t -> u

调用站点需要 FlexibleContextx+FunctionalDepencencies 和 MultiParamTypeClasses+FlexibleInstances。或者消除 class 并使用数据类型代替 Gabriel 显示 here