Haskell - Guia Rápido

Haskell é uma linguagem de programação funcional que foi especialmente projetada para lidar com computação simbólica e aplicativos de processamento de lista. A programação funcional é baseada em funções matemáticas. Além de Haskell, algumas das outras linguagens populares que seguem o paradigma de Programação Funcional incluem: Lisp, Python, Erlang, Racket, F #, Clojure, etc.

Dentro conventional programing, as instruções são tomadas como um conjunto de declarações em uma sintaxe ou formato específico, mas no caso de functional programing, todo o cálculo é considerado como uma combinação de funções matemáticas separadas.

Tornando-se funcional com Haskell

Haskell é uma linguagem puramente funcional amplamente usada. Aqui, listamos alguns pontos que tornam esta linguagem tão especial em relação a outras linguagens de programação convencionais, como Java, C, C ++, PHP, etc.

  • Functional Language- Na linguagem de programação convencional, instruímos ao compilador uma série de tarefas que nada mais são do que dizer ao computador "o que fazer" e "como fazer?" Mas em Haskell diremos ao nosso computador "o que é?"

  • Laziness- Haskell é uma linguagem preguiçosa. Delazy, queremos dizer que Haskell não avaliará nenhuma expressão sem nenhum motivo. Quando o mecanismo de avaliação descobre que uma expressão precisa ser avaliada, ele cria umthunk data structure para coletar todas as informações necessárias para essa avaliação específica e uma indicação para que thunk data structure. O mecanismo de avaliação começará a funcionar apenas quando for necessário avaliar aquela expressão específica.

  • Modularity- Um aplicativo Haskell nada mais é do que uma série de funções. Podemos dizer que um aplicativo Haskell é uma coleção de vários pequenos aplicativos Haskell.

  • Statically Typed- Na linguagem de programação convencional, precisamos definir uma série de variáveis ​​junto com seu tipo. Em contraste, Haskell é uma linguagem estritamente tipificada. Pelo termo linguagem estritamente tipada, queremos dizer que o compilador Haskell é inteligente o suficiente para descobrir o tipo da variável declarada, portanto, não precisamos mencionar explicitamente o tipo da variável usada.

  • Maintainability - Os aplicativos Haskell são modulares e, portanto, é muito fácil e econômico mantê-los.

Os programas funcionais são mais simultâneos e seguem o paralelismo na execução para fornecer um desempenho mais preciso e melhor. Haskell não é exceção; foi desenvolvido de forma a lidar commultithreading efetivamente.

Olá Mundo

É um exemplo simples para demonstrar o dinamismo de Haskell. Dê uma olhada no código a seguir. Tudo o que precisamos é de apenas uma linha para imprimir "Hello Word" no console.

main = putStrLn "Hello World"

Assim que o compilador Haskell encontra o trecho de código acima, ele imediatamente produz a seguinte saída -

Hello World

Forneceremos muitos exemplos ao longo deste tutorial para mostrar o poder e a simplicidade de Haskell.

Configuramos o ambiente de programação Haskell online em - https://www.tutorialspoint.com/compile_haskell_online.php

Este editor online tem muitas opções para praticar exemplos de programação Haskell. Vá para a seção de terminal da página e digite"ghci". Este comando carrega automaticamente o compilador Haskell e inicia o Haskell online. Você receberá a seguinte saída após usar oghci comando.

sh-4.3$ ghci
GHCi,version7.8.4:http://www.haskell.org/ghc/:?forhelp
Loading package ghc-prim...linking...done.
Loading packageinteger gmp...linking... done.
Loading package base...linking...done.
Prelude>

Se você ainda deseja usar Haskell offline em seu sistema local, então você precisa baixar a configuração Haskell disponível em sua página oficial - https://www.haskell.org/downloads

Existem três tipos diferentes de installers disponível no mercado -

  • Minimal Installer - Fornece as ferramentas GHC (The Glasgow Haskell Compiler), CABAL (Common Architecture for Building Applications e Bibliotecas) e Stack.

  • Stack Installer- Neste instalador, o GHC pode ser baixado em uma plataforma cruzada de rede de pedágio gerenciada. Ele instalará seu aplicativo globalmente de forma que possa atualizar suas ferramentas API sempre que necessário. Ele resolve automaticamente todas as dependências orientadas a Haskell.

  • Haskell Platform- Esta é a melhor forma de instalar o Haskell, pois ele irá instalar toda a plataforma em sua máquina e de um local específico. Este instalador não é distributivo como os dois instaladores acima.

Vimos diferentes tipos de instaladores disponíveis no mercado, agora vamos ver como usar esses instaladores em nossa máquina. Neste tutorial, vamos usar o instalador da plataforma Haskell para instalar o compilador Haskell em nosso sistema.

Ambiente configurado no Windows

Para configurar o ambiente Haskell em seu computador Windows, acesse o site oficial https://www.haskell.org/platform/windows.html e baixe o instalador de acordo com sua arquitetura personalizável.

Verifique a arquitetura do seu sistema, baixe o arquivo de configuração correspondente e execute-o. Ele será instalado como qualquer outro aplicativo do Windows. Pode ser necessário atualizar a configuração CABAL do seu sistema.

Ambiente Configurado em MAC

Para configurar o ambiente Haskell em seu sistema MAC, acesse o site oficial https://www.haskell.org/platform/mac.html e baixe o instalador do Mac.

Ambiente configurado no Linux

Instalar Haskell em um sistema baseado em Linux requer a execução de algum comando que não é tão fácil como MAC e Windows. Sim, é cansativo, mas confiável.

Você pode seguir as etapas abaixo para instalar Haskell em seu sistema Linux -

Step 1 - Para configurar o ambiente Haskell em seu sistema Linux, vá para o site oficial https://www.haskell.org/platform/linux.htmle escolha sua distribuição. Você encontrará a seguinte tela em seu navegador.

Step 2- Selecione sua distribuição. No nosso caso, estamos usando o Ubuntu. Após selecionar esta opção, você obterá a seguinte página em sua tela com o comando para instalar o Haskell em nosso sistema local.

Step 3 - Abra um terminal pressionando Ctrl + Alt + T. Execute o comando "$ sudo apt-get install haskell-platform"e pressione Enter. Ele iniciará automaticamente o download do Haskell em seu sistema após autenticá-lo com a senha de root. Após a instalação, você receberá uma mensagem de confirmação.

Step 4- Vá ao seu terminal novamente e execute o comando GHCI. Depois de obter o prompt do Prelude, você está pronto para usar Haskell em seu sistema local.

Para sair do prólogo GHCI, você pode usar o comando ": quit exit".

Haskell é uma linguagem de programação puramente funcional, portanto, é muito mais interativa e inteligente do que outras linguagens de programação. Neste capítulo, aprenderemos sobre modelos de dados básicos de Haskell que são predefinidos ou de alguma forma decodificados de forma inteligente na memória do computador.

