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

如何让Haskell在编译时限制m2k与k2m函数的参数类型

编译阶段捕获Haskell单位转换的类型错误

原问题代码

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Bytes = B Double deriving  (Num,Fractional,Show)
newtype Grams = G Double deriving  (Num,Fractional,Show)
newtype Time = Sec Double deriving (Num,Fractional,Show)

data S a = M a | K a | U a deriving Show

mkM :: Num a => a -> S a
mkM a = M a

mkK :: Num a => a -> S a
mkK a = K a

mkU :: Num a => a -> S a
mkU a = U a

eval :: Num a => S a -> a
eval (M a) = a * (10^6)
eval (K a) = a * (10^3)
eval (U a) = a 

m2k :: Num a => S a-> S a 
m2k (M a)  = K(a*1000)  

k2m :: Fractional a => S a-> S a
k2m (K a) = (M (a/1000.0))

x = mkK $ B 100
y = m2k x

问题说明

上述代码可在GHCi中加载,但y = m2k x会在运行时失败——因为m2k仅处理M构造器生成的S a值,却被传入了K构造的实例。希望让这类错误在编译阶段就被捕获,而非等到运行时才暴露。

简便实现方案:拆分数据类型

最直观的方式是把原来的求和类型S a拆分成三个独立的newtype,让每个构造器对应单独的类型,这样函数的参数类型就能被严格约束,编译器直接在编译阶段检查类型匹配性。

修改后的代码如下:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Bytes = B Double deriving (Num, Fractional, Show)
newtype Grams = G Double deriving (Num, Fractional, Show)
newtype Time = Sec Double deriving (Num, Fractional, Show)

-- 将原S a拆分为三个独立的newtype
newtype M a = M a deriving (Num, Fractional, Show)
newtype K a = K a deriving (Num, Fractional, Show)
newtype U a = U a deriving (Num, Fractional, Show)

-- 用类型类定义统一的eval逻辑
class Eval s where
  eval :: s a -> a

instance Eval M where
  eval (M a) = a * 10^6

instance Eval K where
  eval (K a) = a * 10^3

instance Eval U where
  eval (U a) = a

-- m2k现在明确只接受M类型,返回K类型
m2k :: Num a => M a -> K a
m2k (M a) = K (a * 1000)

-- k2m明确只接受K类型,返回M类型
k2m :: Fractional a => K a -> M a
k2m (K a) = M (a / 1000.0)

-- 测试:x是K Bytes,传给m2k会直接编译报错
x = K $ B 100
-- y = m2k x  -- 编译错误:Couldn't match type ‘K’ with ‘M’

方案优势

  • 类型约束严格:m2k的参数类型被限定为M a,任何非M类型的传入都会触发编译错误,从根源避免运行时崩溃。
  • 代码可读性提升:每个类型的语义清晰,一眼就能看出函数的输入输出单位量级。
  • 实现成本低:仅需拆分原有数据类型,配合简单的类型类即可完成,无需复杂的类型级编程。

内容的提问来源于stack exchange,提问作者Dragno

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 22:33:39