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中,你总是在编译时有这个,所以不需要动态确定它。
或许可以解释一下您想要实现的目标,很可能有更惯用的方法来实现。
更具体地说,假设我有一些数据构造函数
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中,你总是在编译时有这个,所以不需要动态确定它。
或许可以解释一下您想要实现的目标,很可能有更惯用的方法来实现。