Ao longo deste tutorial, usaremos a plataforma online Haskell disponível em nosso site (https://www.tutorialspoint.com/codingground.htm)

Números

Haskell é inteligente o suficiente para decodificar alguns números como números. Portanto, você não precisa mencionar seu tipo externamente, como costumamos fazer no caso de outras linguagens de programação. Como por exemplo, vá para o prompt de comando do prelúdio e apenas execute "2 + 2" e pressione Enter.

sh-4.3$ ghci 
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
Prelude> 2+2

Você receberá a seguinte saída como resultado.

4

No código acima, apenas passamos dois números como argumentos para o compilador GHCI sem predefinir seu tipo, mas o compilador poderia facilmente decodificar essas duas entradas como números.

Agora, vamos tentar cálculos matemáticos um pouco mais complexos e ver se nosso compilador inteligente nos dá a saída correta ou não. Experimente com "15+ (5 * 5) -40"

Prelude> 15+(5*5)-40

A expressão acima produz "0" de acordo com a saída esperada.

0

Personagens

Como os números, Haskell pode identificar de forma inteligente um caractere fornecido como uma entrada para ele. Vá para o prompt de comando Haskell e digite qualquer caractere com aspas duplas ou simples.

Deixe-nos fornecer a seguinte linha como entrada e verificar sua saída.

Prelude> :t "a"

Ele produzirá a seguinte saída -

"a" :: [Char]

Lembre-se de que você usa (:t) ao fornecer a entrada. No exemplo acima,(:t)é incluir o tipo específico relacionado às entradas. Aprenderemos mais sobre esse tipo nos próximos capítulos.

Dê uma olhada no exemplo a seguir, onde passamos alguma entrada inválida como um caractere que, por sua vez, leva a um erro.

Prelude> :t a 
<interactive>:1:1: Not in scope: 'a'  

Prelude> a 
<interactive>:4:1: Not in scope: 'a'

Com a mensagem de erro "<interativo>: 4: 1: Fora do escopo:` a '", o compilador Haskell está nos avisando que não é capaz de reconhecer sua entrada. Haskell é um tipo de linguagem onde tudo é representado por um número.

Haskell segue o estilo de codificação ASCII convencional. Vamos dar uma olhada no exemplo a seguir para entender mais -

Prelude> '\97' 
'a'  
Prelude> '\67' 
'C'

Veja como sua entrada é decodificada no formato ASCII.

Corda

UMA stringnada mais é que uma coleção de personagens. Não existe uma sintaxe específica para o uso de string, mas Haskell segue o estilo convencional de representar uma string com aspas duplas.

Dê uma olhada no exemplo a seguir, onde estamos passando a string “Tutorialspoint.com”.

Prelude> :t "tutorialspoint.com"

Ele produzirá a seguinte saída na tela -

"tutorialspoint.com" :: [Char]

Veja como toda a string foi decodificada como uma matriz de Char apenas. Vamos passar para o outro tipo de dados e sua sintaxe. Assim que começarmos nossa prática real, estaremos habituados a todos os tipos de dados e seu uso.

boleano

O tipo de dados booleano também é bastante direto como outros tipos de dados. Veja o exemplo a seguir, onde usaremos diferentes operações booleanas usando algumas entradas booleanas, como "True" ou "False".

Prelude> True && True 
True  
Prelude> True && False 
False   
Prelude> True || True 
True  
Prelude> True || False 
True

No exemplo acima, não precisamos mencionar que "True" e "False" são os valores booleanos. O próprio Haskell pode decodificá-lo e fazer as respectivas operações. Vamos modificar nossas entradas com "verdadeiro" ou "falso".

Prelude> true

Ele produzirá a seguinte saída -

<interactive>:9:1: Not in scope: 'true'

No exemplo acima, Haskell não pôde diferenciar entre "verdadeiro" e um valor numérico, portanto, nossa entrada "verdadeiro" não é um número. Conseqüentemente, o compilador Haskell emite um erro informando que nossa entrada não é seu escopo.

Lista e compreensão da lista

Como outros tipos de dados, Listtambém é um tipo de dados muito útil usado em Haskell. Como por exemplo, [a, b, c] é uma lista de caracteres, portanto, por definição, Lista é uma coleção do mesmo tipo de dados separados por vírgula.

Como outros tipos de dados, você não precisa declarar uma lista como uma lista. Haskell é inteligente o suficiente para decodificar sua entrada olhando para a sintaxe usada na expressão.

Dê uma olhada no exemplo a seguir, que mostra como Haskell trata uma Lista.

Prelude> [1,2,3,4,5]

Ele produzirá a seguinte saída -

[1,2,3,4,5]

As listas em Haskell são homogêneas por natureza, o que significa que não permitem que você declare uma lista de tipos diferentes de dados. Qualquer lista como [1,2,3,4,5, a, b, c, d, e, f] irá produzir um erro.

Prelude> [1,2,3,4,5,a,b,c,d,e,f]

Este código produzirá o seguinte erro -

<interactive>:17:12: Not in scope: 'a' 
<interactive>:17:14: Not in scope: 'b' 
<interactive>:17:16: Not in scope: 'c' 
<interactive>:17:18: Not in scope: 'd' 
<interactive>:17:20: Not in scope: 'e' 
<interactive>:17:22: Not in scope: 'f'

Compreensão de lista

A compreensão de lista é o processo de geração de uma lista usando expressões matemáticas. Veja o exemplo a seguir, em que estamos gerando uma lista usando uma expressão matemática no formato de [saída | intervalo, condição].

Prelude> [x*2| x<-[1..10]] 
[2,4,6,8,10,12,14,16,18,20]  
Prelude> [x*2| x<-[1..5]] 
[2,4,6,8,10]  
Prelude> [x| x<-[1..5]] 
[1,2,3,4,5]

Este método de criação de uma lista usando expressão matemática é chamado de List Comprehension.

Tupla

Haskell fornece outra maneira de declarar vários valores em um único tipo de dados. É conhecido comoTuple. Uma tupla pode ser considerada uma lista, no entanto, existem algumas diferenças técnicas entre uma tupla e uma lista.

Uma Tupla é um tipo de dados imutável, pois não podemos modificar o número de elementos em tempo de execução, enquanto uma Lista é um tipo de dados mutável.

Por outro lado, Lista é um tipo de dados homogêneo, mas Tupla é heterogênea por natureza, porque uma Tupla pode conter diferentes tipos de dados dentro dela.

As tuplas são representadas por parênteses simples. Dê uma olhada no exemplo a seguir para ver como Haskell trata uma tupla.

Prelude> (1,1,'a')

Ele produzirá a seguinte saída -

(1,1,'a')

No exemplo acima, usamos uma Tupla com duas number variáveis ​​de tipo, e um char variável de tipo.

Neste capítulo, aprenderemos sobre os diferentes operadores usados ​​em Haskell. Como outras linguagens de programação, Haskell lida de maneira inteligente com algumas operações básicas como adição, subtração, multiplicação, etc. Nos próximos capítulos, aprenderemos mais sobre os diferentes operadores e seu uso.

Neste capítulo, usaremos diferentes operadores em Haskell usando nossa plataforma online (https://www.tutorialspoint.com/codingground.htm) Lembre-se de que estamos usando apenasinteger digite números porque aprenderemos mais sobre decimal digite os números nos capítulos subsequentes.

Operador de adição

Como o nome sugere, o operador de adição (+) é usado para a função de adição. O código de exemplo a seguir mostra como você pode adicionar dois números inteiros em Haskell -

main = do 
   let var1 = 2 
   let var2 = 3 
   putStrLn "The addition of the two numbers is:" 
   print(var1 + var2)

No arquivo acima, criamos duas variáveis ​​separadas var1 e var2. No final, estamos imprimindo o resultado usando oadditionoperador. Use ocompile e execute botão para executar seu código.

Este código produzirá a seguinte saída na tela -

The addition of the two numbers is:
5

Operador de subtração

Como o nome sugere, esse operador é usado para operação de subtração. O código de exemplo a seguir mostra como você pode subtrair dois números inteiros em Haskell -

main = do 
   let var1 = 10 
   let var2 = 6 
   putStrLn "The Subtraction of the two numbers is:" 
   print(var1 - var2)

Neste exemplo, criamos duas variáveis var1 e var2. Depois disso, usamos o operador de subtração (-) para subtrair os dois valores.

Este código produzirá a seguinte saída na tela -

The Subtraction of the two numbers is:
4

Operador de multiplicação

Este operador é usado para operações de multiplicação. O código a seguir mostra como multiplicar dois números em Haskell usando o operador de multiplicação -

main = do 
   let var1 = 2 
   let var2 = 3 
   putStrLn "The Multiplication of the Two Numbers is:" 
   print(var1 * var2)

Este código produzirá a seguinte saída, quando você executá-lo em nossa plataforma online -

The Multiplication of the Two Numbers is:
6

Operador de Divisão

Dê uma olhada no código a seguir. Mostra como você pode dividir dois números em Haskell -

main = do 
   let var1 = 12 
   let var2 = 3 
   putStrLn "The Division of the Two Numbers is:" 
   print(var1/var2)

Ele produzirá a seguinte saída -

The Division of the Two Numbers is: 
4.0

Operador de sequência / alcance

Sequence or Range é um operador especial em Haskell. É denotado por "(..)". Você pode usar este operador ao declarar uma lista com uma sequência de valores.

Se você quiser imprimir todos os valores de 1 a 10, pode usar algo como "[1..10]". Da mesma forma, se você deseja gerar todos os alfabetos de "a" a "z", basta digitar"[a..z]".

O código a seguir mostra como você pode usar o operador Sequence para imprimir todos os valores de 1 a 10 -

main :: IO() 
main = do 
   print [1..10]

Ele irá gerar a seguinte saída -

[1,2,3,4,5,6,7,8,9,10]

A Tomada de Decisão é um recurso que permite aos programadores aplicar uma condição no fluxo do código. O programador pode executar um conjunto de instruções dependendo de uma condição predefinida. O fluxograma a seguir mostra a estrutura de tomada de decisão de Haskell -

Haskell fornece os seguintes tipos de declarações de tomada de decisão -

Sr. Não. Declaração e descrição
1 instrução if – else

1 if declaração com um elsedeclaração. A instrução noelse O bloco será executado apenas quando a condição booleana fornecida falhar em satisfazer.

2 Instrução if-else aninhada

Múltiplo if blocos seguidos por else blocos

Haskell é uma linguagem funcional e é rigidamente tipada, o que significa que o tipo de dados usado em toda a aplicação será conhecido pelo compilador no momento da compilação.

Classe de tipo embutido

Em Haskell, cada afirmação é considerada uma expressão matemática e a categoria desta expressão é chamada de Type. Você pode dizer que "Tipo" é o tipo de dados da expressão usada em tempo de compilação.

Para saber mais sobre o Type, usaremos o comando ": t". De uma forma genérica,Type pode ser considerado um valor, enquanto Type Classpode ser considerado como um conjunto de tipos semelhantes. Neste capítulo, aprenderemos sobre os diferentes tipos embutidos.

Int

Inté uma classe de tipo que representa os dados de tipos inteiros. Cada número inteiro dentro do intervalo de 2147483647 a -2147483647 está sob oIntclasse de tipo. No exemplo a seguir, a funçãofType() irá se comportar de acordo com seu tipo definido.

fType :: Int -> Int -> Int 
fType x y = x*x + y*y
main = print (fType 2 4)

Aqui, definimos o tipo de função fType() Como int. A função leva doisint valores e retorna um intvalor. Se você compilar e executar este trecho de código, ele produzirá a seguinte saída -

sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts 
sh-4.3$ main
20

Inteiro

Integer pode ser considerado um superconjunto de Int. Este valor não é limitado por nenhum número, portanto, um inteiro pode ter qualquer comprimento sem qualquer limitação. Para ver a diferença básica entreInt e Integer tipos, vamos modificar o código acima da seguinte maneira -

fType :: Int -> Int -> Int 
fType x y = x*x + y*y 
main = print (fType 212124454 44545454454554545445454544545)

Se você compilar o trecho de código acima, a seguinte mensagem de erro será lançada -

main.hs:3:31: Warning:            
   Literal 44545454454554545445454544545 is out of the Int range -
   9223372036854775808..9223372036854775807 
Linking main ...

Este erro ocorreu porque nossa função fType () esperava um valor de tipo Int e estamos passando algum valor de tipo Int realmente grande. Para evitar este erro, vamos modificar o tipo "Int" para "Integer" e observar a diferença.

fType :: Integer -> Integer -> Integer 
fType x y = x*x + y*y 
main = print (fType 212124454 4454545445455454545445445454544545)

Agora, ele produzirá a seguinte saída -

sh-4.3$ main
1984297512562793395882644631364297686099210302577374055141

Flutuador

Dê uma olhada no seguinte trecho de código. Mostra como funciona o tipo Float em Haskell -

fType :: Float -> Float -> Float 
fType x y = x*x + y*y 
main = print (fType 2.5 3.8)

A função assume dois valores flutuantes como entrada e produz outro valor flutuante como saída. Quando você compila e executa este código, ele produzirá a seguinte saída -

sh-4.3$ main
20.689999

em dobro

Doubleé um número de ponto flutuante com precisão dupla no final. Dê uma olhada no seguinte exemplo -

fType :: Double -> Double -> Double 
fType x y = x*x + y*y 
main = print (fType 2.56 3.81)

Quando você executa o trecho de código acima, ele irá gerar a seguinte saída -

sh-4.3$ main 
21.0697

Bool

Boolé um tipo booleano. Pode ser verdadeiro ou falso. Execute o seguinte código para entender como o tipo Bool funciona em Haskell -

main = do  
   let x = True 
   
   if x == False 
      then putStrLn "X matches with Bool Type" 
   else putStrLn "X is not a Bool Type"

Aqui, estamos definindo uma variável "x" como um Bool e comparando-a com outro valor booleano para verificar sua originalidade. Ele produzirá a seguinte saída -

sh-4.3$ main
X is not a Bool Type

Caracteres

Char representam personagens. Qualquer coisa dentro de uma aspa simples é considerada um personagem. No código a seguir, modificamos nossofType() função para aceitar o valor Char e retornar o valor Char como saída.

fType :: Char-> Char 
fType x = 'K' 
main = do  
   let x = 'v' 
   print (fType x)

O trecho de código acima chamará fType() funcionar com um charvalor de 'v', mas retorna outro valor char, ou seja, 'K'. Aqui está o resultado -

sh-4.3$ main 
'K'

Observe que não vamos usar esses tipos explicitamente porque Haskell é inteligente o suficiente para capturar o tipo antes que ele seja declarado. Nos capítulos subsequentes deste tutorial, veremos como diferentes tipos e classes de tipo tornam Haskell uma linguagem fortemente tipada.

Classe de tipo EQ

EQclasse de tipo é uma interface que fornece a funcionalidade para testar a igualdade de uma expressão. Qualquer classe de tipo que deseja verificar a igualdade de uma expressão deve fazer parte desta classe de tipo de EQ.

Todas as classes de tipo padrão mencionadas acima fazem parte deste EQclasse. Sempre que estamos verificando qualquer igualdade usando qualquer um dos tipos mencionados acima, estamos na verdade fazendo uma chamada paraEQ classe de tipo.

No exemplo a seguir, estamos usando o EQ Digite internamente usando a operação "==" ou "/ =".

main = do 
   if 8 /= 8 
      then putStrLn "The values are Equal" 
   else putStrLn "The values are not Equal"

Isso produzirá a seguinte saída -

sh-4.3$ main 
The values are not Equal

Classe de tipo ord

Ordé outra classe de interface que nos dá a funcionalidade de ordenação. Todostypes que temos usado até agora são parte disso Ordinterface. Como a interface EQ, a interface Ord pode ser chamada usando ">", "<", "<=", "> =", "compare".

Veja abaixo o exemplo em que usamos a funcionalidade de “comparação” dessa classe de tipo.

main = print (4 <= 2)

Aqui, o compilador Haskell verificará se 4 é menor ou igual a 2. Como não é, o código produzirá a seguinte saída -

sh-4.3$ main 
False

exposição

Showtem a funcionalidade de imprimir seu argumento como uma String. Qualquer que seja seu argumento, ele sempre imprime o resultado como uma String. No exemplo a seguir, imprimiremos a lista inteira usando esta interface. "show" pode ser usado para chamar esta interface.

main = print (show [1..10])

Ele produzirá a seguinte saída no console. Aqui, as aspas duplas indicam que é um valor do tipo String.

sh-4.3$ main 
"[1,2,3,4,5,6,7,8,9,10]"

Ler

Readinterface faz a mesma coisa que Mostrar, mas não imprimirá o resultado no formato String. No código a seguir, usamos oread interface para ler um valor de string e convertê-lo em um valor Int.

main = print (readInt "12") 
readInt :: String -> Int 
readInt = read

Aqui, estamos passando uma variável String ("12") para o readIntmétodo que, por sua vez, retorna 12 (um valor Int) após a conversão. Aqui está o resultado -

sh-4.3$ main 
12

Enum

Enumé outro tipo de classe Type que ativa a funcionalidade sequencial ou ordenada em Haskell. Esta classe de tipo pode ser acessada por comandos comoSucc, Pred, Bool, Charetc.

O código a seguir mostra como encontrar o valor do sucessor de 12.

main = print (succ 12)

Ele produzirá a seguinte saída -

sh-4.3$ main
13

Delimitado

Todos os tipos com limites superior e inferior vêm sob esta classe de tipo. Por exemplo,Int Os dados do tipo têm limite máximo de "9223372036854775807" e limite mínimo de "-9223372036854775808".

O código a seguir mostra como Haskell determina o limite máximo e mínimo do tipo Int.

main = do 
   print (maxBound :: Int) 
   print (minBound :: Int)

Ele produzirá a seguinte saída -

sh-4.3$ main
9223372036854775807
-9223372036854775808

Agora, tente encontrar o limite máximo e mínimo dos tipos Char, Float e Bool.

Num

Esta classe de tipo é usada para operações numéricas. Tipos como Int, Integer, Float e Double estão nessa classe de tipo. Dê uma olhada no seguinte código -

main = do 
   print(2 :: Int)  
   print(2 :: Float)

Ele produzirá a seguinte saída -

sh-4.3$ main
2
2.0

Integrante

Integralpode ser considerada uma subclasse da classe Num Type. A classe Num Type contém todos os tipos de números, enquanto a classe do tipo Integral é usada apenas para números inteiros. Int e Integer são os tipos dessa classe de tipo.

Flutuando

Como Integral, Floating também faz parte da classe Num Type, mas contém apenas números de ponto flutuante. Conseqüentemente,Float e Double vêm sob este tipo de classe.

Classe de tipo personalizado

Como qualquer outra linguagem de programação, Haskell permite que os desenvolvedores definam os tipos definidos pelo usuário. No exemplo a seguir, criaremos um tipo definido pelo usuário e o usaremos.

data Area = Circle Float Float Float  
surface :: Area -> Float   
surface (Circle _ _ r) = pi * r ^ 2   
main = print (surface $ Circle 10 20 10 )

Aqui, criamos um novo tipo chamado Area. Em seguida, estamos usando esse tipo para calcular a área de um círculo. No exemplo acima, "superfície" é uma função que levaArea como uma entrada e produz Float como a saída.

Lembre-se de que "dados" é uma palavra-chave aqui e todos os tipos definidos pelo usuário em Haskell sempre começam com uma letra maiúscula.

Ele produzirá a seguinte saída -

sh-4.3$ main
314.15927

As funções desempenham um papel importante em Haskell, pois é uma linguagem de programação funcional. Como outras linguagens, Haskell tem sua própria definição e declaração funcional.

  • A declaração da função consiste no nome da função e sua lista de argumentos junto com sua saída.

  • A definição de função é onde você realmente define uma função.

Vamos dar um pequeno exemplo de add função para compreender este conceito em detalhes.

add :: Integer -> Integer -> Integer   --function declaration 
add x y =  x + y                       --function definition 

main = do 
   putStrLn "The addition of the two numbers is:"  
   print(add 2 5)    --calling a function

Aqui, declaramos nossa função na primeira linha e, na segunda linha, escrevemos nossa função real que receberá dois argumentos e produzirá uma saída do tipo inteiro.

Como a maioria das outras linguagens, Haskell começa a compilar o código do mainmétodo. Nosso código irá gerar a seguinte saída -

The addition of the two numbers is:
7

Correspondência de padrões

A correspondência de padrões é o processo de correspondência de tipos específicos de expressões. Não é nada além de uma técnica para simplificar seu código. Essa técnica pode ser implementada em qualquer tipo de classe de tipo. If-Else pode ser usado como uma opção alternativa de correspondência de padrões.

A correspondência de padrões pode ser considerada como uma variante do polimorfismo dinâmico onde, em tempo de execução, diferentes métodos podem ser executados dependendo de sua lista de argumentos.

Dê uma olhada no seguinte bloco de código. Aqui, usamos a técnica de correspondência de padrões para calcular o fatorial de um número.

fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5)

Todos nós sabemos calcular o fatorial de um número. O compilador começará a procurar uma função chamada "fato" com um argumento. Se o argumento não for igual a 0, o número continuará chamando a mesma função com 1 a menos que o argumento real.

Quando o padrão do argumento corresponder exatamente a 0, ele chamará nosso padrão, que é "fato 0 = 1". Nosso código produzirá a seguinte saída -

The factorial of 5 is:
120

Guardas

Guardsé um conceito muito semelhante ao casamento de padrões. Na correspondência de padrões, geralmente combinamos uma ou mais expressões, mas usamosguards para testar alguma propriedade de uma expressão.

Embora seja aconselhável usar correspondência de padrões sobre guards, mas da perspectiva de um desenvolvedor, guardsé mais legível e simples. Para usuários iniciantes,guards podem ser muito semelhantes às instruções If-Else, mas são funcionalmente diferentes.

No código a seguir, modificamos nosso factorial programa usando o conceito de guards.

fact :: Integer -> Integer 
fact n | n == 0 = 1 
       | n /= 0 = n * fact (n-1) 
main = do 
   putStrLn "The factorial of 5 is:"  
   print (fact 5)

Aqui, nós declaramos dois guards, separados por "|" e chamando ofact função de main. Internamente, o compilador funcionará da mesma maneira que no caso de correspondência de padrões para produzir a seguinte saída -

The factorial of 5 is:
120

Cláusula Where

Whereé uma palavra-chave ou função embutida que pode ser usada em tempo de execução para gerar uma saída desejada. Pode ser muito útil quando o cálculo da função se torna complexo.

Considere um cenário onde sua entrada é uma expressão complexa com vários parâmetros. Nesses casos, você pode quebrar a expressão inteira em pequenas partes usando a cláusula "where".

No exemplo a seguir, estamos pegando uma expressão matemática complexa. Mostraremos como você pode encontrar as raízes de uma equação polinomial [x ^ 2 - 8x + 6] usando Haskell.

roots :: (Float, Float, Float) -> (Float, Float)  
roots (a,b,c) = (x1, x2) where 
   x1 = e + sqrt d / (2 * a) 
   x2 = e - sqrt d / (2 * a) 
   d = b * b - 4 * a * c  
   e = - b / (2 * a)  
main = do 
   putStrLn "The roots of our Polynomial equation are:" 
   print (roots(1,-8,6))

Observe a complexidade de nossa expressão para calcular as raízes da função polinomial fornecida. É bastante complexo. Portanto, estamos quebrando a expressão usando owherecláusula. O trecho de código acima irá gerar a seguinte saída -

The roots of our Polynomial equation are:
(7.1622777,0.8377223)

Função de recursão

Recursão é uma situação em que uma função chama a si mesma repetidamente. Haskell não fornece nenhum recurso de loop de qualquer expressão por mais de uma vez. Em vez disso, Haskell deseja que você divida toda a sua funcionalidade em uma coleção de funções diferentes e use a técnica de recursão para implementar sua funcionalidade.

Vamos considerar nosso exemplo de correspondência de padrões novamente, onde calculamos o fatorial de um número. Encontrar o fatorial de um número é um caso clássico de recursão. Aqui, você pode: "Como a correspondência de padrões é diferente da recursão?” A diferença entre esses dois está na maneira como são usados. A correspondência de padrões funciona na configuração da restrição de terminal, enquanto a recursão é uma chamada de função.

No exemplo a seguir, usamos a correspondência de padrões e a recursão para calcular o fatorial de 5.

fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5)

