Comandos multilínea en GHCi

134

Tengo problemas para ingresar comandos de varias líneas en ghci.

El siguiente código de 2 líneas funciona desde un archivo:

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

Pero cuando entro en ghci, aparece un error:

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

También intenté poner el código dentro :{ ... :}, pero tampoco funcionan para este ejemplo, porque esto solo agrega las líneas en una línea, lo que no debería ser el caso.

Estoy usando WinGHCi, versión 2011.2.0.1

R71
fuente

Respuestas:

183

La mayoría de las veces, puede confiar en la inferencia de tipos para elaborar una firma para usted. En su ejemplo, lo siguiente es suficiente:

Prelude> let addTwo x y = x + y

Si realmente desea una definición con una firma de tipo, o su definición abarca varias líneas, puede hacerlo en ghci:

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

Tenga en cuenta que también puede exprimir esto en una línea:

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

Puede encontrar más información sobre cómo interactuar con ghci en la evaluación interactiva en la sección de solicitud de la documentación.

Nicolas Wu
fuente
1
Muchas gracias por las dos soluciones. Pero tengo otra pregunta relacionada: ¿por qué se requieren los cuatro espacios en blanco iniciales en la segunda línea (antes de addTwo)? Y esto tiene que ser exacto, si hay menos o más espacios en blanco, entonces hay un error.
R71
9
@Rog letcomienza un bloque; las entradas en un bloque se agrupan por sangría; y el primer carácter que no es un espacio en blanco en un bloque establece la sangría por la que se agrupan. Como el primer carácter que no es un espacio en blanco en el letbloque de arriba es el ade addTwo, todas las líneas del bloque deben tener una sangría exactamente igual de profunda a.
Daniel Wagner
Gracias. También noté que en otros bloques let / where. Esta es una gran diferencia con respecto a otros idiomas en los que se ignora el espacio en blanco, por lo que tuve algunas dificultades para comprenderlo.
R71
124

Resuelva este problema activando GHCI y escribiendo :set +m:

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

Auge.


Lo que está sucediendo aquí (y te estoy hablando principalmente a ti , persona buscando ayuda en Google mientras te abres paso a través de Learn You A Haskell ) es que GHCI es un entorno interactivo donde estás cambiando enlaces de nombres de funciones sobre la marcha. Debe ajustar las definiciones de sus funciones en un letbloque, para que Haskell sepa que está a punto de definir algo. El :set +mmaterial es una abreviatura para la construcción del :{ código multilínea :}.

El espacio en blanco también es significativo en los bloques, por lo que debe sangrar la definición de su función después de su definición de tipo en cuatro espacios para tener en cuenta los cuatro espacios let.

adrian
fuente
55
Tan simple, pero no obvio. ¡Quería gritarle al libro que estaba usando por no decirme eso en la página 1!
Tim
2
Desde un shell de Linux, echo ':set +m' >> ~/.ghcipara hacer que esta configuración sea persistente.
truthadjustr 01 de
puede tenerlo letsolo en la primera línea, luego todo el resto no necesita sangrarse en absoluto. donde el espacio en blanco realmente cuenta es que no debe haber espacios finales en sus líneas. el espacio en blanco al final cuenta como un Enter adicional y rompe el bloque de varias líneas.
Will Ness
14

Uso let:

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

A partir de GHCI versión 8.0.1 , letya no es necesario definir funciones en el REPL.

Entonces esto debería funcionar bien para usted:

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

La inferencia de tipos de Haskell proporciona una tipificación generalizada que también funciona para flotantes:

λ: addTwo 2.0 1.0
3.0

Si debe proporcionar su propio tipeo, parece que tendrá que usarlo letcombinado con entrada multilínea (use :set +mpara habilitar la entrada multilínea en GHCI):

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

Pero obtendrá errores si intenta pasar cualquier cosa menos Intdebido a su escritura no polimórfica:

λ: 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
Aaron Hall
fuente
2

Para ampliar la respuesta de Aaron Hall , al menos en la versión GHCi 8.4.4, no necesita usar letdeclaraciones de tipo si usa el :{ :}estilo. Esto significa que no tiene que preocuparse por agregar la sangría de 4 espacios en cada línea posterior para tener en cuenta let, haciendo que las funciones más largas sean mucho más fáciles de escribir, o en muchos casos, copiar y pegar (ya que la fuente original probablemente no tendrá la sangría correcta):

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

Actualizar

Como alternativa, puede activar el modo de entrada multilínea :set +m, luego escribir letsolo, presionar Entrar y luego pegar definiciones sin necesidad de sangría.

Sin embargo, esto no parece funcionar con algunos bloques de código, como:

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

Pero el :{, :}técnica hace.

davidA
fuente
1
en realidad, incluso antes, podría escribir :{, luego en la siguiente línea letpor sí mismo, luego pegar sus definiciones sin sangrado adicional, y luego cerrar con :}. :) y con el modo de entrada multilínea set ( :set +m) ni siquiera necesitaba los comandos de llaves siempre que no hubiera espacios finales en las líneas de código.
Will Ness
Ah, entonces, :set +m¿puedes usarlo leten su propia línea? Entonces puedes, eso es genial. Gracias.
davidA
Hmm, he encontrado algunos casos en los que simplemente escribir y letluego la nueva línea no funciona. Mira mi edición.
davidA