使用 Template Haskell 生成函数

2024-05-02

是否可以使用 Template Haskell 定义函数?例如

convertStringToValue :: String -> Int
convertStringToValue "three" = 3
convertStringToValue "four" = 4

我也有一个Map [Char] Int.

fromList [("five",5),("six",6)]

如何添加功能

convertStringToValue "six" = 6
convertStringToValue "five" = 5

在编译时使用 Template Haskell 和Map?使用 Template Haskell 来达到这个目的似乎很愚蠢,但我还是想知道。


您可以使用两个文件来执行此操作:

一个“制造商”文件:Maker.hs:

module Maker where

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH

maker items = do
    x <- newName "x"
    lamE [varP x] (caseE (varE x) (map (\(a,b) -> match (litP $ stringL a) (normalB $ litE $ integerL b) []) items))

和主文件:Main.hs:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH
import Maker

function = $(maker [("five",5),("six",6)])

在这种情况下function将属于以下类型[Char] -> Int并将被编译为:

\x -> case x of
    "five" -> 5
    "six" -> 6

因此,就好像你会写:

function = \x -> case x of
    "five" -> 5
    "six" -> 6

你自己。显然,这对于两三个案例来说不会有回报,但正如您自己在问题中所写的那样,当您想要使用数千个案例或由列表理解生成的项目列表时,这开始得到回报。

自己制作Haskell模板

本节旨在简要描述如何自己编写 Haskell 模板。本教程不是“关于……的完整介绍”:还有其他技术可以做到这一点。

为了编写Haskell模板,你可以先尝试一些表达式,然后尝试概括他们使用map, fold, etc.

分析 AST 树

首先你最好看看 Haskell 如何解析某个表达式本身。你可以这样做runQ和括号[| ... |] https://stackoverflow.com/a/7489277/67579 with ...您想要分析的表达式。例如:

$ ghci -XTemplateHaskell
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :m Language.Haskell.TH
Prelude Language.Haskell.TH> runQ [| \x -> case x of "five" -> 5; "six" -> 6 |]
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package containers-0.5.0.0 ... linking ... done.
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
LamE [VarP x_0] (CaseE (VarE x_0) [Match (LitP (StringL "five")) (NormalB (LitE (IntegerL 5))) [],Match (LitP (StringL "six")) (NormalB (LitE (IntegerL 6))) []])

因此 AST 是:

LamE [VarP x_0] (CaseE (VarE x_0) [Match (LitP (StringL "five")) (NormalB (LitE (IntegerL 5))) [],Match (LitP (StringL "six")) (NormalB (LitE (IntegerL 6))) []])

所以现在我们有derived the 抽象语法树 (AST)从那个表情。一个提示是使表达式足够通用。例如,在case块,因为使用单个案例并不会告诉您应该如何将第二个案例添加到表达式中。现在我们希望自己创建这样的抽象语法树。

创建变量名

第一个方面是变量,例如VarP x_0 and VarE x_0。你不能简单地复制粘贴他们。这里x_0是一个名字。为了确保您不使用已经存在的名称,您可以使用newName。现在您可以构造以下表达式来完全复制它:

maker = do
    x <- newName "x"
    return $ LamE [VarP x] (CaseE (VarE x) [Match (LitP (StringL "five")) (NormalB (LitE (IntegerL 5))) [],Match (LitP (StringL "six")) (NormalB (LitE (IntegerL 6))) []])

概括函数

显然我们对构建固定的抽象语法树不感兴趣,否则我们可以自己编写它。现在的重点是引入一个或多个变量,并对这些变量进行推理。对于每个元组("five",5)等我们介绍一个Match陈述:

Match (LitP (StringL "five")) (NormalB (LitE (IntegerL 5))) []

现在我们可以轻松地概括这一点\(a,b):

\(a,b) -> Match (LitP (StringL a)) (NormalB (LitE (IntegerL b))) []

然后使用map迭代所有项目:

map (\(a,b) -> Match (LitP (StringL a)) (NormalB (LitE (IntegerL b))) []) items

with items我们希望为其生成案例的元组列表。现在我们完成了:

maker items = do
    x <- newName "x"
    return $ LamE [VarP x] (CaseE (VarE x) (map (\(a,b) -> Match (LitP (StringL a)) (NormalB (LitE (IntegerL b))) []) items))

现在您可以简单地省略return因为该库对所有这些项目都有小写变体。您还可以尝试“cleanup“代码一点点(例如(NormalB (LitE (IntegerL b))) to (NormalB $ LitE $ IntegerL b), ETC。);例如使用hlint.

maker items = do
    x <- newName "x"
    lamE [varP x] (caseE (varE x) (map (\(a,b) -> match (litP $ stringL a) (normalB $ litE $ integerL b) []) items))

The maker这是某种创建/构造函数的函数。

小心无限列表

请注意,编译器将评估美元括号之间的内容$()。例如,如果您要使用无限列表:

function = $(maker [(show i,i)|i<-[1..]]) -- Don't do this!

这将不断为抽象语法树分配内存并最终耗尽内存。编译器做了not在运行时扩展 AST。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Template Haskell 生成函数 的相关文章

