Haskell 作为类型类实例的函数?

Haskell 作为类型类实例的函数?,haskell,typeclass,Haskell,Typeclass,{-#语言LambdaCase} 我有一系列函数,它们以各种方式编码失败。例如: f::A->Bool在失败时返回False g::B->Maybe B'失败时返回Nothing h::C->任一错误C'在失败时返回Left… 我希望以与Maybemonad相同的方式链接这些操作,因此链接函数需要在继续下一个函数之前知道每个函数是否失败。为此,我编写了以下课程: class Fail a where isFail :: a -> Bool instance Fail () wher

{-#语言LambdaCase}

我有一系列函数,它们以各种方式编码失败。例如:

  • f::A->Bool
    在失败时返回
    False
  • g::B->Maybe B'
    失败时返回
    Nothing
  • h::C->任一错误C'
    在失败时返回
    Left…
我希望以与
Maybe
monad相同的方式链接这些操作,因此链接函数需要在继续下一个函数之前知道每个函数是否失败。为此,我编写了以下课程:

class Fail a where
  isFail :: a -> Bool
instance Fail () where
  isFail () = False
instance Fail Bool where -- a
  isFail = not
instance Fail (Maybe a) where -- b
  isFail = not . isJust
instance Fail (Either a b) where -- c
  isFail (Left _) = True
  isFail _ = False
但是,可能存在不一致的函数:

  • f':A->Bool
    在失败时返回
    True
  • g':B->Maybe Error
    在失败时返回
    Just Error
    成功时无任何内容
  • h':C->任意一个C'错误
    在失败时返回
    Right…
这些问题可以通过简单地使用转换它们的函数来解决,例如:

  • f'=not。f'
  • g'=(\case Nothing->Right();Just e->Left e)。g'
  • h'=(\case Left c->Right c;Right e->Left e)。h'
但是,链接功能的用户希望能够组合
f
g
h
f'
g'
、和
h'
,并让它们正常工作。他不会知道需要转换函数的返回类型,除非他查看正在组合的每个函数的语义,并检查它们是否与范围内的
Fail
实例匹配。对于普通用户来说,这是一个冗长而微妙的过程,尤其是在类型推断绕过用户必须选择正确实例的情况下

这些函数不是在了解如何使用它们的情况下创建的。因此,我可以制作一个类型
数据结果ab=Fail a | Success b
,并围绕每个函数制作包装。例如:

  • fR=(\case-True->suces();False->Fail())。f
  • f'R=(\case False->suces();True->Fail())。f'
  • gR=(\case Just a->success a;Nothing->Fail())。g
  • g'R=(\case Nothing->success();Just e->Fail e)。g'
  • hR=(\case Left e->Fail e;Right a->success a)。h
  • h'R=(\case Right e->Fail e;Left a->success a)。h'
然而,这感觉很脏。我们所做的只是验证/解释如何在组合函数的上下文中使用
f
g
h
f'
g'
,以及
h'
。有没有更直接的方法?我想要的是一种确切的方式,来说明每个函数应该使用
Fail
typeclass的哪个实例,即(使用上面给出的typeclass实例的名称),
f
→ <代码>a,
g
→ <代码>b,
h
→ <代码>c和
f'
→ <代码>a',
g'
→ <代码>b',
h'
c'
对于“无效”函数,其中
a'
b'
c'
被定义为以下实例(与前面的实例重叠,因此您需要能够以某种方式按名称选择它们):


不过,这不一定要通过类型类来完成。也许除了使用类型类,还有其他方法可以做到这一点?

不要这样做。Haskell的静态类型系统和引用透明性为您提供了非常有用的保证:您可以正确地确定某些特定值意味着相同的内容1,而不管它是如何产生的。与此无关的是,既没有易变性,也没有动态风格的表达式“运行时重新解释”,正如您所设想的任务所需要的那样

如果您在那里拥有的那些函数没有相应地遵循这样的规范,那么这是不好的。最好摆脱它们(至少,隐藏它们,只导出具有统一行为的重新定义的版本)。或者告诉用户,他们将不得不忍受查找每个产品的规格。但是,不要试图通过某种方式绕过这个定义不正确的特殊症状

一个简单的更改可以应用于“标记”函数,其中failure的意思与其他操作相反,就是让它们返回这样一个包装结果:

newtype Anti a = Anti { profail :: a }

instance (Anti a) => Fail (Anti a) where
  isFail (Anti a) = not $ isFail a


思想:“同一件事”在一个可能非常抽象的意义上。没有必要让
Left
成为一个通用的“失败构造函数”,很明显,它是与第一个类型参数相关联的变量构造函数,这是而不是函子/单子实例操作的对象,从这一点上,它会自动得出“这意味着”一元应用程序失败。
也就是说,当您选择了正确的类型时,内容应该几乎自动地明确无误;显然,当你只是这样做的时候,情况正好相反,所以也许你应该完全摆脱这些…

如果你想把它们像单子一样连在一起(使用
do
符号),那么你需要将它们全部转换成一个单一的类型,然后你可以为其创建一个单子实例。您声明希望类型系统在没有提示或上下文的情况下,只知道函数的失败意味着什么。理论上,我可以有无限多个函数,每个函数返回一个表示失败的不同整数,编译器如何知道特定整数何时失败?它只有一个作为上下文的值。你不能指望编译器为你编写程序,否则我们都会使用Agda。是的,我不一定要把它变成monad,但它会是类似的。我不希望编译器知道哪些整数是失败的,我想以某种方式指出
newtype Anti a = Anti { profail :: a }

instance (Anti a) => Fail (Anti a) where
  isFail (Anti a) = not $ isFail a