实现ContentType关联数据项的优雅类型安全方案
实现类型安全的ContentType与内容关联结构
要让ContentType和对应内容的类型强制绑定,避免出现类型不匹配的错误,Haskell里有两种常用的实现方式:
方法一:使用GADTs(广义代数数据类型)
这种方式最直观,直接让每个Encoding构造器对应固定的ContentType和内容类型:
{-# LANGUAGE GADTs #-} data ContentType = MyInt | MyBool deriving (Show) data Encoding a where EncodingInt :: [Int] -> Encoding Int EncodingBool :: [Bool] -> Encoding Bool -- 从Encoding实例中获取对应的ContentType contentType :: Encoding a -> ContentType contentType (EncodingInt _) = MyInt contentType (EncodingBool _) = MyBool -- 获取实例中的内容 content :: Encoding a -> [a] content (EncodingInt xs) = xs content (EncodingBool xs) = xs
效果验证
- 合法实例可以正常构造:
intEncoding :: Encoding Int intEncoding = EncodingInt [1,2,3] boolEncoding :: Encoding Bool boolEncoding = EncodingBool [True, False] - 尝试构造类型不匹配的实例会直接编译报错:
-- 以下代码无法通过编译 -- badEncoding :: Encoding Int -- badEncoding = EncodingBool [True]
方法二:使用类型族(Type Families)
如果想保留类似原代码的记录结构,可以用类型族建立ContentType和内容类型的映射关系:
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} data ContentType = MyInt | MyBool deriving (Show) -- 定义类型族,将ContentType类型映射到对应的内容类型 type family ContentTypeToType (ct :: ContentType) where ContentTypeToType 'MyInt = Int ContentTypeToType 'MyBool = Bool -- 带类型约束的Encoding记录,确保content类型和ContentType严格对应 data Encoding (ct :: ContentType) = Encoding { contentType :: ContentType , content :: [ContentTypeToType ct] } deriving (Show)
效果验证
- 合法实例构造:
intEncoding :: Encoding 'MyInt intEncoding = Encoding { contentType = MyInt, content = [1,2,3] } boolEncoding :: Encoding 'MyBool boolEncoding = Encoding { contentType = MyBool, content = [True, False] } - 类型不匹配的实例会编译失败:
-- 以下代码无法通过编译 -- badEncoding :: Encoding 'MyInt -- badEncoding = Encoding { contentType = MyInt, content = [True] }
结合动态选择的场景
如果要和chooseContentType这类动态选择逻辑结合,可以用存在类型包装Encoding,实现动态类型下的安全处理:
{-# LANGUAGE GADTs #-} {-# LANGUAGE ExistentialQuantification #-} data SomeEncoding = forall a. SomeEncoding (Encoding a) chooseEncoding :: IO SomeEncoding chooseEncoding = do ct <- chooseContentType case ct of MyInt -> return $ SomeEncoding (EncodingInt [1,2,3]) MyBool -> return $ SomeEncoding (EncodingBool [True, False])
内容的提问来源于stack exchange,提问作者gust
相关产品推荐
相关产品推荐

