Eu li o livro Learn You a Haskell até o ponto em que eles apresentam mônadas e coisas como Just a. Isso é tão contra-intuitivo para mim que sinto vontade de desistir de aprender.
Mas eu realmente quero tentar.
Gostaria de saber se posso ao menos evitar os conceitos avançados por um tempo e apenas começar a usar as outras partes da linguagem para realizar muitas tarefas práticas com as partes mais básicas do Haskell, como funções, IO padrão, manipulação de arquivos, interface de banco de dados e similares.
Essa é uma abordagem razoável para aprender a usar o Haskell?
Respostas:
Vamos primeiro distinguir entre aprender os conceitos abstratos e aprender exemplos específicos deles.
Você não vai muito longe ignorando todos os exemplos específicos, pela simples razão de que eles são totalmente onipresentes. De fato, as abstrações existem em grande parte porque unificam as coisas que você faria de qualquer maneira com os exemplos específicos.
As abstrações em si, por outro lado, são certamente úteis , mas não são imediatamente necessárias. Você pode ir longe ignorando completamente as abstrações e apenas usando os vários tipos diretamente. Você vai querer entendê-los eventualmente, mas sempre poderá voltar depois. Na verdade, eu posso quase garantir que, se você fizer isso, quando voltar a fazê-lo, dará um tapa na testa e se perguntará por que passou todo esse tempo fazendo as coisas da maneira mais difícil, em vez de usar as convenientes ferramentas de uso geral.
Tome
Maybe a
como exemplo. É apenas um tipo de dados:É tudo menos auto-documentado; é um valor opcional. Ou você tem "apenas" algo do tipo
a
ou não tem nada. Digamos que você tenha uma função de pesquisa de algum tipo, que retorneMaybe String
para representar a pesquisa de umString
valor que pode não estar presente. Então, você combina com o valor para ver qual é:Isso é tudo!
Realmente, não há mais nada que você precise. Sem
Functor
s ouMonad
s ou qualquer outra coisa. Eles expressam maneiras comuns de usarMaybe a
valores ... mas são apenas expressões idiomáticas, "padrões de design", como você quiser chamar.O único lugar em que você realmente não pode evitá-lo completamente é
IO
, mas de qualquer maneira é uma caixa preta misteriosa, então não vale a pena tentar entender o que isso significa como algo assimMonad
.De fato, aqui está uma folha de dicas para tudo o que você realmente precisa saber
IO
por enquanto:Se algo tem um tipo
IO a
, isso significa que é um procedimento que faz algo e cospe uma
valor.Quando você tem um bloco de código usando a
do
notação, escreva algo como isto:... significa executar o procedimento à direita do
<-
e atribuir o resultado ao nome à esquerda.Considerando que se você tiver algo parecido com isto:
... significa atribuir o valor da expressão simples (não um procedimento) à direita da
=
e ao nome à esquerda.Se você colocar algo lá sem atribuir um valor, assim:
... significa executar um procedimento e ignorar qualquer coisa que ele retorne. Alguns procedimentos têm o tipo
IO ()
- o()
tipo é um tipo de espaço reservado que não diz nada, de modo que apenas significa que o procedimento faz algo e não retorna um valor. Como umavoid
função em outros idiomas.Executar o mesmo procedimento mais de uma vez pode gerar resultados diferentes; esse é o tipo de ideia. É por isso que não há como "remover" o
IO
valor de um valor, porque algoIO
não é um valor, é um procedimento para obter um valor.A última linha de um
do
bloco deve ser um procedimento simples, sem atribuição, onde o valor de retorno desse procedimento se torna o valor de retorno para todo o bloco. Se você deseja que o valor de retorno use algum valor já atribuído, areturn
função usa um valor simples e fornece um procedimento não operacional que retorna esse valor.Fora isso, não há nada de especial
IO
; esses procedimentos são realmente valores simples, e você pode transmiti-los e combiná-los de maneiras diferentes. É somente quando eles são executados em umdo
bloco chamado em algum lugarmain
que eles fazem qualquer coisa.Então, em algo como este programa de exemplo totalmente chato e estereotipado:
... você pode lê-lo como um programa imperativo. Estamos definindo um procedimento chamado
hello
. Quando executado, primeiro ele executa um procedimento para imprimir uma mensagem perguntando seu nome; Em seguida, ele executa um procedimento que lê uma linha de entrada e atribui o resultado aname
; depois atribui uma expressão ao nomemsg
; depois imprime a mensagem; em seguida, ele retorna o nome do usuário como resultado de todo o bloco. Comoname
é aString
, isso significa quehello
é o procedimento que retorna aString
, então ele tem o tipoIO String
. E agora você pode executar esse procedimento em outro lugar, assim como ele é executadogetLine
.Pfff, mônadas. Quem precisa deles?
fonte
Eles são essenciais. Sem eles, é muito fácil usar Haskell como apenas outra linguagem imperativa e, embora Simon Peyton Jones tenha chamado Haskell, "a melhor linguagem de programação imperativa do mundo", você ainda está perdendo a melhor parte.
Mônadas, Transformadas de Mônada, Monoides, Functors, Functors Aplicable, Functors Contravarient, Arrows, Categories, etc, são padrões de design. Depois de encaixar a coisa específica que você está criando em uma delas, você pode recorrer a uma enorme variedade de funções que são escritas abstratamente. Essas classes de tipo não existem apenas para confundi-lo, mas para facilitar sua vida e torná-lo mais produtivo. Eles são, na minha opinião, a maior razão para a concisão do bom código Haskell.
fonte
É estritamente necessário se você apenas deseja que algo funcione? Não.
É necessário se você deseja escrever belos programas idiomáticos Haskell? Absolutamente.
Ao programar em Haskell, ajuda a manter duas coisas em mente:
O sistema de tipos está lá para ajudá-lo. Se você compõe seu programa a partir de um código altamente polimórfico e pesado de tipografia, muitas vezes você pode entrar na maravilhosa situação em que o tipo de função desejada o guiará para a (uma única!) Implementação correta .
As várias bibliotecas disponíveis foram desenvolvidas usando o princípio nº 1.
Então, ao escrever um programa em Haskell, começo escrevendo os tipos. Então começo de novo e escrevo alguns tipos mais gerais (e, se puderem ser instâncias de classes existentes, tanto melhor). Então escrevo algumas funções que me dão valores dos tipos que eu quero. (Então brinco com eles
ghci
e talvez escreva alguns testes de verificação rápida.) E, finalmente, colo tudomain
.É possível escrever Haskell feio que apenas faz com que algo funcione. Mas há muito mais a aprender quando você atinge esse estágio.
Boa sorte!
fonte