【问题标题】:Multi-line commands in GHCiGHCi 中的多行命令
【发布时间】:2012-01-16 14:24:46
【问题描述】:

我在 ghci 中输入多行命令时遇到问题。

以下两行代码适用于文件:

addTwo :: Int -> Int -> Int
addTwo x y = x + y

但是当我进入ghci时,我得到一个错误:

<interactive>:1:1: error:
    Variable not in scope: addTwo :: Int -> Int -> Int

我也尝试将代码放在:{ ... :} 中,但它们也不适用于此示例,因为这只是将行附加到一行中,不应该是这种情况。

我正在使用 WinGHCi,版本 2011.2.0.1

【问题讨论】:

标签: haskell ghci multiline-repl-definition


【解决方案1】:

大多数时候,您可以依靠类型推断来为您制定签名。在您的示例中,以下内容就足够了:

Prelude> let addTwo x y = x + y

如果你真的想要一个带有类型签名的定义,或者你的定义跨越多行,你可以在 ghci 中这样做:

Prelude> :{
Prelude| let addTwo :: Int -> Int -> Int
Prelude|     addTwo x y = x + y 
Prelude| :}
Prelude> addTwo 4 7
11

请注意,您也可以将其压缩到一行:

Prelude> let addTwo :: Int -> Int -> Int ; addTwo x y = x + y

您可以在文档的 Interactive evaluation at the prompt 部分找到有关与 ghci 交互的更多信息。

【讨论】:

  • 非常感谢这两个解决方案。但我还有另一个相关问题:为什么第二行(addTwo 之前)需要四个前导空格?这必须是准确的,如果有更少或更多的空白,那么就会有错误。
  • @Rog let 开始一个块;块中的条目按缩进分组;并且块中的第一个非空白字符设置它们分组的缩进。由于上面let 块中的第一个非空白字符是addTwoa,因此块中的所有行必须缩进与a 一样深。
  • 谢谢。我还注意到在其他 let/where 块中。这与忽略空格的其他语言有很大不同,所以我很难理解。
【解决方案2】:

使用let:

Prelude> :{
Prelude| let addTwo :: Int -> Int -> Int
Prelude|     addTwo x y = x + y
Prelude| :}
Prelude> addTwo 2 3
5

【讨论】:

    【解决方案3】:

    通过启动 GHCI 并输入 :set +m 来解决此问题:

    Prelude> :set +m
    Prelude> let addTwo :: Int -> Int -> Int
    Prelude|     addTwo x y = x + y
    Prelude| 
    Prelude> addTwo 1 3
    4
    

    轰隆隆。


    这里发生的事情(我主要是在与 交谈,在通过 Learn You A Haskell 工作时正在谷歌搜索寻求帮助的人)是 GHCI 是一个交互式环境,您可以在其中更改绑定动态的函数名称。您必须将函数定义包装在 let 块中,以便 Haskell 知道您将要定义一些东西。 :set +m 是多行 :{ code :} 构造的简写。

    空格在块中也很重要,因此您必须在类型定义之后将函数定义缩进四个空格,以说明let 中的四个空格。

    【讨论】:

    • 如此简单,但并不明显。我想对我正在使用的那本书大喊大叫,因为我在第 1 页没有告诉我!
    • 在 Linux shell 中,echo ':set +m' &gt;&gt; ~/.ghci 使此设置保持不变。
    • 你可以在第一行单独使用let,那么其余的就不需要缩进了。空格真正重要的地方是行上不能有尾随空格。尾随空格算作一个额外的 Enter 并打破多行块。
    【解决方案4】:

    GHCI version 8.0.1 开始,let 不再需要在 REPL 上定义函数。

    所以这应该适合你:

    λ: addTwo x y = x + y
    λ: addTwo 1 2
    3
    λ: :t addTwo
    addTwo :: Num a => a -> a -> a
    

    Haskell 的类型推断提供了适用于浮点数的通用类型:

    λ: addTwo 2.0 1.0
    3.0
    

    如果您必须提供自己的输入,您似乎需要将let 与多行输入结合使用(使用:set +m 在GHCI 中启用多行输入):

    λ: let addTwo :: Int -> Int -> Int
     |     addTwo x y = x + y
     | 
    λ: addTwo 1 2
    3
    

    但是,如果您尝试传递除 Int 之外的任何内容,因为您的非多态类型,您会收到错误:

    λ: addTwo 2.0 1.0
    
    <interactive>:34:8: error:
        • No instance for (Fractional Int) arising from the literal ‘2.0’
        • In the first argument of ‘addTwo’, namely ‘2.0’
          In the expression: addTwo 2.0 1.0
          In an equation for ‘it’: it = addTwo 2.0 1.0
    

    【讨论】:

      【解决方案5】:

      要扩展 Aaron Hall's answer,至少在 GHCi 8.4.4 版本中,如果您使用 :{ :} 样式,则不需要将 let 与类型声明一起使用。这意味着您不必担心在每个后续行中添加 4 空格缩进来说明 let,从而使更长的函数更容易键入,或者在许多情况下,复制粘贴(因为原始来源可能会赢'没有正确的缩进):

      λ: :{
       | addTwo :: Int -> Int -> Int
       | addTwo x y = x + y
       | :}
      λ: addTwo 1 2
      3
      

      更新

      您也可以使用:set +m 打开多行输入模式,然后单独输入let,按Enter,然后粘贴定义而无需缩进。

      但是这似乎不适用于某些代码块,例如:

      class Box a where
        mkBox :: a -> Boxes.Box
      

      但是:{:} 技术可以。

      【讨论】:

      • 实际上,甚至在之前,您也可以输入:{,然后在下一行单独输入let,然后粘贴您的定义而不添加任何缩进,然后用:} 关闭。 :) 并且设置了多行输入模式 (:set +m),只要代码行上没有尾随空格,您甚至不需要大括号命令。
      • 啊,所以:set +m 可以只在自己的线路上使用let?所以你可以 - 这很酷。谢谢。
      • 嗯,我发现在某些情况下,只需输入 let 然后换行不起作用。查看我的编辑。
      猜你喜欢
      • 1970-01-01
      • 2016-05-20
      • 1970-01-01
      • 1970-01-01
      • 2017-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多