如何无需模式匹配所有变体即可访问sum type中的公共字段?
问题
我定义了多个sum type,每个类型的所有变体都包含一个公共字段a,示例代码如下:
data T1 a = C1 String a | C2 Int a | C3 Bool a
data T2 a = C4 Int Int a | C5 [String] a
请问是否无需对所有类型的所有变体进行模式匹配,就能访问这个a字段?(此问题源于AST定义场景,希望能简洁获取节点特定信息)
解决方案
1. 自定义类型类
可以抽象出一个专门用于提取a字段的类型类,为每个sum type实现该类的实例,之后就能用统一的函数获取字段:
class HasA t where getA :: t a -> a instance HasA T1 where getA (C1 _ a) = a getA (C2 _ a) = a getA (C3 _ a) = a instance HasA T2 where getA (C4 _ _ a) = a getA (C5 _ a) = a
使用时直接调用getA即可,无需每次写全模式匹配:
-- 示例:提取T1、T2实例中的a字段 extractT1 :: T1 Int -> Int extractT1 = getA extractT2 :: T2 String -> String extractT2 = getA
2. 重构AST数据结构(推荐)
如果这些sum type是AST节点,更合理的设计是把公共字段a抽出来,用一个统一的包裹类型封装:
-- 定义不同节点的具体内容类型 data ASTContent a = NodeStr String | NodeInt Int | NodeBool Bool | NodeDoubleInt Int Int | NodeStrList [String] -- 统一的AST节点类型,包含公共字段a和具体内容 data ASTNode a = ASTNode { nodeContent :: ASTContent a , nodeA :: a -- 公共字段直接作为记录字段 }
这样提取a字段只需直接访问nodeA,完全不需要模式匹配,后续新增AST节点时,只需扩展ASTContent即可,公共逻辑无需修改。
3. 借助泛型自动提取(依赖GHC扩展)
如果不想手动编写类型类实例,可以利用GHC的泛型功能,自动提取每个变体的最后一个字段(要求a始终是变体的最后一个字段)。
首先启用必要的GHC扩展:
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}
然后编写通用的提取函数:
import GHC.Generics import Generics.SOP -- 为原有类型派生Generic data T1 a = C1 String a | C2 Int a | C3 Bool a deriving (Generic) data T2 a = C4 Int Int a | C5 [String] a deriving (Generic) -- 通用提取最后一个字段的函数 getA :: (Generic (t a), HasDatatypeInfo (t a), All (LastField a) (Code (t a))) => t a -> a getA = unK . lastField . unSOP . from where lastField :: All (LastField a) xss => NP I xss -> K a xss lastField = hcollapse . hcliftA (Proxy @(LastField a)) (K . getLastField) -- 定义类型类处理不同长度的变体字段 class LastField a xs where getLastField :: I xs -> a instance LastField a (x : y : z : '[a]) where getLastField (I (_, _, _, a)) = a instance LastField a (x : '[a]) where getLastField (I (_, a)) = a instance LastField a '[a] where getLastField (I a) = a
这种方法适合字段位置固定的场景,但需要依赖generics-sop库,且对代码的可读性有一定影响。
内容的提问来源于stack exchange,提问作者string_loginUsername
相关产品推荐
相关产品推荐

