Haskell 中的语法糖

发布于 2022-08-16 09:11:23 字数 8620 浏览 16 评论 9

语法糖
下面一段摘自:http://hi.baidu.com/kuangxiangjie/blog/item/f20743fb81bf6b106d22ebd1.html
在Wiki里关于语法糖的一些历史介绍:
语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)
创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜“的语法。语法糖往往给程序员提供了更实用的
编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。

Wiki里提到,在C语言里用a[n]表示*(a+n),用a[n][m]表示*(*(a+n)+m),也是语法糖。
实际上从面向过程到面向对象也是一种语法糖,C语言可以通过它的指针、类型转换,结构实现面向对象的编程风格,
但是C++更进一步的推广了这种风格,更好用了。

按照Wiki的理解,只有计算机硬件指令才不算语法糖,而其他一切利用编译器、汇编器将代码抽象,和自然语言更相近的手段都算语法糖。

现在我们介绍一下 Haskell 中的语法糖,如有错误,请指正,改之

1.缩进
Haskell 也像 C 语言一样可以用大括号 '{}' 和分号 ';' 来构造程序块,但这样的写法会造成程序难以阅读,为了解决这个问题,Haskell 引入了缩进。
注意 Haskell 的缩进不同于 Python 的缩进。在 Python 中,tab/空格 是语法,而 Haskell 中 tab/空格 是定义良好的语法糖,最终会被转换成
括号和分号的程序块的语法。

使用缩进的结构有:let,where,case of,... ? (其他的就不了解了)。在它们之后的缩进都可以转换为括号和分号的程序块语法。

2.List
List 的最终转换如下:

  1. [1, 2, 3, 4] => 1:2:3:4:[]
  2. [1..10] => enumFromTo 1 10
  3. [1, 3..10] => enumFromThenTo 1 3 10
  4. [1..] => enumFrom 1
  5. [1, 3..] => enumFromThen 1 3

