Haskell 类型化表达式解析器

Haskell 类型化表达式解析器,haskell,Haskell,我正在尝试用Haskell创建一个类型化表达式解析器,到目前为止效果很好,但我目前正在努力实现更高阶的函数。我将问题归结为一个简单的例子: {-#语言类型族、GADT、灵活上下文、RankNTypes} --函数具有参数类型和结果类型 f班在哪里 funargf型 FunRes f型 --表达式是函数应用程序的常数 数据表达式a在哪里 常量::a->Expr a 应用程序::Fun f=>f->FunArg f->Expr(FunRes f) --一个非常简单的函数 数据加号=加号 --它接受两

我正在尝试用Haskell创建一个类型化表达式解析器,到目前为止效果很好,但我目前正在努力实现更高阶的函数。我将问题归结为一个简单的例子:

{-#语言类型族、GADT、灵活上下文、RankNTypes}
--函数具有参数类型和结果类型
f班在哪里
funargf型
FunRes f型
--表达式是函数应用程序的常数
数据表达式a在哪里
常量::a->Expr a
应用程序::Fun f=>f->FunArg f->Expr(FunRes f)
--一个非常简单的函数
数据加号=加号
--它接受两个整数表达式并返回一个整数表达式
实例乐趣加上哪里
类型FunArg Plus=(Expr Int,Expr Int)
类型FunRes Plus=Int
--将函数提升到列表的更复杂的函数(如haskell中的函数)
数据映射f r=映射f
--为此,我们需要提升函数参数的概念:
a类升降台在哪里
a型升降机
--通过将表达式类型从A更改为[A]来取消单例参数
可提升实例(Expr a)其中
类型LiftRes(Expr a)=Expr[a]
--通过提升每个参数来提升两个函数参数
实例(可提升a,可提升b)=>可提升(a,b),其中
类型提升管(a,b)=(提升管a,提升管b)
--现在我们可以为Map声明一个函数实例
实例(Fun f,Liftable(FunArg f),r~LiftRes(FunArg f))=>Fun(Map f r),其中
类型FunArg(映射f r)=r
类型FunRes(映射f r)=[FunRes f]
--现在是函数的解析器:
parseFun::[String]->(对于所有f.Fun f=>f->a)->a
--plus函数的解析器很简单:
parseFun[“plus”]f=f plus
--但是映射的解析器是不可能的:
parseFun(“map”:sym)f
=parseFun sym(\fun->f(Map fun))
问题似乎是没有办法让类型检查器相信每个LiftRes本身是可提升的,因为递归类声明是被禁止的

我的问题是:我如何使这项工作?是否还有其他类型化表达式解析器的示例可以提供提示


编辑:这似乎非常相关。但是,在我的例子中,我无法使他们的解决方案起作用,也许有人可以帮助我?

使示例起作用的最简单方法是从实例声明中删除
可提升(FunArg f)
约束。但我认为你的例子过于简单,没有说明你为什么需要它

因此,下一个最好的方法是向
Fun
类添加
Liftable(FunArg f)
超类约束:

class Liftable (FunArg f) => Fun f where
  ...
如果这是不可行的(即,如果不是所有函数都有可提升的参数类型),则不能期望编写给定类型的
parseFun

一个更一般的评论:我认为你在这里试图做的是非常奇怪的,也许是太多了。从非结构化字符串解析到上下文无关的数据类型已经足够困难了。为什么不先这样做,然后编写一个单独的函数,将您的语言的“非类型化”但结构化的表示转换为类型化的表示

EDIT(作为对评论的回应,已修订):正如您在问题中所指出的,您可以使用
ConstraintKinds
绕过超类循环限制。下面是一种使简化示例有效的方法。也许这将扩展到完整的解决方案

{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeFamilies, FlexibleContexts, GADTs #-}

import Data.Constraint  -- from the constraints package
import Data.Proxy       -- from the tagged package

-- A function has an argument type and a result type
class Liftable (FunArg f) => Fun f where
  type FunArg f
  type FunRes f

-- Expr, Plus, and instance Fun Plus as before

class Liftable a where
  type LiftRes a
  get :: p a -> Dict (Liftable (LiftRes a))
    -- acquire "superclass" dictionary by calling this method and
    -- then pattern matching on the result

instance Liftable (Expr a) where
  type LiftRes (Expr a) = Expr [a]
  get _ = Dict

instance (Liftable a, Liftable b) => Liftable (a, b) where
  type LiftRes (a, b) = (LiftRes a, LiftRes b)
  get (_ :: p (a, b)) =
    case get (Proxy :: Proxy a) of -- extra code required
      Dict -> case get (Proxy :: Proxy b) of -- extra code required
        Dict -> Dict

data Map f r = Map f

instance (Fun f, Liftable r, r ~ LiftRes (FunArg f)) => Fun (Map f r) where
  type FunArg (Map f r) = r
  type FunRes (Map f r) = [FunRes f]

parseFun :: forall a. [String] -> (forall f. Fun f => f -> a) -> a
parseFun ["plus"]      f = f Plus
parseFun ("map" : sym) f = parseFun sym
  (\ (fun :: g) -> case get (Proxy :: Proxy (FunArg g)) of -- extra code required
                     Dict -> f (Map fun))

Liftable
约束添加到函数类中的问题是,这需要我将
Liftable r
添加到Map实例中,然后在解析器中需要
Liftable(LiftRes(FunArg f))
的实例。这个过程可以无限期地继续下去。注意你的评论:你说得对,在真实的代码中,我正在解析lisp表达式,但我不想因为必须安装额外的软件包而打扰读者。谢谢,这也是另一篇文章的目的(我想)。但是,我无法让您的代码正常工作,它会给出与以前相同的错误消息。我需要换些别的东西吗?或者我遗漏了一些编译器标志等等?我只能推测你是如何使用这些的。如果您得到的实际错误无法在示例中重现,我很难提出在您的场景中实际有效的建议。@henning我再次编辑了答案,现在希望提供有效的代码。