函数作为类型类的实例?

时间:2015-02-23 22:05:57

标签: haskell typeclass

{-# LANGUAGE LambdaCase #-}

我有一堆以各种方式编码失败的函数。例如:

  • f :: A -> Bool在失败时返回False
  • g :: B -> Maybe B'在失败时返回Nothing
  • h :: C -> Either Error 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(成功时Nothing
  • h' :: C -> Either C' Error在失败时返回Right ...

这些可以通过简单地用变换它们的函数包装它们来解决,例如:

  • f'' = not . f'
  • g'' = (\case Nothing -> Right (); Just e -> Left e) . g'
  • h'' = (\case Left c -> Right c; Right e -> Left e) . h'

但是,链接功能的用户希望能够合并fghf'g'和{{ 1}}并让他们工作。他不会知道函数的返回类型需要转换,除非他查看他正在组合的每个函数的语义,并检查它们是否与范围内的h'个实例匹配。对于普通用户来说,这是一件单调乏味且过于微妙的事情,特别是在类型推断中绕过用户必须选择正确的实例。

这些功能并非在了解如何使用这些功能的情况下创建。所以我可以创建一个类型Fail并围绕每个函数创建包装器。例如:

  • data Result a b = Fail a | Success b
  • fR = (\case True -> Sucess (); False -> Fail ()) . f
  • f'R = (\case False -> Sucess (); True -> Fail ()) . f'
  • gR = (\case Just a -> Sucess a; Nothing -> Fail ()) . g
  • g'R = (\case Nothing -> Sucess (); Just e -> Fail e) . g'
  • hR = (\case Left e -> Fail e; Right a -> Sucess a) . h

然而,这感觉很脏。我们正在做的只是证明/解释每个h'R = (\case Right e -> Fail e; Left a -> Sucess a) . h'fghf'g'是如何进行的在组合函数的上下文中使用。有更直接的方法吗?我想要的确切方法是说明每个函数应使用h'类型类的哪个实例,即(使用上面提到的类型类实例的名称),Fail→{{1} },fagbhcf'a'g'b'用于“无效”功能,其中h'c'a'被定义为以下实例(与之前的实例重叠) ,所以你需要能够以某种方式通过名字选择它们:

b'

但它不一定要通过类型类来完成。除了类型类别之外,还有其他方法可以做到这一点吗?

1 个答案:

答案 0 :(得分:10)

不要这样做。 Haskell的静态类型系统和引用透明性为您提供了非常有用的保证:您可以正确地确定某个特定的意味着同样的事情 1 ,无论它是如何生成的。既不是可以干扰这一点,也不是动态风格的“运行时重新解释”表达式,因为你需要你想象的任务。

如果那里的那些功能没有相应地遵守这样的规范,那么这很糟糕。更好地摆脱它们(至少,隐藏它们并且只导出具有统一行为的重新定义的版本)。或者告诉用户他们必须要查看每个用户的规格。但是,不要试图破解这种破坏定义的特定症状。

一个简单的改变你可以应用于仅仅“标记”失败意味着相反的函数,否则它们会让它们返回这样的包装结果:

newtype Anti a = Anti { profail :: a }

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

1 心灵:可能非常抽象的“同样的东西”。 Left无需普遍成为“失败的构造函数”,显然它是与第一个类型参数关联的变体构造函数就足够了,是什么functor / monad实例在上运行 - 从而自动跟随这将在monadic应用程序中“意味着”失败。
即,当你选择了正确的类型时,东西应该是非常自动的;当你只是tossing around booleans时,显然恰恰相反,所以也许你应该完全摆脱那些......