Ele produzirá a seguinte saída -

The factorial of 5 is:
120

Função de ordem superior

Até agora, o que vimos é que as funções Haskell têm um type como entrada e produzir outro typecomo saída, que é muito semelhante em outras linguagens imperativas. Funções de ordem superior são um recurso exclusivo do Haskell onde você pode usar uma função como argumento de entrada ou saída.

Embora seja um conceito virtual, mas em programas do mundo real, cada função que definimos em Haskell usa um mecanismo de ordem superior para fornecer saída. Se você tiver a chance de examinar a função de biblioteca de Haskell, descobrirá que a maioria das funções de biblioteca foi escrita em ordem superior.

Tomemos um exemplo em que importaremos um mapa de funções de ordem superior embutido e usaremos o mesmo para implementar outra função de ordem superior de acordo com nossa escolha.

import Data.Char  
import Prelude hiding (map) 

map :: (a -> b) -> [a] -> [b] 
map _ [] = [] 
map func (x : abc) = func x : map func abc  
main = print $ map toUpper "tutorialspoint.com"

No exemplo acima, usamos o toUpper função da classe de tipo Charpara converter nossa entrada em maiúsculas. Aqui, o método "map" está tomando uma função como argumento e retornando a saída necessária. Aqui está o resultado -

sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts sh-4.3$ main
"TUTORIALSPOINT.COM"

