Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
什么';Haskell有什么不好?_Haskell_Template Haskell - Fatal编程技术网

什么';Haskell有什么不好?

什么';Haskell有什么不好?,haskell,template-haskell,Haskell,Template Haskell,似乎模板Haskell经常被Haskell社区视为一种不幸的便利。很难准确地说出我在这方面所观察到的话,但请考虑以下几个例子: 在回答问题时,模板Haskell列在“丑陋(但必要)”下 模板Haskell认为线程(库邮件列表)中的临时/劣质解决方案 Yesod经常因过分依赖模板Haskell而受到批评(参见对这种情绪的回应) 我看到过各种各样的博客文章,其中人们使用模板Haskell做了非常简洁的事情,实现了常规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函数变得不那么有趣:

  • 代码并不总是可组合的。假设有人为镜头制作了一个生成器,通常情况下,该生成器的结构只能由“最终用户”直接调用,而不能由其他TH代码调用,例如,将生成镜头的类型构造函数列表作为参数。在代码中生成该列表很困难,而用户只需编写
    generateLenses['Foo','Bar]
  • 开发人员甚至不知道可以编写代码。你知道你可以写
    表单['Foo','Bar]生成器吗
    Q
    只是一个monad,因此您可以在其上使用所有常用函数。有些人不知道这一点,正因为如此,他们用相同的功能创建了本质上相同的函数的多个重载版本,这些函数会导致某种膨胀效应。此外,大多数人在无需编写生成器的情况下也会在
    Q
    monad中编写生成器,这就像编写
    bla::IO Int;bla=返回3
    ;您为函数提供的“环境”超出了它的需要,函数的客户端需要提供该环境作为其结果
最后,有一些因素会降低TH函数作为最终用户使用的乐趣:

  • 不透明。当TH函数的类型为
    Q Dec
    时,它可以在模块的顶层生成任何内容,而您完全无法控制将生成的内容
  • 独石主义。除非开发人员允许,否则无法控制TH函数的生成量;如果您发现一个函数可以生成一个数据库接口和一个JSON序列化接口,那么您不能说“不,我只想要数据库接口,谢谢;我将使用我自己的JSON接口”
  • 运行时。TH代码运行时间相对较长。每次编译一个文件时,代码都会被重新解释,通常,运行TH代码需要大量的包,这些包必须加载。这大大降低了编译时间

    • 我想谈谈Dflmstr提出的几点

      我觉得你不能打字这一事实没有那么令人担忧。为什么?因为即使有错误,它仍然是编译时。我不确定这是否加强了我的论点,但这在精神上与在C++中使用模板时所收到的错误是相似的。不过,我认为这些错误比C++的错误更容易理解,因为生成的代码可以打印出来

      如果一个TH表达式/准引号做了一些非常高级的事情,以至于狡猾的角落可以隐藏,那么也许这是不明智的

      我最近使用的准引号(使用haskell src exts/meta)打破了这个规则。我知道这会引入一些bug,比如不能在广义列表理解中拼接。然而,我认为中的一些想法很有可能最终会出现在编译器中。在此之前,用于将Haskell解析为TH树的库几乎是完美的近似

      关于编译速度/依赖性,我们可以使用“zeroth”包内联生成的代码。这至少对给定库的用户来说是好的,但是对于编辑库的情况,我们做得再好不过了。依赖项是否会膨胀生成的二进制文件?我认为它遗漏了编译代码未引用的所有内容

      sta