Como definir uma função em ghci em várias linhas?

161

Estou tentando definir qualquer função simples que abranja várias linhas em ghci, considere o seguinte como exemplo:

let abs n | n >= 0 = n
          | otherwise = -n

Até agora, tentei pressionar Enter após a primeira linha:

Prelude> let abs n | n >= 0 = n
Prelude>           | otherwise = -n
<interactive>:1:0: parse error on input `|'

Também tentei usar os comandos :{e, :}mas não chego longe:

Prelude> :{
unknown command ':{'
use :? for help.

Estou usando o GHC Interactive versão 6.6 para Haskell 98 no Linux, o que estou perdendo?

Peter McG
fonte
20
Atualize sua instalação do GHC. O GHC 6.6 tem quase 5 anos! As últimas versões do Haskell estão aqui: haskell.org/platform
Don Stewart
possível duplicata de comandos Multi-line em GHCi
mmmmmm
1
@ Mark Este OP já tentou as soluções para esse problema. Esse problema é devido a um ghci desatualizado, não à falta de conhecimento do que fazer. Solução aqui: atualização. Solução: utilização :{, :}.
AndrewC

Respostas:

124

Para guardas (como seu exemplo), você pode colocá-los todos em uma linha e funciona (os guardas não se importam com o espaçamento)

let abs n | n >= 0 = n | otherwise = -n

Se você quiser escrever sua função com várias definições que correspondam aos padrões, assim:

fact 0 = 1
fact n = n * fact (n-1)

Então você usaria chaves com ponto e vírgula separando as definições

let { fact 0 = 1 ; fact n = n * fact (n-1) }
newacct
fonte
258

Agora o GHCi possui um modo de entrada multilinha, ativado com: set + m. Por exemplo,

Prelude> :set +m
Prelude> let fac 0 = 1
Prelude|     fac n = n * fac (n-1)
Prelude|
Prelude> fac 10
3628800
karakfa
fonte
39
Definir o modo multilinha faz com que ghcise comporte de maneira semelhante ao interpretador Python. Muito conveniente! De fato, você pode criar um .ghciarquivo em seu diretório pessoal no qual coloca o :set +mmodo multilinha, que se tornará o padrão sempre que iniciar ghci!
kqr
2
Isso é realmente incrível. Mas notei que, quando defino meu prompt usando :set prompt "λ "as linhas continuadas, diga em Preludevez de λ. Alguma forma de contornar isso?
abhillman
2
Veja aqui para o patch para definir uma nova continuação prompt de ghc.haskell.org/trac/ghc/ticket/7509#no1
karakfa
4
Para impedir que o Prelude apareça nas linhas de continuação, adicione também: configure prompt2 "|" no seu .ghci.
Nick
12
Você pode evitar completamente o recuo usando um final let. Basta digitar a letseguido de uma nova linha: let⏎. Então fac 0 = 1⏎. Então fac n = n * fac (n-1)⏎ ⏎ e pronto!
Iceland_jack 17/09/16
62

Dan é correto, mas :{e :}deve aparecer cada um em sua própria linha:

> :{ 
> let foo a b = a +
>           b
> :}
> :t foo
foo :: (Num a) => a -> a -> a

Isso também interage com a regra de layout, portanto, ao usar a doação, pode ser mais fácil usar chaves e ponto-e-vírgula explicitamente. Por exemplo, esta definição falha:

> :{
| let prRev = do
|   inp <- getLine
|   putStrLn $ reverse inp
| :}
<interactive>:1:18:
    The last statement in a 'do' construct must be an expression

Mas funciona quando chaves e pontos-e-vírgulas são adicionados:

> :{
| let prRev = do {
|   inp <- getLine;
|   putStrLn $ reverse inp;
| }
| :}
> :t prRev
prRev :: IO ()

Isso realmente será importante ao colar definições de um arquivo, onde o recuo pode mudar.

Justin Bailey
fonte
Isso não funciona se você tiver uma linha que termina em '=' (com a definição a seguir na próxima linha), pelo menos na versão 7.6.3.
22414 AdamC
1
Talvez isso falhe, porque a segunda e a terceira linha do let não são recuadas o suficiente ...? (Mais dois espaços.)
Evi1M4chine
7

Se você não deseja atualizar o GHC apenas para :{e :}, precisará escrever tudo em uma linha:

> let abs' n | n >= 0 = n | otherwise = -n

Não conheço nenhuma definição única em Haskell que deva ser escrita em várias linhas. O acima exposto realmente funciona no GHCi:

> :t abs'
abs' :: (Num a, Ord a) => a -> a

Para outras expressões, como doblocos, você precisará usar a sintaxe que não é de layout com chaves e ponto e vírgula (eugh).

CA McCann
fonte
0

Estou usando o GHCi, versão 8.2.1 no macOS Catalina 10.15.2. A seguir, é apresentado como eu coloco a declaração do tipo de função e os guardas juntos. Observe que as barras verticais à esquerda são para várias linhas do GHCi.

λ: let abs' :: (Num a, Ord a) => a -> a
 |     abs' n | n >= 0 = n | otherwise = -n
 | 
λ: abs' 7
7
λ: abs' (-7)
7
Thumb dourado
fonte
1
Se você usar :{e :}não precisar especificar let antes da sua declaração de tipo, o que significa que não é necessário recuar a segunda e as linhas subseqüentes.
davidA
Muito obrigado davidA. Era exatamente o que eu estava procurando, mas não consegui encontrar.
Golden Thumb
0

Parece que colar as duas linhas de uma vez ou usar a tecla Control-Enter para cada nova linha mantém tudo junto, pelo menos em https://repl.it/languages/haskell . Você verá 2 pontos no início da segunda linha. Ou coloque-o em um arquivo e: carregue o arquivo (: l principal). Como é que os abdominais não funcionam com números negativos? Oh, você deve colocar parênteses em torno do número.

   let abs n | n >= 0 = n 
..           | otherwise = -n
   abs (-1)
js2010
fonte