Expressão Lambda

Às vezes, temos que escrever uma função que será usada apenas uma vez, durante toda a vida útil de um aplicativo. Para lidar com esse tipo de situação, os desenvolvedores de Haskell usam outro bloco anônimo conhecido comolambda expression ou lambda function.

Uma função sem definição é chamada de função lambda. Uma função lambda é denotada pelo caractere "\". Tomemos o seguinte exemplo onde aumentaremos o valor de entrada em 1 sem criar nenhuma função.

main = do 
   putStrLn "The successor of 4 is:"  
   print ((\x -> x + 1) 4)

Aqui, criamos uma função anônima que não possui um nome. Leva o inteiro 4 como argumento e imprime o valor de saída. Basicamente, estamos operando uma função sem nem mesmo declará-la adequadamente. Essa é a beleza das expressões lambda.

Nossa expressão lambda produzirá a seguinte saída -

sh-4.3$ main
The successor of 4 is:
5

Até agora, discutimos muitos tipos de funções Haskell e usamos diferentes maneiras de chamar essas funções. Neste capítulo, aprenderemos sobre algumas funções básicas que podem ser facilmente usadas em Haskell sem importar nenhuma classe especial de tipo. A maioria dessas funções faz parte de outras funções de ordem superior.

Função Principal

Headfunção funciona em uma lista. Ele retorna o primeiro argumento de entrada que é basicamente uma lista. No exemplo a seguir, estamos passando uma lista com 10 valores e estamos gerando o primeiro elemento dessa lista usando ohead função.

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "The first element of the list is:" 
   print (head x)

