更具体地说,假设我有一些数据构造函数
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)的方式完成我想要的东西?
答案 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中,你总是在编译时有这个,所以不应该需要动态地确定它。
或许解释一下你想要实现的目标,可能会有更为惯用的方法。