2011-02-06 36 views
11

[这个问题是由第9章中的“真实世界哈斯克尔”动机]

这里有一个简单的函数(砍倒要领):

saferOpenFile path = handle (\_ -> return Nothing) $ do 
     h <- openFile path ReadMode 
     return (Just h) 

为什么我需要一个$

如果处理的第二个参数是不是DO块,我并不需要它。下面的作品就好了:

handle (\_ -> putStrLn "Error calculating result") (print x) 

当我试图消除$编译失败。我可以得到它,如果我明确地加括号的工作,即

saferOpenFile path = handle (\_ -> return Nothing) (do 
     h <- openFile path ReadMode 
     return (Just h)) 

我能理解,但我想我很期待,当哈斯克尔击中do应该想到“我开始块”,而且我们不应该明确地把$打破。

我也想过推DO块到下一行,像这样:

saferOpenFile path = handle (\_ -> return Nothing) 
    do 
    h <- openFile path ReadMode 
    return (Just h) 

但是,这并不没有括号工作,要么。这让我困惑,因为以下工作:

add a b = a + b 

addSeven b = add 7 
    b 

我敢肯定,我只是达到,我接受它作为“这只是你如何写成语哈斯克尔”的地步,但没有任何人有任何的角度给?提前致谢。

+0

对不起,伙计们,我不知道stackoverflow非常好。代码只是垃圾。 (为什么没有“预览问题”)。我会尝试重新发布它。 – Jonathan 2011-02-06 03:50:46

+0

只需点击编辑器工具栏上的“代码”按钮(它看起来像一对大括号)。此外,预览正好在您输入的地方,实时更新... – Jon 2011-02-06 03:52:22

+1

啊,NoScript需要允许一个域!它现在看起来很好。谢谢,乔恩! – Jonathan 2011-02-06 03:54:20

回答

6

按照Haskell 2010 reportdo desugars这样的:

do {e;stmts} = e >>= do {stmts}

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

对于第二种情况,这是相同的写呢脱糖这样的(和更容易说明的目的):

do {p <- e; stmts} = e >>= (\p -> do stmts)

因此,假设您这样写:

-- to make these examples shorter 
saferOpenFile = f 
o = openFile 

f p = handle (\_ -> return Nothing) do 
    h <- o p ReadMode 
    return (Just h) 

它desugars这样:

f p = handle (\_ -> return Nothing) (o p ReadMode) >>= (\h -> return (Just h)) 

这是与此相同:

f p = (handle (\_ -> return Nothing) (o p ReadMode)) >>= (\h -> return (Just h)) 

即使你可能打算为它的意思这个:

handle (\_ -> return Nothing) ((o p ReadMode) >>= (\h -> return (Just h))) 

tl; dr - 什么时候语法被删除,它不会像你认为它应该括起整个语句(截至Haskell 2010)。

这答案只有AFAIK和

  1. 可能不正确
  2. 可能是过时的一天,如果它是正确的

注意:如果你对我说“,但实际的脱糖用途一个'let'语句应该组e >>= ok,对不对?“,那么我会回答与tl; dr相同的do语句:它不会像您认为的那样使用小括号/ group。

10

这是由于操作Haskell的顺序。函数应用绑定最紧密,这可能是混淆的来源。例如:

add a b = a + b 
x = add 1 add 2 3 

Haskell将此解释为:函数add应用于1,函数add。这可能不是程序员的意图。 Haskell会抱怨期待Num的第二个参数,但是取而代之的是函数。

有两个解决方案:

1)的表达可以被括号:

x = add 1 (add 2 3) 

哪个Haskell中会解释:该函数添加应用到1,则值加2 3.

但是,如果嵌套变得太深,这可能会令人困惑,因此第二个解决方案。

2)$操作:

x = add 1 $ add 2 3 

$是适用的功能,它的参数操作。 Haskell将此读为:函数(add 1)应用于add 2的值3.请记住,在Haskell中,函数可以部分应用,因此(add 1)是一个参数的完全有效函数。

$操作符可以多次使用:

x = add 1 $ add 2 $ add 3 4 

你选择哪种方案将被确定的,你认为是在特定环境下更具可读性。

10

这实际上不是特定于do -notation。你也不能写如print if x then "Yes" else "No"print let x = 1+1 in x*x

您可以从语法定义在chapter 3 of the Haskell Report开始验证这一点:一个do表达式或条件或let表达是lexp,但是功能应用的参数为AEXPlexp通常无效,作为aexp

当然,这并不能告诉你为什么选择是在报告中做出的。如果我不得不猜测,我可能会推测它是扩展有用的规则“功能应用程序绑定比其他任何东西”更紧密,例如,do和它的块之间的绑定。但我不知道这是否是最初的动机。 (我发现像print if x then ...这样的被拒绝的表单很难阅读,但那可能只是因为我习惯阅读Haskell接受的代码。)

相关问题