Ele produzirá a seguinte saída -

Our list is: 
[1,2,3,4,5,6,7,8,9,10]
The first element of the list is:
1

Função Tail

Tail é a função que complementa o headfunção. Leva umlistcomo entrada e produz a lista inteira sem a parte principal. Isso significa que otailfunção retorna a lista inteira sem o primeiro elemento. Dê uma olhada no seguinte exemplo -

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "The tail of our list is:" 
   print (tail x)

Ele produzirá a seguinte saída -

Our list is:
[1,2,3,4,5,6,7,8,9,10]
The tail of our list is:
[2,3,4,5,6,7,8,9,10]

Última Função

Como o nome sugere, ele produz o último elemento da lista que é fornecido como entrada. Verifique o seguinte exemplo.

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "The last element of our list is:" 
   print (last x)

Ele produzirá a seguinte saída -

Our list is:
[1,2,3,4,5,6,7,8,9,10]
The last element of our list is:
10

Função Init

Init funciona exatamente como o oposto de tailfunção. Recebe uma lista como argumento e retorna a lista inteira sem a última entrada.

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "Our list without the last entry:"  
   print (init x)

Agora, observe sua saída -

Our list is:
[1,2,3,4,5,6,7,8,9,10]
Our list without the last entry:
[1,2,3,4,5,6,7,8,9]

