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

时间:2016-06-02 10:33:55

标签: haskell constraints instance typeclass

我正在学习如何在Haskell中使用类型类

考虑具有类型约束类函数T的类型类f的以下实现。

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

我在做什么/理解错误?我觉得有必要通过提供一个伴随数据构造函数和函数实现来专门化一个实例中的类型类。部分

  

无法匹配预期类型' u'实际类型' Float'

特别令人困惑。如果u只有Float类型必须符合u类型(Eq s那样做)的约束,为什么FloatIf Not WScript.Arguments.Named.Exists("elevate") Then CreateObject("Shell.Application").ShellExecute WScript.FullName _ , WScript.ScriptFullName & " /elevate", "", "runas", 1 WScript.Quit End If 'Your code goes here 不匹配?

3 个答案:

答案 0 :(得分:15)

签名

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

表示来电者可以根据需要选择tu,唯一的责任是确保u属于Eq类(以及t类的T - 在类方法中,存在隐式T t约束。)

这并不意味着实施可以选择任何u

因此,来电者可以通过以下任何方式使用f :(在课程t中使用T

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

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

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

表示"您向我提供了Float,但您必须提供一般类型u的值(调用方将选择u)&#34 ;

答案 1 :(得分:8)

Chi已经指出了为什么你的代码不能编译。但它甚至不是类型问题;实际上,你的例子只有一个实例,所以它也可能是一个普通的函数,而不是一个类。

从根本上说,问题是你正在尝试做类似

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

这不起作用。它会尝试使foobar返回不同的类型,具体取决于您在运行时提供的值。但是在Haskell中,所有类型必须在编译时确定100%。所以这不起作用。

然而,可以做几件事。

首先,你可以这样做:

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

换句话说,实际上是显示它,而不是返回可显示的内容。这意味着结果类型始终为String。这意味着调用哪个版本的show会在运行时发生变化,但这很好。代码路径在运行时可能会有所不同,它的类型不能。

可以做的另一件事是:

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

再次,这完全正常。

可以做其他事情;不知道你为什么要这样,很难建议其他人。

作为旁注,你想要停止思考它,就像它的面向对象编程一样。事实并非如此。它需要一种新的思维方式。特别是,除非你真的需要一个类型类,否则不要使用它。 (我意识到这个特殊的例子可能仅仅是学习类型课程的学习练习......)

答案 2 :(得分:0)

可以这样做:

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

您需要在呼叫站点上使用FlexibleContextx + FunctionalDepencencies和MultiParamTypeClasses + FlexibleInstances。或者消除类并改用数据类型,例如Gabriel展示here