You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 15:15:02