You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Haskell的EvalATree类型实现Read实例?已写解析器但无法应用

EvalATree类型的Read实例实现方案

类型定义

data EvalATree b a = Leaf a | Node ([b] -> a) [EvalATree b a]

已实现的Show与Foldable实例(a=b时)

instance (Show a, Show a) => Show (EvalATree a a) where
  show :: (Show a, Show a) => EvalATree a a -> String
  show (Leaf a) = "Leaf " ++ show a
  show (Node g trees) = "Node " ++ show (calcNode (Node g trees)) ++ " " ++ show trees

instance Foldable (EvalATree a) where
  foldr f z (Leaf x) = f x z
  foldr f z (Node func subtrees) = foldr (\subtree acc -> foldr f acc subtree) z subtrees

问题

无法实现该类型的Read实例,原因是Node构造器中的函数仅以名称形式存在,需要从字典中获取对应函数;已编写解析器,但不知道如何将其应用到Read实例中:

parseEvalATree :: (Read a, Read a) =>  Parser a -> Parser (EvalATree a a)
parseEvalATree parserA = parseLeaf parserA <|> parseNode intFunctionDict parserA

parseLeaf :: (Read a, Read a) => Parser a -> Parser (EvalATree a a)
parseLeaf parserA = Leaf <$> parserA

parseNode :: (Read a, Read a) => Map String ([a] -> a) -> Parser a -> Parser (EvalATree a a)
parseNode dict parserA = do
  string "Node"
  spaces
  funcName <- many1 letter
  spaces
  subtrees <- between (char '[') (char ']') (parseEvalATree parserA `sepBy` char ',')
  let func = case Map.lookup funcName dict of
        Just f -> f
        Nothing -> error "Function not defined"
  return (Node func subtrees)

解决方案

核心矛盾

Haskell标准Read类的实例是无上下文的,无法携带额外参数(比如你的函数字典)。因此有两种解决方向:


1. 硬编码字典实现标准Read实例

如果函数字典是全局固定的(比如intFunctionDict是常量),可以直接将其嵌入Read实例的解析逻辑中,使用Text.Read模块的工具实现:

import Text.Read
import qualified Data.Map as Map

-- 示例全局函数字典
intFunctionDict :: Map String ([Int] -> Int)
intFunctionDict = Map.fromList [
    ("sum", sum),
    ("product", product)
  ]

instance Read (EvalATree Int Int) where
  readPrec = parens $ 
    -- 解析Leaf
    (prec 10 $ do
      Ident "Leaf" <- lexP
      val <- readPrec
      return $ Leaf val) 
    <++ 
    -- 解析Node
    (prec 10 $ do
      Ident "Node" <- lexP
      Ident funcName <- lexP
      subtrees <- readPrec
      case Map.lookup funcName intFunctionDict of
        Just func -> return $ Node func subtrees
        Nothing -> pfail)

2. 自定义带上下文的读取函数

如果需要动态传入不同的函数字典,放弃标准Read实例,基于你已有的解析器封装自定义读取函数:

import Text.Parsec
import qualified Data.Map as Map

-- 自定义解析函数,支持传入函数字典
readEvalATree :: Map String ([a] -> a) -> Parser a -> String -> Either ParseError (EvalATree a a)
readEvalATree dict parser input = parse (parseEvalATree' dict parser <* eof) "" input
  where
    parseEvalATree' d p = parseLeaf p <|> parseNode d p
    parseLeaf p = Leaf <$> p

-- 调用示例(解析Int类型的EvalATree)
readIntEvalATree :: String -> Either ParseError (EvalATree Int Int)
readIntEvalATree = readEvalATree intFunctionDict readParser
  where
    readParser = do
      s <- many1 (noneOf " ,[]")
      case reads s of
        [(x, "")] -> return x
        _ -> fail "无效的Int值"

关键注意事项

你当前的Show实例存在格式不兼容问题:Show输出的是calcNode的计算结果(比如Node 3 [Leaf 1, Leaf 2]),但你的解析器期望读取函数名称(比如Node sum [Leaf 1, Leaf 2])。必须修正Show实例,让它输出函数名称而非计算结果,否则无法正确解析。

最直接的修正方式是修改数据类型,让Node同时存储函数名和函数:

data EvalATree b a = Leaf a | Node String ([b] -> a) [EvalATree b a]

-- 修正后的Show实例
instance (Show a) => Show (EvalATree a a) where
  show (Leaf a) = "Leaf " ++ show a
  show (Node name _ trees) = "Node " ++ show name ++ " " ++ show trees

内容的提问来源于stack exchange,提问作者WmWarescot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:03:21