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

时间:2015-02-28 07:29:05

标签: haskell polymorphism

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

data Foo = ... deriving Eq

以下愚蠢的功能

f :: Eq a => a -> Bool

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

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

class IsFoo a where
    isFoo :: a -> Bool

虽然为 Foo 编写 IsFoo 的实例很容易,但显然我不想为所有类型的实例执行此操作方程式

回答时,您可以假设Foo有很多构造函数,并且我不想在所有构造函数上进行模式匹配。我也不想使用 Data.Typeable (我已经读过它的糟糕风格)。有没有办法以优雅和自然(w.r.t.Haskell)的方式完成我想要的东西?

2 个答案:

答案 0 :(得分:5)

在我看来,你不应该这样做。这对我来说似乎是一个严重的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扩展名,只需在给定True类型的参数时返回Foo,就可以节省一些工作量,该参数在类型Foo的实例中指定使用任何其他类型的变量调用False时{和isFoo。请注意,您还必须使用扩展名OverlappingInstances,否则将发生运行时错误,并使用类型为isFoo的参数调用Foo,因为程序将不知道要使用哪个实例。要启用这些扩展程序,只需添加

即可
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}

位于源文件的顶部。

仍然:我强烈建议您尝试不同的方法解决问题,因为一般情况下您不必处理这种“低级”打字事件。

答案 1 :(得分:5)

如果这确实是您想要做的,我建议您使用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中,你总是在编译时有这个,所以不应该需要动态地确定它。

或许解释一下你想要实现的目标,可能会有更为惯用的方法。