Haskell quasiquotation如何用于替换Haskell级别的令牌? [英] How can Haskell quasiquotation be used for replacing tokens on the Haskell level?

查看:118
本文介绍了Haskell quasiquotation如何用于替换Haskell级别的令牌?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

haskellwiki 中所述的Quasiquotation大多显示为用于在Haskell内嵌入其他语言的有用工具,而不会混淆字符串引用。



问题在于:对于Haskell本身来说,将现有Haskell代码通过quasiquoter放在代替令牌并将结果传递给ghc?也许模板Haskell是关键吗?



我查找了代码示例,但没有找到任何代码。一些EDSL可以通过减小它们组合运算符的大小(例如,将'a。|。b。>>。c'转换为'[myedsl | a | b >> c]')来受益于这种能力。 b $ b

解决方案

例如,您可以通过使用 haskell-src-meta 包。它将有效的Haskell代码分析到AST中,然后您可以修改它。



在这种情况下,修改AST的最简单方法是使用 Data.Generics 将一般转换应用于整个AST,以替换其他运算符的运算符。



我们将从构建泛型Haskell表达式的转换函数开始。表示表达式的数据类型是 template-haskell 包中的> Exp 。



例如,要将运算符>> 转换为。>>。,我们'd使用类似于

的函数
$ b

  import Language.Haskell.TH(Exp(..),mkName)

replaceOp :: Exp - > Exp
replaceOp(VarE n)| n == mkName>> = VarE(mkName。>> ;.)
replaceOp e = e

这改变了一个变量表达式( VarE ),但对任何其他表达式都无能为力。

要遍历整个AST并替换所有出现的>> ,我们将使用函数 everywhere mkT Data.Generic

  import data。泛型(无处不在,mkT)

replaceEveryOp :: Exp - > Exp
replaceEveryOp = everywhere(mkT replaceOp)

为了进行多次替换,我们可以修改该函数,以便它可以取代任何运算符的关联列表。

 类型Replacements = [(String,String)] 

replaceOps ::替换 - > Exp - > Exp
replaceOps reps = everywhere(mkT f)其中
f e @(VarE n)=
的情况代表只是n' - > VarE(mkName n')
_ - > e
其中rep = lookup(显示n)代表
fe = e

