Haskell 根据变量类型执行不同的函数

Haskell function that executes differently depending on type of variable

更具体地说,假设我有一些数据构造函数

data Foo = ... deriving Eq

还有下面这个愚蠢的函数

f :: Eq a => a -> Bool

在变量 a 实际上是类型 Foo 的情况下,我想要 f输出 True。在所有其他情况下(即对于 Eq 的所有其他实例),我希望 f 输出 False

起初我想也许我可以为此目的定义一个新类型class

class IsFoo a where
    isFoo :: a -> Bool

虽然为 Foo 编写 IsFoo 的实例很容易,但显然我不想对所有类型都这样做是方程式的实例。

回答时,您可以假设 Foo 有很多构造函数,并且我不想对所有构造函数进行模式匹配。我也不想使用 Data.Typeable (我读过它是不好的风格)。有没有一种方法可以优雅自然地完成我想要的(w.r.t。Haskell)?

我认为您不需要这样做。这对我来说似乎是一个严重的 XY 问题,因为 Haskell 的类型系统通常应该为你做这些事情。


但是,这是可能的。实现这一点的最简单方法确实是使用类型类:

data Foo = A | B | C | D | ... | Z deriving Eq

class IsFoo a where
  isFoo :: a -> Bool

instance IsFoo Foo where
  isFoo = const True

instance IsFoo x where
  isFoo = const False

使用FlexibleInstances extension, you save yourself some work by simply returning True when given an argument of type Foo, which is specified in the instance for type Foo and False when calling isFoo with a variable of any other type. Note that you also have to use the extension OverlappingInstances,否则使用Foo类型的参数调用isFoo会发生运行时错误,因为程序不知道要使用哪个实例。要启用这些扩展,只需包含

{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}

在源文件的顶部。

仍然:我强烈建议您尝试使用不同的方法来解决您的问题,因为通常您不必处理此类 "low-level" 打字内容。

如果这确实是您想要做的,我建议您使用 Data.Typeable,因为它非常适合这个目的:

import Data.Maybe (isJust)
import Data.Typeable

isFoo :: (Typeable a) => a -> Bool
isFoo x = isJust (cast x :: Maybe Foo)

糟糕风格的问题与使用像 Data.Typeable 这样的特定库无关。这是关于没有正确使用 Haskell 的类型系统,特别是将其视为动态 OO 语言。如果您需要确定某个泛型类型是 Foo 还是不是,那么您在某处忘记了类型信息。但是在Haskell中,你总是在编译时有这个,所以不需要动态确定它。

或许可以解释一下您想要实现的目标,很可能有更惯用的方法来实现。