Função Nula

Null é uma função de verificação booleana que funciona em uma string e retorna True apenas quando a lista fornecida está vazia, caso contrário, ele retorna False. O código a seguir verifica se a lista fornecida está vazia ou não.

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "Is our list empty?"  
   print (null x)

Ele produzirá a seguinte saída -

Our list is:
[1,2,3,4,5,6,7,8,9,10]
Is our list empty?
False

Função reversa

Ele funciona em uma entrada String e converte toda a entrada em ordem reversa e dá uma saída como resultado. Abaixo está a base de código para esta função.

main = do 
   let x = [1..10]  
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "The list in Reverse Order is:" 
   print (reverse x)

Ele produzirá a seguinte saída -

Our list is:
[1,2,3,4,5,6,7,8,9,10]
The list in Reverse Order is:
[10,9,8,7,6,5,4,3,2,1]

Função Comprimento

Esta função é usada para calcular o comprimento do listdado como um argumento. Dê uma olhada no seguinte exemplo -

main = do 
   let x = [1..10]   
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "The length of this list is:" 
   print (length x)

Temos 10 elementos em nossa lista, portanto, nosso código produzirá 10 como saída.

Our list is:
[1,2,3,4,5,6,7,8,9,10]
The length of this list is:
10

Take Function

Takefunção é usada para criar uma sub-string de outra String. O código a seguir mostra como você pode usar a função take em Haskell -

main = print(take 5 ([1 .. 10]))

O código gera uma sub-string contendo 5 elementos da lista fornecida -

[1,2,3,4,5]

Função de queda

Esta função também é usada para gerar uma string secundária. Funciona como o oposto dotakefunção. Observe o seguinte trecho de código -

main = print(drop 5 ([1 .. 10]))

O código descarta os primeiros 5 elementos da lista fornecida e imprime os 5 elementos restantes. Ele produzirá a seguinte saída -

[6,7,8,9,10]

Função Máxima

Esta função é usada para encontrar o elemento com o valor máximo da lista fornecida. Vamos ver como usá-lo na prática -

main = do 
   let x = [1,45,565,1245,02,2]   
   putStrLn "The maximum value element of the list is:"  
   print (maximum x)

O trecho de código acima irá gerar a seguinte saída -

The maximum value element of the list is:
1245

Função Mínima

Esta função é usada para encontrar o elemento com o valor mínimo da lista fornecida. É exatamente o oposto domaximum função.

main = do 
   let x = [1,45,565,1245,02,2]   
   putStrLn "The minimum value element of the list is:"  
   print (minimum x)

A saída do código acima é -

The minimum value element of the list is:
1

Função Soma

Como o nome sugere, esta função retorna a soma de todos os elementos presentes na lista fornecida. O código a seguir pega uma lista de 5 elementos e retorna sua soma como saída.

main = do 
   let x = [1..5] 
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "The summation of the list elements is:" 
   print (sum x)

Ele produzirá a seguinte saída -

Our list is:
[1,2,3,4,5]
The summation of the list elements is:
15

Função do Produto

Você pode usar esta função para multiplicar todos os elementos em uma lista e imprimir seu valor.

main = do 
   let x = [1..5] 
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "The multiplication of the list elements is:" 
   print (product x)

Nosso código produzirá a seguinte saída -

Our list is:
[1,2,3,4,5]
The multiplication of the list elements is: 
120

Função Elem

Esta função é usada para verificar se a lista fornecida contém um elemento específico ou não. Assim, ele retorna umtrue ou um false.

O código a seguir verifica se a lista de elementos fornecida contém o valor 786.

main = do 
   let x = [1,45,155,1785] 
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "Does it contain 786?" 
   print (elem 786 (x))

Ele produzirá a seguinte saída -

Our list is:
[1,45,155,1785]
Does it contain 786?
False

Use o mesmo código para verificar se a lista fornecida contém o valor 1785 ou não.

Function Compositioné o processo de usar a saída de uma função como entrada de outra função. Será melhor se aprendermos a matemática por tráscomposition. Na matemática,composition é denotado por f{g(x)} Onde g() é uma função e sua saída é usada como uma entrada de outra função, ou seja, f().

A composição da função pode ser implementada usando quaisquer duas funções, desde que o tipo de saída de uma função corresponda ao tipo de entrada da segunda função. Usamos o operador ponto (.) Para implementar a composição da função em Haskell.

Dê uma olhada no código de exemplo a seguir. Aqui, usamos a composição de funções para calcular se um número de entrada é par ou ímpar.

eveno :: Int -> Bool 
noto  :: Bool -> String 

eveno x = if x `rem` 2 == 0 
   then True 
else False 
noto x = if x == True 
   then "This is an even Number" 
else "This is an ODD number" 

main = do 
   putStrLn "Example of Haskell Function composition" 
   print ((noto.eveno)(16))

Aqui no main função, estamos chamando duas funções, noto e eveno, simultaneamente. O compilador irá primeiro chamar a função"eveno()" com 16como um argumento. Depois disso, o compilador usará a saída doeveno método como uma entrada de noto() método.

Sua saída seria a seguinte -

Example of Haskell Function composition                
"This is an even Number"

Uma vez que estamos fornecendo o número 16 como entrada (que é um número par), o eveno() função retorna true, que se torna a entrada para o noto() função e retorna a saída: "Este é um número par".

Se você trabalhou em Java, então você sabe como todas as classes são vinculadas a uma pasta chamada package. Da mesma forma, Haskell pode ser considerado uma coleção demodules.

Haskell é uma linguagem funcional e tudo é denotado como uma expressão, portanto, um Módulo pode ser chamado como uma coleção de tipos de funções semelhantes ou relacionados.

Você pode importuma função de um módulo em outro módulo. Todas as instruções "import" devem vir primeiro, antes de você começar a definir outras funções. Neste capítulo, aprenderemos os diferentes recursos dos módulos Haskell.

Módulo de Lista

List fornece algumas funções maravilhosas para trabalhar listdigite dados. Depois de importar o módulo Lista, você terá uma ampla gama de funções à sua disposição.

No exemplo a seguir, usamos algumas funções importantes disponíveis no módulo Lista.

import Data.List  