复制代码

  1. Prelude> :t enumFrom
  2. enumFrom :: (Enum a) => a -> [a]
  3. Prelude> :t enumFromTo
  4. enumFromTo :: (Enum a) => a -> a -> [a]
  5. Prelude> :t enumFromThen
  6. enumFromThen :: (Enum a) => a -> a -> [a]
  7. Prelude> :t enumFromThenTo
  8. enumFromThenTo :: (Enum a) => a -> a -> a -> [a]
  9. Prelude> enumFromTo 1 10
  10. [1,2,3,4,5,6,7,8,9,10]
  11. Prelude> enumFromThenTo 1 3 10
  12. [1,3,5,7,9]
  13. Prelude> enumFrom 1
  14. [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
  15. ...
  16. ...
  17. 15732,15733,15Interrupted.

复制代码
3.String
String 其实也是 List,之所以把它单独列出来,是因为 String 和 List 写法不一样,前者被双引号括住,而后者被中括号括住
String 的最终转换如下:

  1. "abcde" => 'a':'b':'c':'d':'e':[]

复制代码
4.List Comprehensions
List Comprehensions 等同于 map, filter 函数,但比 map, filter 函数更直观。
List Comprehensions 的最终转换如下:

  1. [ x | x <- aList , f x]
  2. =>
  3. let ok x = if f x
  4.              then [x]
  5.              else []
  6. in concatMap ok aList

复制代码

  1. Prelude> :t concatMap
  2. concatMap :: (a -> [b]) -> [a] -> [b]

复制代码
5.Section
Section 最终会转换为 lambda 函数
例:

  1. (+2)
  2. => x -> x+2
  3. (*2)
  4. => x -> x*2

复制代码
6. infix/prefix

  1. 1 + 2 => (+) 1 2
  2. take 1 [0, 1] => 1 `take` [0, 1]

复制代码

7. Named Fields

  1. data Configuration =
  2.   Configuration { username        :: String,
  3.                        localhost        :: String,
  4.                       remotehost        :: String,
  5.                       isguest        :: Bool,
  6.                       issuperuser        :: Bool,
  7.                       currentdir        :: String,
  8.                       homedir        :: String,
  9.                      timeconnected        :: Integer
  10.                 }
  11. 会转换为:
  12. data Configuration =
  13.   Configuration String                -- user name
  14.                     String                -- local host
  15.                     String                -- remote host
  16.                     Bool                -- is guest?
  17.                     Bool                -- is super user?
  18.                     String                -- current directory
  19.                     String                -- home directory
  20.                    Integer                -- time connected
  21.                 deriving (Eq, Show)
  22. username :: Configuration -> String
  23. userName (Configuration un _ _ _ _ _ _ _) = un
  24. localhost :: Configuration -> String
  25. localHost (Configuration _ lh _ _ _ _ _ _) = lh
  26. remoteHost :: Configuration -> String
  27. remoteHost (Configuration _ _ rh _ _ _ _ _) = rh
  28. isGuest :: Configuration -> Bool
  29. isGuest (Configuration _ _ _ ig _ _ _ _) = ig
  30. ...

复制代码

  1. hostData (Configuration {localhost=lh,remotehost=rh}) = (lh,rh)
  2. 会转换为:
  3. hostData (Configuration _ lh rh _ _ _ _ _) = (lh,rh)

复制代码

  1. initCFG' =
  2.   Configuration
  3.     { username="nobody",
  4.       localhost="nowhere",
  5.       remotehost="nowhere",
  6.       isguest=False,
  7.       issuperuser=False,
  8.       currentdir="/",
  9.       homedir="/",
  10.       timeconnected=0 }
  11. 会转换为:
  12. initCFG = Configuration "nobody" "nowhere" "nowhere" False False "/" "/" 0

复制代码
8.do
do 也是语法糖

  1. main = do
  2.    putStrLn "Greetings! What is your name?"
  3.    inpStr <- getLine
  4.    putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!"

复制代码
=>

  1. main =
  2.    putStrLn "Greetings! What is your name?" >>
  3.    getLine >>=
  4.    (inpStr -> putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!")

复制代码
do 语法糖的四条转换规则:

1. do {e} --> e

2. do {e; es} --> e >> do {es}

3. do {let decls; es} --> let decls in do {es}

4. do {p <- e; es} -> let ok p = do {es} ; ok _ = fail "..." in e >> ok

还有几个没有提到的,请参阅回帖

[ 本帖最后由 izhier 于 2009-5-1 19:10 编辑 ]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

时光瘦了 2022-08-21 23:56:32

原帖由 flw 于 2009-4-9 11:48 发表

还有,元组也是语法糖。

我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。

二元组是二元组,三元组是三元组,它们根本就不是同一样东西,

这个是 Haskell 的类型系统中让我觉得最别扭的地方

王权女流氓 2022-08-21 23:55:49

原帖由 roy_hu 于 2009-4-9 12:36 发表

ghc-6.10.1支持的更少:
*Main> (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,3
8,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56, ...

我不是用这个方法测试的。
我是

  1. :t (,,,,,,,,,,,,,,,,,,,)

复制代码这样子的。
发现 GHCi 支持的好多啊。看来都是骗人的。

方圜几里 2022-08-21 23:13:20

是这样的:

  1. foo = [ fm x | x <- aList , ff x]

复制代码

变成

  1. foo = do
  2.     x <- aList
  3.     if ff x
  4.         then return (fm x)
  5.         else fail "no message"

复制代码

嗯,刚才看了楼上 roy_hu 兄的回复,
觉得用 guard 比用 if 更合适一些,
因为列表同时也是 MonadPlus 的实例。

[ 本帖最后由 flw 于 2009-4-9 12:57 编辑 ]

彼岸花似海 2022-08-21 18:45:14

原帖由 izhier 于 2009-4-9 11:51 发表
想问一下,在 List Comprehensions 中

[ fm x | x  

翻译成Monad:
do
  x <- aList
  guard (ff x)
  return $ fm x

九命猫 2022-08-21 13:43:18

原帖由 flw 于 2009-4-9 11:48 发表
还有,元组也是语法糖。

我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。

二元组是二元组,三元组是三元组, ...

ghc-6.10.1支持的更少:

*Main> (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,3
8,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,7
5,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100)

<interactive>:1:0:                                                                                             
    A 100-tuple is too large for GHC
      (max size is 62)
      Workaround: use nested tuples or define a data type

但是ghc-6.10.2的user's guide里面说Tuples are currently limited to size 100.

你是暖光i 2022-08-20 20:27:28

想问一下,在 List Comprehensions 中

[ fm x | x <- aList, ff x]
这样的怎么转化
难道是:
[ fm x | x <- aList , ff x]
=>
let ok x = if ff x
             then [fm x]
             else []
in concatMap ok aList

[ 本帖最后由 izhier 于 2009-4-9 11:56 编辑 ]

浅语花开 2022-08-20 10:15:17

还有,元组也是语法糖。

我一开始看到了二元组、三元组、四元组、N 元组,
还以为把一堆量用括号括起来、中间用逗号隔开,这便是元组了呢,
后来才发现原来不是这样。

二元组是二元组,三元组是三元组,它们根本就不是同一样东西,
之所以我们平时写七元组八元组都没有什么问题,
是因为 Haskell 环境预定义了很多种元组,
前段时间我试图尝试这个预定义值的上限的时候,失败了。
但是我确实在前几年碰到过这个上限,是一个并不太大的值。
也许是由于 GHC 6.10 增强了也不一定。

补充一下:刚才我在我的 WinHugs 上测试了一下,最多支持 99 元组。换句话说,并不存在“元组”这个类型,只存在 99 个 n 元组类型(1 < n < 100)
Version: WinHugs Sep 2006

[ 本帖最后由 flw 于 2009-4-9 11:51 编辑 ]

Saygoodbye 2022-08-20 08:56:09

其实 -> 也是语法糖。a -> b 和 【data FunctionTakeTypeASpawnTypeB a b】是一个意思。只不过很显然第一种写法更方便,更直观。

:i (->)

一瞬间的火花 2022-08-16 11:08:31

关于缩进,既然提到了 Python,我觉得就不得不提到一点:
Haskell 不光有缩进,还有对齐!

事实上,Haskell 的缩进和对齐这一点,导致了 Haskell 程序只能用空格,不能用 TAB。

例子如下:

  1. D:MoChou>cat ttt.hs
  2. main = do   print "hello"
  3.             print "world"
  4. D:MoChou>runhaskell ttt.hs
  5. "hello"
  6. "world"
  7. D:MoChou>

复制代码
上面这段代码中我使用了空格,而不是 TAB,如果用 TAB,而想让这个程序正确工作,那么就得这么写:

  1. main = do<tab>print "hello"
  2. <tab><tab>print "world"

复制代码
这时,问题出现了:【你必须将 tab 键的宽度设置为 8 才能正常阅读此代码!】
如果不是 8 而是 4,就会看到这个效果:

  1. main = do   print "hello"
  2.         print "world"

复制代码
而众所周知的是,TAB 宽度是 8 已经用的人越来越少了,现在流行的是 4,
而 Haskell 程序员经常用 2。

综上所述:Haskell 用户应该之用空格做缩进和对齐。
如果这个程序员正巧还在用 Python,那么他必须要在 Python 社区(或公司团队中)提倡空格抵制 TAB,
否则他将不得不面临为这两种语言选择两款不同的编辑器,或者不同的 profile 才行。

虽然像 vim 或者 emacs 这样的超级编辑器是可以为不同的语言采用不同的设置的,
但是许多其它的编辑器并不具备这样的功能。

[ 本帖最后由 flw 于 2009-4-9 12:54 编辑 ]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文