什么';Haskell有什么不好?
似乎模板Haskell经常被Haskell社区视为一种不幸的便利。很难准确地说出我在这方面所观察到的话,但请考虑以下几个例子:什么';Haskell有什么不好?,haskell,template-haskell,Haskell,Template Haskell,似乎模板Haskell经常被Haskell社区视为一种不幸的便利。很难准确地说出我在这方面所观察到的话,但请考虑以下几个例子: 在回答问题时,模板Haskell列在“丑陋(但必要)”下 模板Haskell认为线程(库邮件列表)中的临时/劣质解决方案 Yesod经常因过分依赖模板Haskell而受到批评(参见对这种情绪的回应) 我看到过各种各样的博客文章,其中人们使用模板Haskell做了非常简洁的事情,实现了常规Haskell中不可能实现的更漂亮的语法,并大大简化了样板文件。那么,为什么模板
- 在回答问题时,模板Haskell列在“丑陋(但必要)”下
- 模板Haskell认为线程(库邮件列表)中的临时/劣质解决方案
- Yesod经常因过分依赖模板Haskell而受到批评(参见对这种情绪的回应)
我看到过各种各样的博客文章,其中人们使用模板Haskell做了非常简洁的事情,实现了常规Haskell中不可能实现的更漂亮的语法,并大大简化了样板文件。那么,为什么模板Haskell会以这种方式被看不起呢?什么使它不受欢迎?在什么情况下应该避免使用Template Haskell?为什么?避免使用Template Haskell的一个原因是,它作为一个整体是不安全的,因此与“Haskell的精神”背道而驰。以下是一些例子:
- 您无法控制一段TH代码将生成什么类型的Haskell AST,超出其显示位置;您可以有一个类型的值,但不知道它是表示
还是表示[Char]
或其他内容的表达式。如果一个函数只能生成某种类型的表达式,或者只能生成函数声明,或者只能生成与模式匹配的数据构造函数,那么这个函数就更可靠了(a->(对于所有b.b->c))
- 您可以生成不编译的表达式。生成的表达式引用了不存在的自由变量
?不幸的是,只有在实际使用代码生成器时,并且只有在触发生成特定代码的情况下,您才会看到这一点。单元测试也是非常困难的foo
- 编译时运行的代码可以执行任意的
IO
,包括发射导弹或窃取您的信用卡。你不想在搜索TH漏洞的过程中查看你下载的每一个阴谋集团软件包 - TH可以访问“模块专用”函数和定义,在某些情况下完全打破了封装
- 代码并不总是可组合的。假设有人为镜头制作了一个生成器,通常情况下,该生成器的结构只能由“最终用户”直接调用,而不能由其他TH代码调用,例如,将生成镜头的类型构造函数列表作为参数。在代码中生成该列表很困难,而用户只需编写
generateLenses['Foo','Bar]
- 开发人员甚至不知道可以编写代码。你知道你可以写
表单['Foo','Bar]生成器吗
只是一个monad,因此您可以在其上使用所有常用函数。有些人不知道这一点,正因为如此,他们用相同的功能创建了本质上相同的函数的多个重载版本,这些函数会导致某种膨胀效应。此外,大多数人在无需编写生成器的情况下也会在Q
monad中编写生成器,这就像编写Q
;您为函数提供的“环境”超出了它的需要,函数的客户端需要提供该环境作为其结果bla::IO Int;bla=返回3
- 不透明。当TH函数的类型为
时,它可以在模块的顶层生成任何内容,而您完全无法控制将生成的内容Q Dec
- 独石主义。除非开发人员允许,否则无法控制TH函数的生成量;如果您发现一个函数可以生成一个数据库接口和一个JSON序列化接口,那么您不能说“不,我只想要数据库接口,谢谢;我将使用我自己的JSON接口”
- 运行时。TH代码运行时间相对较长。每次编译一个文件时,代码都会被重新解释,通常,运行TH代码需要大量的包,这些包必须加载。这大大降低了编译时间
- 我想谈谈Dflmstr提出的几点
我觉得你不能打字这一事实没有那么令人担忧。为什么?因为即使有错误,它仍然是编译时。我不确定这是否加强了我的论点,但这在精神上与在C++中使用模板时所收到的错误是相似的。不过,我认为这些错误比C++的错误更容易理解,因为生成的代码可以打印出来
如果一个TH表达式/准引号做了一些非常高级的事情,以至于狡猾的角落可以隐藏,那么也许这是不明智的
我最近使用的准引号(使用haskell src exts/meta)打破了这个规则。我知道这会引入一些bug,比如不能在广义列表理解中拼接。然而,我认为中的一些想法很有可能最终会出现在编译器中。在此之前,用于将Haskell解析为TH树的库几乎是完美的近似
关于编译速度/依赖性,我们可以使用“zeroth”包内联生成的代码。这至少对给定库的用户来说是好的,但是对于编辑库的情况,我们做得再好不过了。依赖项是否会膨胀生成的二进制文件?我认为它遗漏了编译代码未引用的所有内容
sta