键入的表达式解析器

hen*_*ing 7 haskell

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

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

-- A function has an argument type and a result type
class Fun f where
  type FunArg f
  type FunRes f

-- Expressions are either constants of function applications
data Expr a where
  Const :: a -> Expr a
  App :: Fun f => f -> FunArg f -> Expr (FunRes f)

-- A very simple function
data Plus = Plus

-- Which takes two integer expressions and returns an integer expression
instance Fun Plus where
  type FunArg Plus = (Expr Int,Expr Int)
  type FunRes Plus = Int

-- A more complicated function which lifts a function to lists (like in haskell)
data Map f r = Map f

-- For this we need the concept of lifting function arguments:
class Liftable a where
  type LiftRes a

-- A singleton argument is lifted by changing the expression type from a to [a]
instance Liftable (Expr a) where
  type LiftRes (Expr a) = Expr [a]

-- Two function arguments are lifted by lifting each argument
instance (Liftable a,Liftable b) => Liftable (a,b)  where
  type LiftRes (a,b) = (LiftRes a,LiftRes b)

-- Now we can declare a function instance for Map
instance (Fun f,Liftable (FunArg f),r ~ LiftRes (FunArg f)) => Fun (Map f r) where
  type FunArg (Map f r) = r
  type FunRes (Map f r) = [FunRes f]

-- Now a parser for functions:
parseFun :: [String] -> (forall f. Fun f => f -> a) -> a
-- The parser for the plus function is easy:
parseFun ["plus"] f = f Plus
-- But the parser for map is not possible:
parseFun ("map":sym) f 
  = parseFun sym (\fun -> f (Map fun))
Run Code Online (Sandbox Code Playgroud)

问题似乎是没有办法说服类型检查器每个LiftRes本身都是Liftable,因为禁止递归类声明.

我的问题是:我如何使这项工作?是否有其他类型表达式解析器的示例,我可以从中获取提示?

编辑:似乎关于类型族限制的讨论似乎非常相关.但是,我没有让他的解决方案在我的情况下工作,也许有人可以帮忙吗?

kos*_*kus 4

使示例正常工作的最简单方法是Liftable (FunArg f)从实例声明中删除约束。但我认为你的例子太简洁了,它并没有说明你真正需要它的原因。

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

class Liftable (FunArg f) => Fun f where
  ...
Run Code Online (Sandbox Code Playgroud)

如果这是不可行的(即,如果并非所有函数都有可提升的参数类型),那么您不能期望编写给parseFun定类型的 a 。

更笼统的评论:我认为您在这里尝试做的事情非常奇怪,而且可能一次做的事情太多。从非结构化字符串解析为上下文无关的数据类型已经足够困难了。为什么不先这样做,然后编写一个单独的函数,将语言的“非类型化”但结构化的表示形式转换为类型化的表示形式。

编辑(作为对评论的反应,修订):正如您在问题中链接的类型族约束的讨论中所指出的,您可以使用 绕过超类循环限制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))
Run Code Online (Sandbox Code Playgroud)