随机推荐

  • org.hibernate.AssertionFailure:例如无法执行取消删除

    当我尝试在一些删除操作后进行读取时 我收到此休眠断言错误 我找不到任何有关此 无法执行取消删除 错误的信息 除了源代码 https github com hibernate hibernate orm blob master hiberna
  • git:显示所有已修改的文件 - 已暂存和未暂存

    我需要一个命令给我所有修改过的文件 这包括暂存 即新添加的文件 和非暂存更改 在普通列表我可以在脚本中使用它 虽然这个问题可能听起来很熟悉 但我只找到接近我想要做的命令 git ls files m 列出 非分阶段 修改但忽略了上演的和新的
  • 在 Ionic/Cordova 中接收 URL

    我正在尝试设置一种从另一个应用程序接收网址的方法 就像 您在浏览器中 单击共享 然后将链接发送到另一个应用程序 我的应用程序 我发现这个科尔多瓦插件 https github com Initsogar cordova webintent
  • chcp 65001 代码页导致程序终止且没有任何错误

    Problem 当我想要的时候问题就出现了inputPython 解释器中的 Unicode 字符 为简单起见 我在示例中使用了变音符号 但我第一次遇到波斯语字符 每当我使用 Python 时CHCP 65001 https ss64 co
  • OnIdle 事件中的异常不会冒泡

    在我的主窗体上 我订阅了两个事件 Application ThreadException 和 Application Idle 理论上 任何未捕获的异常都应该冒泡到主窗体 但是 如果异常发生在 OnIdle 事件中 则此方法不起作用 系统就
  • Altair 中具有自定义置信区间的折线图

    假设我有下面的数据框 我检查了文档 https altair viz github io gallery line with ci html但它仅基于单个列 可重现的代码 x np random normal 100 5 100 data
  • Visual Studio 中的 HTML5 Javascript API Intellisense 支持

    我开始使用 HTML5 CSS3 和新的 JavaScript API 我在 VS 2010 中注意到它不支持新的 JavaScript API 我想知道我是否可以对此做些什么 所以在 Vs2010 中如果我输入 var canvas do
  • SQL DROP TABLE 外键约束

    如果我想像这样删除数据库中的所有表 它会处理外键约束吗 如果没有 我该如何处理 GO IF OBJECT ID dbo Course U IS NOT NULL DROP TABLE dbo Course GO IF OBJECT ID d
  • core.async不是违背Clo​​jure原则吗?

    我看到许多 Clo jure 程序员对新的 core async 库充满热情 尽管它看起来很有趣 但我很难看出它如何符合 Clojure 原则 所以我有以下问题 它在任何地方都使用可变状态 正如函数名称通过感叹号所暗示的那样 例如 alt
  • 更新 Doctrine 后 Symfony 中的“ObjectManager 和 EntityManagerInterface 之间的兼容性”是什么?

    在我的 Symfony 项目中尝试更新 composer update 后出现错误 我寻找解决方案 发现有必要修改实体和构造函数中的使用和类型提示 我已经完成了 然后 我重新启动更新 但出现了不同的错误 并且更新未完全完成 结果 我的网站已
  • Heroku上传-预编译资产失败

    我需要帮助 当尝试将我的应用程序上传到heroku时 我收到此错误 有人知道为什么吗 有几个是错的 谢谢 Using rake 10 1 0 Using tlsmail 0 0 1 Using uglifier 2 1 2 Your bun
  • SVN存储库内容

    我已经设置了 VisualSvn Server 创建了一个存储库 并使用 AnkhSVN 向其中添加了 Visual Studio 解决方案 存储库的 url 类似于https msi pc svn MyProj 由于我的无能 一个问题 此
  • Objective-C / Cocoa Touch 中的 HTML 字符解码

    首先 我发现了这个 https stackoverflow com questions 659602 objective c html escape unescapeObjective C HTML 转义 unescape https st
  • 使用 Beautiful Soup - Python 查找 HTML 中 1 级内的所有文本

    我需要用美丽的汤来完成以下任务 HTML 示例 div Text1 div Text3 div div 我需要对此进行搜索 以便在列表的单独实例中返回给我 Text1 Text2 Text3 我尝试执行 findAll div 但它多次重复
  • LinkedIn OAuth 缺少必需参数“client_id”

    我正在使用 LinkedIn API 并尝试发出请求 但是当我尝试获取 accesstoken 时 我在 json 打印中收到以下错误 Array error gt missing parameter error description g
  • 使用ggmap在地图上绘制等高线

    我有洛杉矶港地区的颗粒物浓度差异 之后 之前 我正在尝试使用 ggmap 在地图上绘制浓度等值线 但结果看起来很不同 我使用的代码如下所示 数据位于代码下方 Code 安装 packages ggmap library ggmap PM r
  • 对 smtp.live.com 和 TIdSmtp(Indy、Delphi)的 SSL 支持

    我正在尝试连接到 smtp live com 发送电子邮件 live com 自 2009 年以来显然支持免费的 pop3 smtp 但这对我来说完全是新闻 当我尝试连接到 smtp live com 端口 587 时 会发生以下情况 Me
  • 如何使用 Core Data (iPhone) 存储 CLLocation?

    我试图保存一个位置 然后使用 Core Location MapKit 和 Core Data 框架在地图上检索该位置 我所做的只是创建了名为 POI 的实体 并添加了诸如纬度 双精度类型 经度 双精度类型 等属性以及其他一些属性 简而言之
  • Mongoose/Mongodb更新返回值及错误处理

    我对 Mongodb update 的返回值以及如何处理它的错误有点困惑 我使用 Node js Express js 和 Mongoose js 作为我的 Mongodb 驱动程序 当我浏览许多教程时 我看到的错误处理的唯一方法是 示例
  • 使用 Template Haskell 生成函数

    是否可以使用 Template Haskell 定义函数 例如 convertStringToValue String gt Int convertStringToValue three 3 convertStringToValue fou