如何覆盖aeson包中ToJSON (Ratio Integer)的类型类实例?
解决Aeson中
Ratio Integer的ToJSON实例重叠问题 优先推荐:用newtype包装类型
Haskell的类型类实例绑定到具体类型,直接覆盖已有实例会触发重叠错误,最稳妥的方式是用newtype给Ratio Integer套一层新类型,再为这个新类型定义你需要的ToJSON实例:
import Data.Aeson (ToJSON(..)) import Data.Ratio (Ratio, numerator, denominator) import Data.Text (pack) newtype FloatRatio = FloatRatio (Ratio Integer) instance ToJSON FloatRatio where toJSON (FloatRatio r) = let floatVal = (fromIntegral $ numerator r) / (fromIntegral $ denominator r) :: Double in String $ pack $ show floatVal
使用时把Ratio Integer值用FloatRatio包装即可,比如toJSON $ FloatRatio (3 % 4)会输出"0.75"。
不推荐:启用重叠实例编译选项
如果一定要直接覆盖原实例,可以通过GHC编译pragma强制开启重叠实例支持,但这种做法会破坏类型类一致性,可能导致依赖默认实例的其他代码行为异常,维护成本很高:
在自定义实例上方添加{-# OVERLAPPING #-}pragma:
{-# OVERLAPPING #-} instance ToJSON (Ratio Integer) where toJSON r = let floatVal = (fromIntegral $ numerator r) / (fromIntegral $ denominator r) :: Double in String $ pack $ show floatVal
注意:原代码里直接用numerator r / denominator r是整数除法,结果还是整数,必须用fromIntegral转成浮点数类型才能得到正确的小数格式。
内容的提问来源于stack exchange,提问作者Shawn Zhang
相关产品推荐
相关产品推荐

