如何实现Constraint反射?基于可用约束的特设多态方案
基于Constraint的特设多态实现(以安全整数除法为例)
你提到用Typeable可以根据具体类型实现分支逻辑,现在要扩展为检查类型是否满足某个Constraint来做分支,这里以处理有界/无界整数的安全除法为例,给出具体实现方案。
核心思路
利用Data.Constraint包的Dict类型携带约束实例,结合Typeable的类型匹配能力,判断当前类型是否满足目标约束(比如Bounded),从而在分支中启用对应约束的方法。
完整实现
首先导入必要模块:
import Data.Typeable import Data.Constraint import GHC.Enum (Bounded(minBound))
实现安全除法函数:
polymorphicSafeDiv :: forall a. (Integral a, Typeable a) => a -> a -> Maybe a polymorphicSafeDiv x y | y == 0 = Nothing | y == (-1) = case hasBounded @a of -- 类型满足Bounded约束,可使用minBound检查溢出 Just Dict -> if x == minBound then Nothing else Just (x `div` y) -- 类型无Bounded约束(如Integer),直接计算无溢出风险 Nothing -> Just (x `div` y) | otherwise = Just (x `div` y) where -- 判断当前Integral类型是否具有Bounded约束 hasBounded :: forall b. (Integral b, Typeable b) => Maybe (Dict (Bounded b)) hasBounded = lookupBoundedDict where lookupBoundedDict = case eqT @b @Int of Just Refl -> Just Dict Nothing -> case eqT @b @Word of Just Refl -> Just Dict Nothing -> case eqT @b @Int8 of Just Refl -> Just Dict Nothing -> case eqT @b @Int16 of Just Refl -> Just Dict Nothing -> case eqT @b @Int32 of Just Refl -> Just Dict Nothing -> case eqT @b @Int64 of Just Refl -> Just Dict Nothing -> case eqT @b @Word8 of Just Refl -> Just Dict Nothing -> case eqT @b @Word16 of Just Refl -> Just Dict Nothing -> case eqT @b @Word32 of Just Refl -> Just Dict Nothing -> case eqT @b @Word64 of Just Refl -> Just Dict Nothing -> Nothing
代码说明
hasBounded辅助函数:通过eqT匹配标准的有界整数类型(Int、Word、各种位宽的有界整数),匹配成功则返回Just Dict——这个Dict相当于"证明"当前类型满足Bounded约束,让我们可以在分支中使用minBound等方法。- 溢出处理分支:当除数为-1时,若类型是有界整数,检查被除数是否等于
minBound(此时除以-1会溢出),若是则返回Nothing;无界类型(如Integer)则直接返回计算结果。 - 基础逻辑:除零直接返回
Nothing,其他情况正常执行除法。
测试用例
-- 无界Integer:无溢出风险,正常返回 >>> polymorphicSafeDiv (minBound :: Integer) (-1) Just 9223372036854775808 -- 有界Int:溢出场景,返回Nothing >>> polymorphicSafeDiv (minBound :: Int) (-1) Nothing -- 正常除法 >>> polymorphicSafeDiv 10 2 Just 5 -- 除零场景 >>> polymorphicSafeDiv 10 0 Nothing
简化写法(可选)
如果觉得逐个匹配类型太繁琐,也可以用typeRep直接比较类型标识,效果一致:
hasBounded :: forall b. (Integral b, Typeable b) => Maybe (Dict (Bounded b)) hasBounded | typeRep @b == typeRep @Int = Just Dict | typeRep @b == typeRep @Word = Just Dict | typeRep @b == typeRep @Int8 = Just Dict | typeRep @b == typeRep @Int16 = Just Dict | typeRep @b == typeRep @Int32 = Just Dict | typeRep @b == typeRep @Int64 = Just Dict | typeRep @b == typeRep @Word8 = Just Dict | typeRep @b == typeRep @Word16 = Just Dict | typeRep @b == typeRep @Word32 = Just Dict | typeRep @b == typeRep @Word64 = Just Dict | otherwise = Nothing
内容的提问来源于stack exchange,提问作者Mathias Sven
相关产品推荐
相关产品推荐

