Haskell中如何简洁提取重复出现的多组类型类约束?
解决Haskell重复类型类约束的实用方案
太懂这种重复写长串约束的痛苦了——30多次重复不仅让代码臃肿不堪,后续修改更是繁琐到离谱!这里给你两个针对性的解决办法,优先推荐第一种,简单直接还省心:
1. 约束同义词(Constraint Synonym)
这是最直接的解决方案,GHC支持通过ConstraintKinds扩展把一组约束打包成一个自定义的名字,一次性搞定重复问题。
首先在代码文件开头启用必要的扩展:
{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}
然后定义你的约束同义词,把所有重复的约束打包进去:
-- 给这个约束组起个直观的名字,比如NumericContext type NumericContext a = (RealFloat a, Floating a, Real a, Show a, Ord a, Typeable a)
接下来,所有需要这组约束的函数,直接用这个同义词代替长串约束就行:
-- 原来要写一长串约束,现在只用一个名字 calculateVolume :: NumericContext a => a -> a -> a calculateVolume r h = pi * r^2 * h logValue :: NumericContext a => a -> IO () logValue x = putStrLn $ "Logged value: " ++ show x
好处:以后要修改约束(比如加个Enum a或者删掉Typeable a),只需要修改NumericContext这一处,所有用到它的函数都会自动同步,彻底告别重复修改的噩梦!
2. 自定义类型类(适合需扩展方法的场景)
如果除了打包约束,你还想给这些类型添加一些通用方法,那可以定义一个继承所有必要约束的自定义类型类,并让满足条件的类型自动实例化:
先启用所需扩展:
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-}
然后定义类型类:
class (RealFloat a, Floating a, Real a, Show a, Ord a, Typeable a) => NumericType a where -- 这里可以添加你需要的通用方法,比如: roundToTwo :: a -> a roundToTwo x = fromIntegral (round (x * 100)) / 100
最后给所有满足基础约束的类型自动生成实例:
instance (RealFloat a, Floating a, Real a, Show a, Ord a, Typeable a) => NumericType a where
之后函数里就可以用NumericType a作为约束,同时还能调用你定义的通用方法:
printRounded :: NumericType a => a -> IO () printRounded x = print $ roundToTwo x
注意:这种方式比约束同义词重一些,如果只是单纯打包约束,优先选第一种;如果需要扩展方法,这个方案更合适。
内容的提问来源于stack exchange,提问作者kye
相关产品推荐
相关产品推荐