main = do  
   putStrLn("Different methods of List Module") 
   print(intersperse '.' "Tutorialspoint.com") 
   print(intercalate " " ["Lets","Start","with","Haskell"]) 
   print(splitAt 7 "HaskellTutorial") 
   print (sort [8,5,3,2,1,6,4,2])

Aqui, temos muitas funções, mesmo sem defini-las. Isso ocorre porque essas funções estão disponíveis no módulo Lista. Após importar o módulo List, o compilador Haskell disponibilizou todas essas funções no namespace global. Portanto, poderíamos usar essas funções.

Nosso código produzirá a seguinte saída -

Different methods of List Module
"T.u.t.o.r.i.a.l.s.p.o.i.n.t...c.o.m"
"Lets Start with Haskell"
("Haskell","Tutorial")
[1,2,2,3,4,5,6,8]

Módulo Char

o CharO módulo tem muitas funções predefinidas para trabalhar com o tipo de caractere. Dê uma olhada no seguinte bloco de código -

import Data.Char 

main = do  
   putStrLn("Different methods of Char Module") 
   print(toUpper 'a') 
   print(words "Let us study tonight") 
   print(toLower 'A')

Aqui, as funções toUpper e toLower já estão definidos dentro do Charmódulo. Ele produzirá a seguinte saída -

Different methods of Char Module
'A'
["Let","us","study","tonight"]
'a'

Módulo de Mapa

Mapé um tipo de dados de par de valor agregado não classificado. É um módulo amplamente utilizado com muitas funções úteis. O exemplo a seguir mostra como você pode usar uma função predefinida disponível no módulo Mapa.

import Data.Map (Map) 
import qualified Data.Map as Map  --required for GHCI  

myMap :: Integer -> Map Integer [Integer] 
myMap n = Map.fromList (map makePair [1..n]) 
   where makePair x = (x, [x])  

main = print(myMap 3)

Ele produzirá a seguinte saída -

fromList [(1,[1]),(2,[2]),(3,[3])]

Definir Módulo

O módulo Set tem algumas funções predefinidas muito úteis para manipular dados matemáticos. Um conjunto é implementado como uma árvore binária, portanto, todos os elementos em um conjunto devem ser únicos.

Dê uma olhada no seguinte código de exemplo

import qualified Data.Set as Set   

text1 = "Hey buddy"   
text2 = "This tutorial is for Haskell"   

main = do  
   let set1 = Set.fromList text1   
       set2 = Set.fromList text2 
   print(set1) 
   print(set2)

Aqui, estamos modificando uma String em um Conjunto. Ele produzirá a seguinte saída. Observe que o conjunto de saída não possui repetição de caracteres.

fromList " Hbdeuy"
fromList " HTaefhiklorstu"

Módulo Personalizado

Vamos ver como podemos criar um módulo personalizado que pode ser chamado em outros programas. Para implementar este módulo personalizado, criaremos um arquivo separado chamado"custom.hs" junto com nosso "main.hs".

Vamos criar o módulo personalizado e definir algumas funções nele.

custom.hs

module Custom ( 
   showEven, 
   showBoolean 
) where 

showEven:: Int-> Bool 
showEven x = do 

if x 'rem' 2 == 0 
   then True 
else False 
showBoolean :: Bool->Int 
showBoolean c = do 

if c == True 
   then 1 
else 0

Nosso módulo personalizado está pronto. Agora, vamos importá-lo para um programa.

main.hs

import Custom 

main = do 
   print(showEven 4) 
   print(showBoolean True)

Nosso código irá gerar a seguinte saída -

True
1

o showEven função retorna True, pois "4" é um número par. oshowBoolean function retorna "1", pois a função booleana que passamos para a função é "True".

Todos os exemplos que discutimos até agora são de natureza estática. Neste capítulo, aprenderemos a se comunicar dinamicamente com os usuários. Aprenderemos diferentes técnicas de entrada e saída usadas em Haskell.

Arquivos e streams

Até agora, codificamos todas as entradas do próprio programa. Temos obtido entradas de variáveis ​​estáticas. Agora, vamos aprender como ler e escrever a partir de um arquivo externo.

Vamos criar um arquivo com o nome "abc.txt". Em seguida, insira as seguintes linhas neste arquivo de texto: "Bem-vindo ao Tutorialspoint. Aqui, você obterá o melhor recurso para aprender Haskell."

A seguir, escreveremos o código a seguir, que exibirá o conteúdo desse arquivo no console. Aqui, estamos usando a função readFile () que lê um arquivo até encontrar um caractere EOF.

main = do  
   let file = "abc.txt" 
   contents <- readFile file 
   putStrLn contents

O trecho de código acima lerá o arquivo "abc.txt" como uma String até encontrar qualquer caractere de Fim de Arquivo. Este pedaço de código irá gerar a seguinte saída.

Welcome to Tutorialspoint
Here, you will get the best resource to learn Haskell.

Observe que tudo o que está sendo impresso no terminal está escrito naquele arquivo.

Argumento de linha de comando

Haskell também fornece a facilidade de operar um arquivo por meio do prompt de comando. Vamos voltar ao nosso terminal e digitar"ghci". Em seguida, digite o seguinte conjunto de comandos -

let file = "abc.txt" 
writeFile file "I am just experimenting here." 
readFile file

Aqui, criamos um arquivo de texto chamado "abc.txt". Em seguida, inserimos uma instrução no arquivo usando o comandowriteFile. Finalmente, usamos o comandoreadFilepara imprimir o conteúdo do arquivo no console. Nosso código produzirá a seguinte saída -

I am just experimenting here.

Exceções

A exceptionpode ser considerado um bug no código. É uma situação em que o compilador não obtém a saída esperada no tempo de execução. Como qualquer outra boa linguagem de programação, Haskell fornece uma maneira de implementar o tratamento de exceções.

Se você estiver familiarizado com Java, então deve conhecer o bloco Try-Catch, onde geralmente lançamos um erro e detectamos o mesmo no catchquadra. Em Haskell, também temos a mesma função para capturar erros de tempo de execução.

A definição da função de tryse parece com "try :: Exception e => IO a -> IO (Either ea)". Dê uma olhada no código de exemplo a seguir. Mostra como você pode capturar a exceção "Divide by Zero".

import Control.Exception 

main = do 
   result <- try (evaluate (5 `div` 0)) :: IO (Either SomeException Int) 
   case result of 
      Left ex   -> putStrLn $ "Caught exception: " ++ show ex 
      Right val -> putStrLn $ "The answer was: " ++ show val

No exemplo acima, usamos o embutido try função do Control.Exceptionmódulo, portanto, estamos capturando a exceção de antemão. O trecho de código acima renderá abaixo da saída na tela.

Caught exception: divide by zero

Functorem Haskell é um tipo de representação funcional de diferentes tipos que podem ser mapeados. É um conceito de alto nível de implementação de polimorfismo. De acordo com os desenvolvedores do Haskell, todos os Tipos, como Lista, Mapa, Árvore, etc. são a instância do Função Haskell.

UMA Functor é uma classe embutida com uma definição de função como -