顺便说一下,这是一个很好的例子,它使用

  { - #LANGUAGE ViewPatterns# - } 

replaceOps ::替换 - > Exp - > Exp
replaceOps reps = everywhere(mkT f)其中
f(VarE(replace - > Just n'))= VarE(mkName n')
fe = e

替换n = lookup(show n)reps

现在我们要做的就是构建

$ $ p $ { - #LANGUAGE ViewPatterns# - }

导入数据.Generics(everywhere,mkT)
import Language.Haskell.Meta.Parse(parseExp)
import Language.Haskell.TH(Exp(..),mkName,ExpQ)
import语言。 Haskell.TH.Quote(QuasiQuoter(..))

类型Replacements = [(String,String)]

替换::替换
替换=
[(||,。|。)
,(>>,。>> ;.)
]

myedls = QuasiQuoter
{quoteExp = replaceOpsQ
,quotePat = undefined
,quoteType = undefined
,quoteDec = undefined
}

replaceOpsQ: :字符串 - > ExpQ
replaceOpsQ s = case
的parseExp s右e - >返回$ replaceOps替换件e
Left err - >失败err

replaceOps ::替换 - > Exp - > Exp
replaceOps reps = everywhere(mkT f)其中
f(VarE(replace - > Just n'))= VarE(mkName n')
fe = e

替换n = lookup(show n)reps

如果将上述内容保存到自己的模块中 MyEDSL.hs ),那么您可以导入它并使用准价格。

  { - #LANGUAGE TemplateHaskell# - } 
{ - #LANGUAGE QuasiQuotes# - }

import MyEDSL

foo = [myedsl |一个|| b>>注意我已经使用了 ||

$ c>而不是 | ,因为后者在Haskell中不是一个有效的运算符(因为它是用于模式守护的语法元素)。


Quasiquotation as described in haskellwiki is shown mostly as useful tool for embedding other languages inside Haskell without messing around with string quotation.

Question is: For Haskell itself, how easy it would be to put existing Haskell code through a quasiquoter for the purpose of just replacing tokens and passing the result over to ghc? Perhaps Template Haskell is key here?

I have looked for code examples and didn't find any. Some EDSLs can benefit from this ability by reducing the size of their combinating operators (e.g. turn 'a .|. b .>>. c' to '[myedsl|a | b >> c]').

解决方案

You can build quasi-quoters that manipulate Haskell code by, for example, using the haskell-src-meta package. It parses valid Haskell code into an AST, which you can then modify.

In this case, the easiest way to modify the AST is by using Data.Generics to apply a generic transformation to the whole AST that replaces operators with other operators.

We'll begin by building the transformation function for generic Haskell expressions. The data type that represents an expression is Exp in the template-haskell package.

For example, to convert the operator >> to .>>. we'd use a function like

import Language.Haskell.TH (Exp(..), mkName)

replaceOp :: Exp -> Exp
replaceOp (VarE n) | n == mkName ">>" = VarE (mkName ".>>.")
replaceOp e = e

This changes a variable expression (VarE), but cannot do anything to any other kind of expressions.

Now, to walk the whole AST and to replace all occurrences of >> we'll use the functions everywhere and mkT from Data.Generic.

import Data.Generics (everywhere, mkT)

replaceEveryOp :: Exp -> Exp
replaceEveryOp = everywhere (mkT replaceOp) 

In order to make several replacements, we can alter the function so that it takes an association list of any operator to replace.

type Replacements = [(String, String)]

replaceOps :: Replacements -> Exp -> Exp
replaceOps reps = everywhere (mkT f) where
    f e@(VarE n) = case rep of
        Just n' -> VarE (mkName n')
        _ -> e
        where rep = lookup (show n) reps
    f e = e

And by the way, this is a good example of a function that is much nicer to write by using the view patterns language extension.

{-# LANGUAGE ViewPatterns #-}

replaceOps :: Replacements -> Exp -> Exp
replaceOps reps = everywhere (mkT f) where
    f (VarE (replace -> Just n')) = VarE (mkName n')
    f e = e

    replace n = lookup (show n) reps

Now all that's left for us to do is to build the "myedsl" quasi-quoter.

{-# LANGUAGE ViewPatterns #-}

import Data.Generics (everywhere, mkT)
import Language.Haskell.Meta.Parse (parseExp)
import Language.Haskell.TH (Exp(..), mkName, ExpQ)
import Language.Haskell.TH.Quote (QuasiQuoter(..))

type Replacements = [(String, String)]

replacements :: Replacements
replacements =
    [ ("||", ".|.")
    , (">>", ".>>.")
    ]

myedls = QuasiQuoter
    { quoteExp  = replaceOpsQ
    , quotePat  = undefined
    , quoteType = undefined
    , quoteDec  = undefined
    }

replaceOpsQ :: String -> ExpQ
replaceOpsQ s = case parseExp s of
    Right e -> return $ replaceOps replacements e
    Left err -> fail err

replaceOps :: Replacements -> Exp -> Exp
replaceOps reps = everywhere (mkT f) where
    f (VarE (replace -> Just n')) = VarE (mkName n')
    f e = e

    replace n = lookup (show n) reps

If you save the above to its own module (e.g. MyEDSL.hs), then you can import it and use the quasi-quoter.

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}

import MyEDSL

foo = [myedsl| a || b >> c |]

Note that I've used || instead of | because the latter is not a valid operator in Haskell (since it's the syntactic element used for pattern guards).

这篇关于Haskell quasiquotation如何用于替换Haskell级别的令牌?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