Haskell中如何定义支持任意Num类型的多态Expr数据类型
解决方案
你需要的是带类型参数的多态数据类型,以下两种实现分别对应不同的使用场景:
1. 基础参数化实现(完全匹配题目要求)
不需要开启任何GHC扩展,即可支持你给出的expr1、expr2写法:
data Expr a = Number a | Plus (Expr a) (Expr a) | Times (Expr a) (Expr a) deriving (Show, Eq)
说明
- 类型参数
a对应你存储的数值类型,只要a是Num的实例就可以正常使用 - 对于
expr1 = Times (Plus (Number (5.2 :: Double)) (Number 4)) (Number 2):Haskell类型推导会自动把同表达式中的4、2统一为Double类型,最终expr1的类型为Expr Double - 对于
expr2 = Plus (Number (2 :: Int)) (Times (Number 3) (Number 4)):同理3、4会统一为Int类型,最终expr2的类型为Expr Int
如果想要简化写法,还可以给Expr实现Num类型类,不用手动写Number包裹字面量:
instance Num a => Num (Expr a) where (+) = Plus (*) = Times fromInteger = Number . fromInteger -- 以下方法按需实现即可,不影响基础的表达式构造 negate = Times (Number (-1)) abs = undefined signum = undefined
实现后可以直接这样写表达式:
expr1 = (5.2 + 4) * 2 :: Expr Double expr2 = (2 + 3 * 4) :: Expr Int
2. 存在类型实现(支持同表达式混合不同Num类型)
如果你需要在同一个表达式中同时使用不同的Num类型(比如Plus (Number (1 :: Int)) (Number (2.2 :: Double))),可以开启ExistentialQuantification扩展实现:
{-# LANGUAGE ExistentialQuantification #-} data Expr = forall a. Num a => Number a | Plus Expr Expr | Times Expr Expr deriving (Show)
说明
- 该实现中
Expr不需要类型参数,Number构造子可以接收任意属于Num类型类的数值 - 该方案无法自动推导
Eq实例,因为不同类型的数值无法直接比较,需要先实现表达式的求值逻辑,再对求值结果进行比较
内容的提问来源于stack exchange,提问作者antonsmoke
相关产品推荐
相关产品推荐

