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

Mic*_* Wu 1 polymorphism haskell

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

data Foo = ... deriving Eq
Run Code Online (Sandbox Code Playgroud)

以及愚蠢的功能

f :: Eq a => a -> Bool
Run Code Online (Sandbox Code Playgroud)

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

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

class IsFoo a where
    isFoo :: a -> Bool
Run Code Online (Sandbox Code Playgroud)

虽然为Foo编写一个IsFoo实例很容易,但显然我不想对所有类型为Eq的类型执行此操作.

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

Thr*_*eFx 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
Run Code Online (Sandbox Code Playgroud)

使用FlexibleInstances扩展,通过简单地返回True给定的类型参数,在类型Foo的实例中指定Foo并且使用任何其他类型的变量False调用时,可以节省一些工作isFoo.请注意,您还必须使用扩展OverlappingInstances,否则将isFoo使用类型的参数调用运行时错误,Foo因为程序将不知道要使用哪个实例.要启用这些扩展,只需包含

{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
Run Code Online (Sandbox Code Playgroud)

在源文件的顶部.

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


Pet*_*lák 5

如果这真的是你想要做的,我建议你使用Data.Typeable,因为它完全适合这个目的:

import Data.Maybe (isJust)
import Data.Typeable

isFoo :: (Typeable a) => a -> Bool
isFoo x = isJust (cast x :: Maybe Foo)
Run Code Online (Sandbox Code Playgroud)

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

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