【问题标题】:Why don't we write haskell in LISP syntax? (we can!) [closed]为什么我们不用 LISP 语法编写 haskell 呢? (我们可以!)[关闭]
【发布时间】:2020-02-29 10:25:36
【问题描述】:

它...对你们有点用(这绝对编译,改编自https://hackage.haskell.org/package/scotty):

main :: IO ()
main = (do
  (putStrLn "Starting Server....")
  (scotty 3000 (do
    (get "/hello/:name"
        (text ("hello " <> (param "name") <> "!")))

    (get "/users"
      (json allUsers))

    (get "/users/:id"
      (json (filter (matchesId (param "id")) allUsers))))))

(我不知道足够的 haskell 将 &lt;&gt; 转换为简单的括号,但聪明的人可以轻松做到。)

我们为什么要这样做?我们可以使用任何 lisp 宏引擎对 Haskell 进行预处理!微不足道!。

想象一下。 HASKELL 和 LISP 在一起。我们可以统治银河!

(我知道你的想法,但实际上我已经考虑过了:在这个例子中,Vader 是 Lisp,Luke 是 Haskell,Yoda 是 Alonzo Church)

(编辑“感谢所有回答和评论的人,我现在聪明多了。 我认为这种技术的最大问题还没有被提及,并且被朋友 IRL 指出:如果您编写一些 lispy 预处理器,您将失去 IDE 和工具中的类型检查、语法突出显示和理解。这听起来像是我的硬传。”

“我现在关注https://github.com/finkel-lang/finkel项目,这是我想要的lisp风格的haskell项目!”

【问题讨论】:

  • 因为 Lisp 使用了太多的括号,使其非常不可读。
  • /rant (1) 所有语言都可以用 Lisp 符号来表达,但人们有不同的品味。就我个人而言,我发现 Haskell 不可读,我更喜欢单词而不是 Perlisms,并且更喜欢不必记住哪个运算符优先于其他运算符,但这不是 Haskell 的优点(请参阅箭头语法、组合符等)。从视觉上讲,我希望 Haskell 采用完整的 Unicode 并使用现有的数学运算符,如 APL。 (2) 据我所知,Haskell 用户似乎不太重视元编程。
  • 这是一个在 Racket 中编写类似 Haskell 语言的实验:github.com/lexi-lambda/hackett
  • 我不确定它是否真的让你买了很多东西。您的宏仍然需要了解所有特殊的语法形式——例如,您不想对作为模式的语法位与作为表达式的语法位运行相同的递归操作,因此您不能只写一个在转换后的let 表达式的位上统一工作的宏——因此对文本的统一序列化实际上并没有帮助。
  • 单独使用单词可能会走得太远(参见 Python);尝试添加新功能成为尝试尽可能多地重用现有关键字的冒险(以避免破坏假定单词可用作标识符的代码),这种方式不会与关键字的现有用途产生歧义。符号有其位置;正确的答案既不是使用不足也不是过度使用。

标签: haskell macros lisp defmacro


【解决方案1】:

Haskell 的语法是 historically derived 来自 ISWIM 的语法,这种语言出现的时间不晚于 LISP,并且在 Peter J. Landin 1966 年的文章 The Next 700 Programming Languages 中有所描述。

第 6 节专门讨论与 LISP 的关系:

ISWIM 可以被视为一种尝试将 LISP 从其 同名的对列表的承诺,它以手到嘴的声誉 存储分配,其教学法的硬件依赖风格, 它的重包围,以及它对传统的妥协。

稍后在同一部分:

ISWIM 的文本外观不像 LISP 的 S 表达式。 它 更接近 LISP 的 M 表达式(构成一个非正式的 用作手动准备 LISP 的中间结果的语言 程式)。 ISWIM 具有以下附加功能:[...]

因此,有明确的意图是偏离 LISP 语法,或者至少偏离 S 表达式。

【讨论】:

  • 令人着迷!然而,就像我的例子一样,兼容性似乎仍然存在?
  • @0atman 你可以写出相当括号括起来的 Haskell(我通常更喜欢使用显式括号而不是 $ 运算符)但相似性有点欺骗性,因为括号并不真正表示语言中的列表,你不能直接用列表和原子组装代码,你需要模板 Haskell AST。顺便说一句,&lt;&gt; 的非运营商版本是mappend,而mconcat 组合了整个项目列表。
【解决方案2】:

在结构上,Haskell 程序由一组模块组成。每个模块由一组声明组成。模块和声明是惰性的——仅靠它们的存在不会导致任何事情发生。它们只是在静态命名空间中形成条目,编译器在生成代码时用来解析名称。

顺便说一句,您可能会在这里对Main.main 提出质疑。作为入口点,它只是为了被定义而运行。这还算公平。但所有其他声明仅在Main.main 需要时才用于代码生成,而不是仅仅因为它存在。

相比之下,Lisp 是更动态的系统。 Lisp 程序由一系列按顺序执行的 s 表达式组成。每一个都会导致代码执行具有任意副作用,包括修改全局命名空间。

这里的事情变得更加基于意见。我认为 Lisp 的动态结构与其语法的规律性密切相关。语法检查无法区分旨在将值添加到全局名称空间的 s 表达式和旨在运行其副作用的表达式。如果没有句法区分,添加语义区分似乎很尴尬。因此,我认为 Lisp 语法在某种意义上是太规则,无法用于 Haskell 中不同类型代码之间具有严格语义分离的语言。相比之下,Haskell 的语法提供了句法区别来匹配语义区别。

【讨论】:

  • 添加到全局命名空间是指 s-expr 顶层的 lambda?
  • @X10D 我的意思是defundefine,具体取决于您的特定Lisp。它们在执行时将值插入到全局命名空间中。
【解决方案3】:
  1. Haskell 没有 s-exp,因此括号仅用于标记归约和构造元组的优先级,这也意味着在 Haskell 中让类似 lisp 的宏工作并不容易,因为它们大量使用 s-exp和动态类型
  2. Haskell 有一个右关联函数应用程序(即 ($)),它涵盖了括号的大多数用例
  3. 空格在 Haskell 中具有语义意义,这就是我们大多数人写作的原因
do
  p1
  p2
  p3

而不是

do { p1
   ; p2
   ; p3
   }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-17
    • 1970-01-01
    • 2014-03-17
    • 2016-12-10
    • 2023-03-29
    • 1970-01-01
    • 2018-08-02
    • 2011-05-31
    相关资源
    最近更新 更多