class Functor f where 
   fmap :: (a -> b) -> f a -> f b

Por esta definição, podemos concluir que o Functor é uma função que assume uma função, digamos, fmap()e retorna outra função. No exemplo acima,fmap() é uma representação generalizada da função map().

No exemplo a seguir, veremos como o Haskell Functor funciona.

main = do  
   print(map (subtract 1) [2,4,8,16])      
   print(fmap (subtract 1) [2,4,8,16])

Aqui, usamos ambos map() e fmap()sobre uma lista para uma operação de subtração. Você pode observar que ambas as declarações produzirão o mesmo resultado de uma lista contendo os elementos [1,3,7,15].

Ambas as funções são chamadas de outra função chamada subtract() para produzir o resultado.

[1,3,7,15]
[1,3,7,15]

Então, qual é a diferença entre map e fmap? A diferença está em seu uso. Functor nos permite implementar mais alguns funcionalistas em diferentes tipos de dados, como "apenas" e "Nada".

main = do 
   print (fmap  (+7)(Just 10)) 
   print (fmap  (+7) Nothing)

O trecho de código acima produzirá a seguinte saída no terminal -

Just 17
Nothing

Função Aplicativa

Um Functor Aplicativo é um Functor normal com alguns recursos extras fornecidos pela Classe de Tipo Aplicativo.

Usando Functor, geralmente mapeamos uma função existente com outra função definida dentro dela. Mas não há como mapear uma função definida dentro de um Functor com outro Functor. É por isso que temos outra instalação chamadaApplicative Functor. Esta facilidade de mapeamento é implementada pela classe de Tipo de Aplicativo definida noControlmódulo. Esta classe nos dá apenas dois métodos para trabalhar: um épure e o outro é <*>.

A seguir está a definição de classe do Functor Aplicativo.

class (Functor f) => Applicative f where   
   pure :: a -> f a   
   (<*>) :: f (a -> b) -> f a -> f b

De acordo com a implementação, podemos mapear outro Functor usando dois métodos: "Pure" e "<*>". O método "Pure" deve assumir um valor de qualquer tipo e sempre retornará um Functor Aplicativo desse valor.

O exemplo a seguir mostra como um Functor Aplicativo funciona -

import Control.Applicative 

f1:: Int -> Int -> Int 
f1 x y = 2*x+y  
main = do  
   print(show $ f1 <$> (Just 1) <*> (Just 2) )

Aqui, implementamos functores aplicativos na chamada de função da função f1. Nosso programa produzirá a seguinte saída.

"Just 4"

Monoides

Todos nós sabemos que Haskell define tudo na forma de funções. Nas funções, temos opções para obter nossa entrada como saída da função. Isso é o queMonoid é.

UMA Monoidé um conjunto de funções e operadores onde a saída é independente de sua entrada. Vamos pegar uma função (*) e um inteiro (1). Agora, qualquer que seja a entrada, sua saída permanecerá o mesmo número apenas. Ou seja, se você multiplicar um número por 1, obterá o mesmo número.

Aqui está uma definição de classe de tipo de monóide.

class Monoid m where  
   mempty :: m 
   mappend :: m -> m -> m  
   mconcat :: [m] -> m 
   mconcat = foldr mappend mempty

Dê uma olhada no exemplo a seguir para entender o uso de Monoid em Haskell.

multi:: Int->Int 
multi x = x * 1 
add :: Int->Int 
add x = x + 0 

main = do  
   print(multi 9)  
   print (add 7)

Nosso código produzirá a seguinte saída -

9
7

Aqui, a função "multi" multiplica a entrada por "1". Da mesma forma, a função "add" adiciona a entrada com "0". Em ambos os casos, a saída será igual à entrada. Portanto, as funções{(*),1} e {(+),0} são os exemplos perfeitos de monoides.

Monadsnada mais são do que um tipo de Função Aplicativa com alguns recursos extras. É uma classe de tipo que governa três regras básicas conhecidas comomonadic rules.

Todas as três regras são estritamente aplicáveis ​​em uma declaração da Mônada, que é a seguinte -

class Monad m where  
   return :: a -> m a 
   (>>=) :: m a -> (a -> m b) -> m b 
   (>>) :: m a -> m b -> m b 
   x >> y = x >>= \_ -> y 
   fail :: String -> m a  
   fail msg = error msg

As três leis básicas que são aplicáveis ​​em uma declaração Mônada são -

  • Left Identity Law - o returna função não altera o valor e não deve alterar nada na Mônada. Pode ser expresso como "return> => mf = mf".

  • Right Identity Law - o returna função não altera o valor e não deve alterar nada na Mônada. Pode ser expresso como "mf> => return = mf".

  • Associativity- De acordo com esta lei, tanto os Functors quanto a instância Monad devem funcionar da mesma maneira. Pode ser expresso matematicamente como "(f> ==> g)> => h = f> => (g> = h)".

As primeiras duas leis repetem o mesmo ponto, ou seja, um return deve ter comportamento de identidade em ambos os lados do bind operador.

Já usamos muitas Mônadas em nossos exemplos anteriores sem perceber que são Mônadas. Considere o exemplo a seguir, em que estamos usando uma Monad de Lista para gerar uma lista específica.

main = do
   print([1..10] >>= (\x -> if odd x then [x*2] else []))

Este código produzirá a seguinte saída -

[2,6,10,14,18]

Zippers em Haskell são basicamente ponteiros que apontam para algum local específico de uma estrutura de dados, como um tree.

Vamos considerar um tree tendo 5 elementos [45,7,55,120,56]que pode ser representada como uma árvore binária perfeita. Se eu quiser atualizar o último elemento desta lista, preciso percorrer todos os elementos para chegar ao último elemento antes de atualizá-lo. Direito?

Mas, e se pudéssemos construir nossa árvore de tal maneira que uma árvore de ter N elementos é uma coleção de [(N-1),N]. Então, não precisamos atravessar todos os indesejáveis(N-1)elementos Podemos atualizar diretamente o enésimo elemento. Este é exatamente o conceito do Zipper. Ele focaliza ou aponta para um local específico de uma árvore onde podemos atualizar esse valor sem percorrer toda a árvore.

No exemplo a seguir, implementamos o conceito de Zipper em uma lista. Da mesma forma, pode-se implementar o Zipper em umtree ou um file estrutura de dados.

data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
type Zipper_List a = ([a],[a])    

go_Forward :: Zipper_List a -> Zipper_List a   
go_Forward (x:xs, bs) = (xs, x:bs)   
   
go_Back :: Zipper_List a -> Zipper_List a   
go_Back (xs, b:bs) = (b:xs, bs)    

main = do 
   let list_Ex = [1,2,3,4] 
   print(go_Forward (list_Ex,[]))       
   print(go_Back([4],[3,2,1]))

Quando você compila e executa o programa acima, ele irá produzir a seguinte saída -

([2,3,4],[1]) 
([3,4],[2,1])

Aqui, estamos nos concentrando em um elemento de toda a corda enquanto avançamos ou voltamos.