Haskell - Guide rapide

Haskell est un langage de programmation fonctionnel spécialement conçu pour gérer les applications de calcul symbolique et de traitement de liste. La programmation fonctionnelle est basée sur des fonctions mathématiques. Outre Haskell, certains des autres langages populaires qui suivent le paradigme de la programmation fonctionnelle incluent: Lisp, Python, Erlang, Racket, F #, Clojure, etc.

Dans conventional programing, les instructions sont considérées comme un ensemble de déclarations dans une syntaxe ou un format spécifique, mais dans le cas de functional programing, tout le calcul est considéré comme une combinaison de fonctions mathématiques séparées.

Devenir fonctionnel avec Haskell

Haskell est un langage purement fonctionnel largement utilisé. Ici, nous avons énuméré quelques points qui rendent ce langage si spécial par rapport aux autres langages de programmation conventionnels tels que Java, C, C ++, PHP, etc.

  • Functional Language- En langage de programmation conventionnel, nous donnons au compilateur une série de tâches qui ne sont rien d'autre que de dire à votre ordinateur «quoi faire» et «comment faire? Mais dans Haskell, nous dirons à notre ordinateur "ce que c'est?"

  • Laziness- Haskell est une langue paresseuse. Parlazy, nous voulons dire que Haskell n'évaluera aucune expression sans aucune raison. Lorsque le moteur d'évaluation trouve qu'une expression doit être évaluée, il crée unthunk data structure pour collecter toutes les informations requises pour cette évaluation spécifique et un pointeur vers cela thunk data structure. Le moteur d'évaluation ne commencera à fonctionner que lorsqu'il sera nécessaire d'évaluer cette expression spécifique.

  • Modularity- Une application Haskell n'est rien d'autre qu'une série de fonctions. On peut dire qu'une application Haskell est une collection de nombreuses petites applications Haskell.

  • Statically Typed- Dans le langage de programmation conventionnel, nous devons définir une série de variables avec leur type. En revanche, Haskell est un langage strictement typé. Par le terme de langage Strictement Typé, nous entendons que le compilateur Haskell est suffisamment intelligent pour déterminer le type de la variable déclarée, nous n'avons donc pas besoin de mentionner explicitement le type de variable utilisée.

  • Maintainability - Les applications Haskell sont modulaires et, par conséquent, leur maintenance est très simple et économique.

Les programmes fonctionnels sont plus simultanés et ils suivent le parallélisme dans l'exécution pour fournir des performances plus précises et meilleures. Haskell ne fait pas exception; il a été développé de manière à gérermultithreading effectivement.

Bonjour le monde

C'est un exemple simple pour démontrer le dynamisme d'Haskell. Jetez un œil au code suivant. Tout ce dont nous avons besoin est une seule ligne pour imprimer "Hello Word" sur la console.

main = putStrLn "Hello World"

Une fois que le compilateur Haskell rencontre le morceau de code ci-dessus, il produit rapidement la sortie suivante -

Hello World

Nous fournirons de nombreux exemples tout au long de ce didacticiel pour présenter la puissance et la simplicité de Haskell.

Nous avons mis en place l'environnement de programmation Haskell en ligne sur - https://www.tutorialspoint.com/compile_haskell_online.php

Cet éditeur en ligne propose de nombreuses options pour pratiquer des exemples de programmation Haskell. Accédez à la section terminal de la page et saisissez"ghci". Cette commande charge automatiquement le compilateur Haskell et démarre Haskell en ligne. Vous recevrez la sortie suivante après avoir utilisé leghci commander.

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>

Si vous souhaitez toujours utiliser Haskell hors ligne dans votre système local, vous devez télécharger la configuration Haskell disponible à partir de sa page Web officielle - https://www.haskell.org/downloads

Il existe trois types différents de installers disponible sur le marché -

  • Minimal Installer - Il fournit des outils GHC (The Glasgow Haskell Compiler), CABAL (Common Architecture for Building Applications and Libraries) et Stack.

  • Stack Installer- Dans ce programme d'installation, le GHC peut être téléchargé dans une multiplateforme de chaîne de péage gérée. Il installera votre application dans le monde entier afin de pouvoir mettre à jour ses outils API chaque fois que nécessaire. Il résout automatiquement toutes les dépendances orientées Haskell.

  • Haskell Platform- C'est le meilleur moyen d'installer Haskell car il installera la plate-forme entière dans votre machine et cela à partir d'un emplacement spécifique. Ce programme d'installation n'est pas distributif comme les deux programmes d'installation ci-dessus.

Nous avons vu différents types d'installateurs disponibles sur le marché maintenant, voyons comment utiliser ces installateurs dans notre machine. Dans ce tutoriel, nous allons utiliser le programme d'installation de la plate-forme Haskell pour installer le compilateur Haskell dans notre système.

Configuration de l'environnement sous Windows

Pour configurer l'environnement Haskell sur votre ordinateur Windows, accédez à leur site officiel https://www.haskell.org/platform/windows.html et téléchargez l'installateur en fonction de votre architecture personnalisable.

Vérifiez l'architecture de votre système, téléchargez le fichier d'installation correspondant et exécutez-le. Il s'installera comme n'importe quelle autre application Windows. Vous devrez peut-être mettre à jour la configuration CABAL de votre système.

Configuration de l'environnement sous MAC

Pour configurer l'environnement Haskell sur votre système MAC, rendez-vous sur leur site officiel https://www.haskell.org/platform/mac.html et téléchargez le programme d'installation Mac.

Configuration de l'environnement sous Linux

L'installation de Haskell sur un système basé sur Linux nécessite d'exécuter une commande qui n'est pas aussi simple que MAC et Windows. Oui, c'est fatiguant mais c'est fiable.

Vous pouvez suivre les étapes ci-dessous pour installer Haskell sur votre système Linux -

Step 1 - Pour configurer l'environnement Haskell sur votre système Linux, rendez-vous sur le site officiel https://www.haskell.org/platform/linux.htmlet choisissez votre distribution. Vous trouverez l'écran suivant sur votre navigateur.

Step 2- Sélectionnez votre distribution. Dans notre cas, nous utilisons Ubuntu. Après avoir sélectionné cette option, vous obtiendrez la page suivante sur votre écran avec la commande pour installer le Haskell dans notre système local.

Step 3 - Ouvrez un terminal en appuyant sur Ctrl + Alt + T.Exécutez la commande "$ sudo apt-get install haskell-platform"et appuyez sur Entrée. Il commencera automatiquement à télécharger Haskell sur votre système après vous être authentifié avec le mot de passe root. Après l'installation, vous recevrez un message de confirmation.

Step 4- Accédez à nouveau à votre terminal et exécutez la commande GHCI. Une fois que vous obtenez l'invite Prelude, vous êtes prêt à utiliser Haskell sur votre système local.

Pour quitter le prologue GHCI, vous pouvez utiliser la commande ": quit exit".

Haskell est un langage de programmation purement fonctionnel, il est donc beaucoup plus interactif et intelligent que les autres langages de programmation. Dans ce chapitre, nous allons découvrir les modèles de données de base de Haskell qui sont en fait prédéfinis ou décodés intelligemment dans la mémoire de l'ordinateur.

Tout au long de ce tutoriel, nous utiliserons la plateforme en ligne Haskell disponible sur notre site Web (https://www.tutorialspoint.com/codingground.htm).

Nombres

Haskell est assez intelligent pour décoder un certain nombre sous forme de nombre. Par conséquent, vous n'avez pas besoin de mentionner son type en externe comme nous le faisons habituellement dans le cas d'autres langages de programmation. Comme par exemple, allez à votre invite de commande prélude et exécutez simplement "2 + 2" et appuyez sur Entrée.

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

En conséquence, vous recevrez la sortie suivante.

4

Dans le code ci-dessus, nous venons de passer deux nombres comme arguments au compilateur GHCI sans prédéfinir leur type, mais le compilateur pourrait facilement décoder ces deux entrées sous forme de nombres.

Maintenant, essayons un calcul mathématique un peu plus complexe et voyons si notre compilateur intelligent nous donne la sortie correcte ou non. Essayez avec "15+ (5 * 5) -40"

Prelude> 15+(5*5)-40

L'expression ci-dessus donne "0" selon la sortie attendue.

0

Personnages

Comme les nombres, Haskell peut identifier intelligemment un caractère donné en entrée. Accédez à votre invite de commande Haskell et tapez n'importe quel caractère avec une citation double ou simple.

Fournissons la ligne suivante comme entrée et vérifions sa sortie.

Prelude> :t "a"

Il produira la sortie suivante -

"a" :: [Char]

N'oubliez pas que vous utilisez (:t) tout en fournissant l'entrée. Dans l'exemple ci-dessus,(:t)consiste à inclure le type spécifique lié aux entrées. Nous en apprendrons plus sur ce type dans les prochains chapitres.

Jetez un œil à l'exemple suivant où nous transmettons une entrée non valide sous forme de caractère, ce qui à son tour entraîne une erreur.

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

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

Par le message d'erreur "<interactif>: 4: 1: Pas dans la portée:` a '", le compilateur Haskell nous avertit qu'il n'est pas capable de reconnaître votre entrée. Haskell est un type de langage où tout est représenté à l'aide d'un nombre.

Haskell suit le style d'encodage ASCII conventionnel. Jetons un coup d'œil à l'exemple suivant pour en comprendre plus -

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

Regardez comment votre entrée est décodée au format ASCII.

Chaîne

UNE stringn'est rien d'autre qu'une collection de personnages. Il n'y a pas de syntaxe spécifique pour l'utilisation de la chaîne, mais Haskell suit le style conventionnel de représentation d'une chaîne avec des guillemets doubles.

Jetez un œil à l'exemple suivant où nous passons la chaîne «Tutorialspoint.com».

Prelude> :t "tutorialspoint.com"

Il produira la sortie suivante à l'écran -

"tutorialspoint.com" :: [Char]

Voyez comment la chaîne entière a été décodée comme un tableau de Char uniquement. Passons à l'autre type de données et à sa syntaxe. Une fois que nous avons commencé notre pratique réelle, nous serons habitués à tous les types de données et à leur utilisation.

Booléen

Le type de données booléen est également assez simple comme les autres types de données. Regardez l'exemple suivant où nous utiliserons différentes opérations booléennes en utilisant des entrées booléennes telles que "True" ou "False".

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

Dans l'exemple ci-dessus, nous n'avons pas besoin de mentionner que "True" et "False" sont les valeurs booléennes. Haskell lui-même peut le décoder et effectuer les opérations respectives. Modifions nos entrées avec "vrai" ou "faux".

Prelude> true

Il produira la sortie suivante -

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

Dans l'exemple ci-dessus, Haskell ne pouvait pas faire la différence entre "vrai" et une valeur numérique, donc notre entrée "vrai" n'est pas un nombre. Par conséquent, le compilateur Haskell lève une erreur indiquant que notre entrée n'est pas sa portée.

Compréhension de listes et de listes

Comme d'autres types de données, Listest également un type de données très utile utilisé dans Haskell. Comme par exemple, [a, b, c] est une liste de caractères, donc, par définition, List est une collection du même type de données séparés par des virgules.

Comme d'autres types de données, vous n'avez pas besoin de déclarer une liste en tant que liste. Haskell est suffisamment intelligent pour décoder votre entrée en regardant la syntaxe utilisée dans l'expression.

Jetez un œil à l'exemple suivant qui montre comment Haskell traite une liste.

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

Il produira la sortie suivante -

[1,2,3,4,5]

Les listes dans Haskell sont de nature homogène, ce qui signifie qu'elles ne vous permettront pas de déclarer une liste de types de données différents. Toute liste comme [1,2,3,4,5, a, b, c, d, e, f] produira une erreur.

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

Ce code produira l'erreur suivante -

<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'

Compréhension de liste

La compréhension de liste est le processus de génération d'une liste à l'aide d'une expression mathématique. Regardez l'exemple suivant où nous générons une liste à l'aide d'une expression mathématique au format [sortie | plage, condition].

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]

Cette méthode de création d'une liste à l'aide d'une expression mathématique est appelée List Comprehension.

Tuple

Haskell fournit un autre moyen de déclarer plusieurs valeurs dans un seul type de données. Il est connu commeTuple. Un tuple peut être considéré comme une liste, mais il existe des différences techniques entre un tuple et une liste.

Un Tuple est un type de données immuable, car nous ne pouvons pas modifier le nombre d'éléments à l'exécution, alors qu'un List est un type de données mutable.

D'autre part, List est un type de données homogène, mais Tuple est de nature hétérogène, car un Tuple peut contenir différents types de données à l'intérieur.

Les tuples sont représentés par des parenthèses simples. Jetez un œil à l'exemple suivant pour voir comment Haskell traite un Tuple.

Prelude> (1,1,'a')

Il produira la sortie suivante -

(1,1,'a')

Dans l'exemple ci-dessus, nous avons utilisé un tuple avec deux number variables de type, et un char variable de type.

Dans ce chapitre, nous découvrirons les différents opérateurs utilisés dans Haskell. Comme d'autres langages de programmation, Haskell gère intelligemment certaines opérations de base comme l'addition, la soustraction, la multiplication, etc. Dans les prochains chapitres, nous en apprendrons davantage sur les différents opérateurs et leur utilisation.

Dans ce chapitre, nous utiliserons différents opérateurs dans Haskell en utilisant notre plateforme en ligne (https://www.tutorialspoint.com/codingground.htm). N'oubliez pas que nous n'utilisons queinteger taper des nombres car nous en apprendrons plus sur decimal tapez les numéros dans les chapitres suivants.

Opérateur d'addition

Comme son nom l'indique, l'opérateur d'addition (+) est utilisé pour la fonction d'addition. L'exemple de code suivant montre comment vous pouvez ajouter deux nombres entiers dans Haskell -

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

Dans le fichier ci-dessus, nous avons créé deux variables distinctes var1 et var2. À la fin, nous imprimons le résultat en utilisant leadditionopérateur. Utilisez lecompile et execute bouton pour exécuter votre code.

Ce code produira la sortie suivante à l'écran -

The addition of the two numbers is:
5

Opérateur de soustraction

Comme son nom l'indique, cet opérateur est utilisé pour les opérations de soustraction. L'exemple de code suivant montre comment vous pouvez soustraire deux nombres entiers dans Haskell -

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

Dans cet exemple, nous avons créé deux variables var1 et var2. Ensuite, nous utilisons l'opérateur de soustraction (-) pour soustraire les deux valeurs.

Ce code produira la sortie suivante à l'écran -

The Subtraction of the two numbers is:
4

Opérateur de multiplication

Cet opérateur est utilisé pour les opérations de multiplication. Le code suivant montre comment multiplier deux nombres dans Haskell à l'aide de l'opérateur de multiplication -

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

Ce code produira la sortie suivante, lorsque vous l'exécuterez sur notre plateforme en ligne -

The Multiplication of the Two Numbers is:
6

Opérateur de division

Jetez un œil au code suivant. Il montre comment vous pouvez diviser deux nombres en Haskell -

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

Il produira la sortie suivante -

The Division of the Two Numbers is: 
4.0

Opérateur de séquence / plage

Sequence or Range est un opérateur spécial dans Haskell. Il est désigné par "(..)". Vous pouvez utiliser cet opérateur lors de la déclaration d'une liste avec une séquence de valeurs.

Si vous souhaitez imprimer toutes les valeurs de 1 à 10, vous pouvez utiliser quelque chose comme "[1..10]". De même, si vous souhaitez générer tous les alphabets de "a" à "z", vous pouvez simplement taper"[a..z]".

Le code suivant montre comment vous pouvez utiliser l'opérateur de séquence pour imprimer toutes les valeurs de 1 à 10 -

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

Il générera la sortie suivante -

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

La prise de décision est une fonctionnalité qui permet aux programmeurs d'appliquer une condition dans le flux de code. Le programmeur peut exécuter un ensemble d'instructions en fonction d'une condition prédéfinie. L'organigramme suivant montre la structure décisionnelle de Haskell -

Haskell fournit les types d'énoncés décisionnels suivants -

Sr.No. Déclaration et description
1 instruction if – else

Une if déclaration avec un elsedéclaration. L'instruction dans leelse block ne s'exécutera que lorsque la condition booléenne donnée ne sera pas satisfaite.

2 Instruction if-else imbriquée

Plusieurs if blocs suivis de else blocs

Haskell est un langage fonctionnel et il est strictement typé, ce qui signifie que le type de données utilisé dans l'ensemble de l'application sera connu du compilateur au moment de la compilation.

Classe de type intégrée

Dans Haskell, chaque instruction est considérée comme une expression mathématique et la catégorie de cette expression est appelée Type. Vous pouvez dire que "Type" est le type de données de l'expression utilisée au moment de la compilation.

Pour en savoir plus sur le Type, nous utiliserons la commande ": t". De manière générique,Type peut être considéré comme une valeur, alors que Type Classpeut être considéré comme un ensemble de types similaires. Dans ce chapitre, nous allons découvrir les différents types intégrés.

Int

Intest une classe de type représentant les données de types Integer. Chaque nombre entier compris entre 2147483647 et -2147483647 relève duIntclasse de type. Dans l'exemple suivant, la fonctionfType() se comportera selon son type défini.

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

Ici, nous avons défini le type de la fonction fType() comme int. La fonction prend deuxint valeurs et en renvoie un intvaleur. Si vous compilez et exécutez ce morceau de code, il produira la sortie suivante -

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

Entier

Integer peut être considéré comme un sur-ensemble de Int. Cette valeur n'est limitée par aucun nombre, par conséquent un entier peut être de n'importe quelle longueur sans aucune limitation. Pour voir la différence fondamentale entreInt et Integer types, modifions le code ci-dessus comme suit -

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

Si vous compilez le morceau de code ci-dessus, le message d'erreur suivant sera généré -

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

Cette erreur s'est produite car notre fonction fType () attend une valeur de type Int et nous transmettons une vraie grande valeur de type Int. Pour éviter cette erreur, modifions le type "Int" par "Integer" et observons la différence.

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

Maintenant, il produira la sortie suivante -

sh-4.3$ main
1984297512562793395882644631364297686099210302577374055141

Flotte

Jetez un œil au morceau de code suivant. Il montre comment le type Float fonctionne dans Haskell -

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

La fonction prend deux valeurs flottantes comme entrée et renvoie une autre valeur flottante comme sortie. Lorsque vous compilez et exécutez ce code, il produira la sortie suivante -

sh-4.3$ main
20.689999

Double

Doubleest un nombre à virgule flottante avec une double précision à la fin. Jetez un œil à l'exemple suivant -

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

Lorsque vous exécutez le morceau de code ci-dessus, il générera la sortie suivante -

sh-4.3$ main 
21.0697

Booléen

Boolest un type booléen. Cela peut être vrai ou faux. Exécutez le code suivant pour comprendre comment le type Bool fonctionne dans Haskell -

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

Ici, nous définissons une variable «x» comme un Bool et la comparons à une autre valeur booléenne pour vérifier son originalité. Il produira la sortie suivante -

sh-4.3$ main
X is not a Bool Type

Carboniser

Les caractères représentent les caractères. Tout ce qui se trouve dans un guillemet simple est considéré comme un personnage. Dans le code suivant, nous avons modifié notre précédentfType() pour accepter la valeur Char et renvoyer la valeur Char en sortie.

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

Le morceau de code ci-dessus appellera fType() fonction avec un charvaleur de «v» mais renvoie une autre valeur de caractère, c'est-à-dire «K». Voici sa sortie -

sh-4.3$ main 
'K'

Notez que nous n'allons pas utiliser ces types explicitement car Haskell est suffisamment intelligent pour attraper le type avant qu'il ne soit déclaré. Dans les chapitres suivants de ce didacticiel, nous verrons comment différents types et classes de types font de Haskell un langage fortement typé.

Classe de type d'égaliseur

EQla classe type est une interface qui fournit la fonctionnalité pour tester l'égalité d'une expression. Toute classe Type qui souhaite vérifier l'égalité d'une expression doit faire partie de cette classe de type EQ.

Toutes les classes de type standard mentionnées ci-dessus en font partie EQclasse. Chaque fois que nous vérifions une égalité en utilisant l'un des types mentionnés ci-dessus, nous faisons en fait un appel àEQ classe de type.

Dans l'exemple suivant, nous utilisons le EQ Tapez en interne à l'aide de l'opération "==" ou "/ =".

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

Cela donnera la sortie suivante -

sh-4.3$ main 
The values are not Equal

Classe de type Ord

Ordest une autre classe d'interface qui nous donne la fonctionnalité de commande. Tous lestypes que nous avons utilisé jusqu'à présent en font partie Ordinterface. Comme l'interface EQ, l'interface Ord peut être appelée en utilisant ">", "<", "<=", "> =", "compare".

Veuillez trouver ci-dessous un exemple où nous avons utilisé la fonctionnalité de «comparaison» de cette classe de type.

main = print (4 <= 2)

Ici, le compilateur Haskell vérifiera si 4 est inférieur ou égal à 2. Comme ce n'est pas le cas, le code produira la sortie suivante -

sh-4.3$ main 
False

Spectacle

Showa une fonctionnalité pour imprimer son argument sous forme de chaîne. Quel que soit son argument, il imprime toujours le résultat sous forme de chaîne. Dans l'exemple suivant, nous imprimerons la liste entière en utilisant cette interface. "show" peut être utilisé pour appeler cette interface.

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

Il produira la sortie suivante sur la console. Ici, les guillemets doubles indiquent qu'il s'agit d'une valeur de type String.

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

Lis

ReadL'interface fait la même chose que Show, mais elle n'imprimera pas le résultat au format String. Dans le code suivant, nous avons utilisé leread interface pour lire une valeur de chaîne et la convertir en une valeur Int.

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

Ici, nous passons une variable String ("12") au readIntméthode qui à son tour renvoie 12 (une valeur Int) après la conversion. Voici sa sortie -

sh-4.3$ main 
12

Enum

Enumest un autre type de classe Type qui active la fonctionnalité séquentielle ou ordonnée dans Haskell. Cette classe Type est accessible par des commandes telles queSucc, Pred, Bool, Char, etc.

Le code suivant montre comment trouver la valeur successeur de 12.

main = print (succ 12)

Il produira la sortie suivante -

sh-4.3$ main
13

Délimité

Tous les types ayant des limites supérieures et inférieures relèvent de cette classe de types. Par exemple,Int les données de type ont une limite maximale de "9223372036854775807" et une limite minimale de "-9223372036854775808".

Le code suivant montre comment Haskell détermine la limite maximale et minimale du type Int.

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

Il produira la sortie suivante -

sh-4.3$ main
9223372036854775807
-9223372036854775808

Maintenant, essayez de trouver la limite maximale et minimale des types Char, Float et Bool.

Num

Cette classe de type est utilisée pour les opérations numériques. Les types tels que Int, Integer, Float et Double relèvent de cette classe Type. Jetez un œil au code suivant -

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

Il produira la sortie suivante -

sh-4.3$ main
2
2.0

Intégral

Integralpeut être considérée comme une sous-classe de la classe de type Num. La classe de type Num contient tous les types de nombres, tandis que la classe de type Integral est utilisée uniquement pour les nombres entiers. Int et Integer sont les types de cette classe Type.

Flottant

Comme Integral, Floating fait également partie de la classe Num Type, mais il ne contient que des nombres à virgule flottante. Par conséquent,Float et Double relèvent de cette classe de type.

Classe de type personnalisé

Comme tout autre langage de programmation, Haskell permet aux développeurs de définir des types définis par l'utilisateur. Dans l'exemple suivant, nous allons créer un type défini par l'utilisateur et l'utiliser.

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

Ici, nous avons créé un nouveau type appelé Area. Ensuite, nous utilisons ce type pour calculer l'aire d'un cercle. Dans l'exemple ci-dessus, "surface" est une fonction qui prendArea comme entrée et produit Float comme sortie.

Gardez à l'esprit que "data" est ici un mot-clé et que tous les types définis par l'utilisateur dans Haskell commencent toujours par une majuscule.

Il produira la sortie suivante -

sh-4.3$ main
314.15927

Les fonctions jouent un rôle majeur dans Haskell, car il s'agit d'un langage de programmation fonctionnel. Comme les autres langages, Haskell a sa propre définition fonctionnelle et sa propre déclaration.

  • La déclaration de fonction se compose du nom de la fonction et de sa liste d'arguments ainsi que de sa sortie.

  • La définition de fonction est l'endroit où vous définissez réellement une fonction.

Prenons un petit exemple de add fonction pour comprendre ce concept en détail.

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

Ici, nous avons déclaré notre fonction dans la première ligne et dans la deuxième ligne, nous avons écrit notre fonction réelle qui prendra deux arguments et produira une sortie de type entier.

Comme la plupart des autres langages, Haskell commence à compiler le code à partir du mainméthode. Notre code générera la sortie suivante -

The addition of the two numbers is:
7

Correspondance de motif

La correspondance de modèles est un processus de correspondance de types d'expressions spécifiques. Ce n'est rien d'autre qu'une technique pour simplifier votre code. Cette technique peut être implémentée dans n'importe quel type de classe Type. If-Else peut être utilisé comme une option alternative de correspondance de modèle.

Le Pattern Matching peut être considéré comme une variante du polymorphisme dynamique où, à l'exécution, différentes méthodes peuvent être exécutées en fonction de leur liste d'arguments.

Jetez un œil au bloc de code suivant. Ici, nous avons utilisé la technique du Pattern Matching pour calculer la factorielle d'un nombre.

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

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

Nous savons tous comment calculer la factorielle d'un nombre. Le compilateur commencera à rechercher une fonction appelée "fact" avec un argument. Si l'argument n'est pas égal à 0, alors le nombre continuera d'appeler la même fonction avec 1 de moins que celui de l'argument réel.

Lorsque le modèle de l'argument correspond exactement à 0, il appellera notre modèle qui est "fait 0 = 1". Notre code produira la sortie suivante -

The factorial of 5 is:
120

Gardes

Guardsest un concept très similaire à la correspondance de modèles. Dans la correspondance de modèles, nous faisons généralement correspondre une ou plusieurs expressions, mais nous utilisonsguards pour tester une propriété d'une expression.

Bien qu'il soit conseillé d'utiliser la correspondance de motifs sur guards, mais du point de vue du développeur, guardsest plus lisible et simple. Pour les nouveaux utilisateurs,guards peuvent ressembler beaucoup aux instructions If-Else, mais elles sont fonctionnellement différentes.

Dans le code suivant, nous avons modifié notre factorial programme en utilisant le concept 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)

Ici, nous avons déclaré deux guards, séparés par "|" et appeler lefact fonction de main. En interne, le compilateur fonctionnera de la même manière que dans le cas de la correspondance de modèles pour produire la sortie suivante -

The factorial of 5 is:
120

Clause Où

Whereest un mot-clé ou une fonction intégrée qui peut être utilisée lors de l'exécution pour générer une sortie souhaitée. Cela peut être très utile lorsque le calcul de fonction devient complexe.

Considérez un scénario dans lequel votre entrée est une expression complexe avec plusieurs paramètres. Dans de tels cas, vous pouvez diviser l'expression entière en petites parties à l'aide de la clause "where".

Dans l'exemple suivant, nous prenons une expression mathématique complexe. Nous montrerons comment vous pouvez trouver les racines d'une équation polynomiale [x ^ 2 - 8x + 6] en utilisant 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))

Notez la complexité de notre expression pour calculer les racines de la fonction polynomiale donnée. C'est assez complexe. Par conséquent, nous cassons l'expression en utilisant lewhereclause. Le morceau de code ci-dessus générera la sortie suivante -

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

Fonction de récursivité

La récursivité est une situation dans laquelle une fonction s'appelle elle-même à plusieurs reprises. Haskell ne fournit aucune possibilité de boucler une expression plus d'une fois. Au lieu de cela, Haskell souhaite que vous divisiez l'ensemble de vos fonctionnalités en un ensemble de fonctions différentes et que vous utilisiez une technique de récursivité pour implémenter votre fonctionnalité.

Considérons à nouveau notre exemple de correspondance de motifs, où nous avons calculé la factorielle d'un nombre. Trouver la factorielle d'un nombre est un cas classique d'utilisation de la récursivité. Ici, vous pourriez, "En quoi la correspondance de modèle est-elle différente de la récursivité?" La différence entre ces deux réside dans la manière dont ils sont utilisés: la correspondance de motifs fonctionne sur la configuration de la contrainte de terminal, tandis que la récursivité est un appel de fonction.

Dans l'exemple suivant, nous avons utilisé à la fois la correspondance de modèle et la récursivité pour calculer la factorielle 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)

Il produira la sortie suivante -

The factorial of 5 is:
120

Fonction d'ordre supérieur

Jusqu'à présent, ce que nous avons vu, c'est que les fonctions Haskell en prennent une type comme intrant et produire un autre typeen sortie, ce qui est assez similaire dans d'autres langages impératifs. Les fonctions d'ordre supérieur sont une fonctionnalité unique de Haskell où vous pouvez utiliser une fonction comme argument d'entrée ou de sortie.

Bien que ce soit un concept virtuel, mais dans les programmes du monde réel, chaque fonction que nous définissons dans Haskell utilise un mécanisme d'ordre supérieur pour fournir une sortie. Si vous avez une chance de regarder dans la fonction de bibliothèque de Haskell, alors vous constaterez que la plupart des fonctions de bibliothèque ont été écrites dans un ordre supérieur.

Prenons un exemple où nous allons importer une carte de fonctions d'ordre supérieur intégrée et l'utiliser pour implémenter une autre fonction d'ordre supérieur selon notre choix.

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"

Dans l'exemple ci-dessus, nous avons utilisé le toUpper fonction de la classe de type Charpour convertir notre entrée en majuscules. Ici, la méthode "map" prend une fonction comme argument et renvoie la sortie requise. Voici sa sortie -

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

Expression Lambda

Nous devons parfois écrire une fonction qui ne sera utilisée qu'une seule fois, pendant toute la durée de vie d'une application. Pour faire face à ce genre de situation, les développeurs Haskell utilisent un autre bloc anonyme appelélambda expression ou lambda function.

Une fonction sans définition est appelée une fonction lambda. Une fonction lambda est désignée par le caractère "\". Prenons l'exemple suivant où nous allons augmenter la valeur d'entrée de 1 sans créer de fonction.

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

Ici, nous avons créé une fonction anonyme qui n'a pas de nom. Il prend l'entier 4 comme argument et imprime la valeur de sortie. Nous exploitons essentiellement une fonction sans même la déclarer correctement. C'est la beauté des expressions lambda.

Notre expression lambda produira la sortie suivante -

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

Jusqu'à présent, nous avons discuté de nombreux types de fonctions Haskell et utilisé différentes manières d'appeler ces fonctions. Dans ce chapitre, nous allons découvrir quelques fonctions de base qui peuvent être facilement utilisées dans Haskell sans importer de classe Type spéciale. La plupart de ces fonctions font partie d'autres fonctions d'ordre supérieur.

Fonction de la tête

HeadLa fonction fonctionne sur une liste. Il renvoie le premier argument d'entrée qui est essentiellement une liste. Dans l'exemple suivant, nous passons une liste avec 10 valeurs et nous générons le premier élément de cette liste en utilisant lehead fonction.

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

Il produira la sortie suivante -

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

Fonction de queue

Tail est la fonction qui complète le headfonction. Il faut unlistcomme entrée et donne la liste entière sans la partie de tête. Cela signifie que letailLa fonction renvoie la liste entière sans le premier élément. Jetez un œil à l'exemple suivant -

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

Il produira la sortie suivante -

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]

Dernière fonction

Comme son nom l'indique, il donne le dernier élément de la liste qui est fourni en entrée. Vérifiez l'exemple suivant.

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

Il produira la sortie suivante -

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

Fonction Init

Init fonctionne exactement comme l'opposé de tailfonction. Il prend une liste comme argument et renvoie la liste entière sans la dernière entrée.

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

Maintenant, observez sa sortie -

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]

Fonction nulle

Null est une fonction de vérification booléenne qui fonctionne sur une chaîne et retourne True uniquement lorsque la liste donnée est vide, sinon elle retourne False. Le code suivant vérifie si la liste fournie est vide ou non.

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

Il produira la sortie suivante -

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

Fonction inverse

Il fonctionne sur une entrée String et convertit toute l'entrée en ordre inverse et donne une sortie en conséquence. Voici la base de code pour cette fonction.

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

Il produira la sortie suivante -

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]

Fonction de longueur

Cette fonction permet de calculer la longueur du listdonné en argument. Jetez un œil à l'exemple suivant -

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

Nous avons 10 éléments dans notre liste, donc notre code donnera 10 en sortie.

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

Prendre la fonction

TakeLa fonction est utilisée pour créer une sous-chaîne à partir d'une autre chaîne. Le code suivant montre comment utiliser la fonction take dans Haskell -

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

Le code génère une sous-chaîne contenant 5 éléments de la liste fournie -

[1,2,3,4,5]

Fonction Drop

Cette fonction est également utilisée pour générer une sous-chaîne. Il fonctionne comme l'opposé de latakefonction. Regardez le morceau de code suivant -

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

Le code supprime les 5 premiers éléments de la liste fournie et imprime les 5 éléments restants. Il produira la sortie suivante -

[6,7,8,9,10]

Fonction maximale

Cette fonction permet de trouver l'élément avec la valeur maximale dans la liste fournie. Voyons comment l'utiliser dans la pratique -

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

Le morceau de code ci-dessus générera la sortie suivante -

The maximum value element of the list is:
1245

Fonction minimale

Cette fonction est utilisée pour trouver l'élément avec la valeur minimale de la liste fournie. C'est juste l'opposé de lamaximum fonction.

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

La sortie du code ci-dessus est -

The minimum value element of the list is:
1

Fonction de somme

Comme son nom l'indique, cette fonction renvoie la somme de tous les éléments présents dans la liste fournie. Le code suivant prend une liste de 5 éléments et renvoie leur somme en sortie.

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

Il produira la sortie suivante -

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

Fonction du produit

Vous pouvez utiliser cette fonction pour multiplier tous les éléments d'une liste et imprimer sa valeur.

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

Notre code produira la sortie suivante -

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

Fonction Elem

Cette fonction permet de vérifier si la liste fournie contient un élément spécifique ou non. En conséquence, il renvoie soit untrue ou un false.

Le code suivant vérifie si la liste d'éléments fournie contient la valeur 786.

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

Il produira la sortie suivante -

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

Utilisez le même code pour vérifier si la liste fournie contient la valeur 1785 ou non.

Function Compositionest le processus d'utilisation de la sortie d'une fonction comme entrée d'une autre fonction. Ce sera mieux si nous apprenons les mathématiques derrièrecomposition. En mathématiques,composition est désigné par f{g(x)}g() est une fonction et sa sortie est utilisée comme entrée d'une autre fonction, c'est-à-dire f().

La composition de fonction peut être implémentée à l'aide de deux fonctions quelconques, à condition que le type de sortie d'une fonction corresponde au type d'entrée de la seconde fonction. Nous utilisons l'opérateur point (.) Pour implémenter la composition de fonctions dans Haskell.

Jetez un œil à l'exemple de code suivant. Ici, nous avons utilisé la composition de fonctions pour calculer si un nombre d'entrée est pair ou impair.

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))

Ici, dans le main fonction, nous appelons deux fonctions, noto et eveno, simultanément. Le compilateur appellera d'abord la fonction"eveno()" avec 16comme argument. Par la suite, le compilateur utilisera la sortie dueveno méthode comme entrée de noto() méthode.

Sa sortie serait la suivante -

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

Puisque nous fournissons le nombre 16 comme entrée (qui est un nombre pair), le eveno() retourne la fonction true, qui devient l'entrée pour le noto() et renvoie la sortie: "Ceci est un nombre pair".

Si vous avez travaillé sur Java, vous saurez comment toutes les classes sont liées dans un dossier appelé package. De même, Haskell peut être considéré comme une collection demodules.

Haskell est un langage fonctionnel et tout est désigné comme une expression, par conséquent un module peut être appelé comme une collection de types de fonctions similaires ou liés.

Vous pouvez importune fonction d'un module vers un autre module. Toutes les instructions "import" doivent venir en premier avant de commencer à définir d'autres fonctions. Dans ce chapitre, nous allons apprendre les différentes fonctionnalités des modules Haskell.

Module de liste

List fournit de merveilleuses fonctions avec lesquelles travailler listtaper des données. Une fois que vous avez importé le module Liste, vous disposez d'un large éventail de fonctions.

Dans l'exemple suivant, nous avons utilisé certaines fonctions importantes disponibles dans le module Liste.

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])

Ici, nous avons de nombreuses fonctions sans même les définir. En effet, ces fonctions sont disponibles dans le module Liste. Après avoir importé le module List, le compilateur Haskell a rendu toutes ces fonctions disponibles dans l'espace de noms global. Par conséquent, nous pourrions utiliser ces fonctions.

Notre code donnera la sortie suivante -

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]

Module de caractères

le CharLe module a de nombreuses fonctions prédéfinies pour fonctionner avec le type Character. Jetez un œil au bloc de code suivant -

import Data.Char 

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

Ici, les fonctions toUpper et toLower sont déjà définis dans le Charmodule. Il produira la sortie suivante -

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

Module de carte

Mapest un type de données de type paire à valeur ajoutée non triée. C'est un module largement utilisé avec de nombreuses fonctions utiles. L'exemple suivant montre comment utiliser une fonction prédéfinie disponible dans le module Carte.

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)

Il produira la sortie suivante -

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

Définir le module

Le module Set a des fonctions prédéfinies très utiles pour manipuler des données mathématiques. Un ensemble est implémenté sous la forme d'un arbre binaire, de sorte que tous les éléments d'un ensemble doivent être uniques.

Jetez un œil à l'exemple de code suivant

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)

Ici, nous modifions une chaîne en un ensemble. Il produira la sortie suivante. Observez que l'ensemble de sortie n'a pas de répétition de caractères.

fromList " Hbdeuy"
fromList " HTaefhiklorstu"

Module personnalisé

Voyons comment nous pouvons créer un module personnalisé qui peut être appelé dans d'autres programmes. Pour implémenter ce module personnalisé, nous allons créer un fichier séparé appelé"custom.hs" avec notre "main.hs".

Créons le module personnalisé et définissons-y quelques fonctions.

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

Notre module personnalisé est prêt. Maintenant, importons-le dans un programme.

main.hs

import Custom 

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

Notre code générera la sortie suivante -

True
1

le showEven retourne la fonction True, car "4" est un nombre pair. leshowBoolean function renvoie "1" car la fonction booléenne que nous avons passée dans la fonction est "True".

Tous les exemples dont nous avons discuté jusqu'à présent sont de nature statique. Dans ce chapitre, nous allons apprendre à communiquer de manière dynamique avec les utilisateurs. Nous allons apprendre différentes techniques d'entrée et de sortie utilisées dans Haskell.

Fichiers et flux

Nous avons jusqu'à présent codé en dur toutes les entrées dans le programme lui-même. Nous avons pris des entrées de variables statiques. Maintenant, apprenons à lire et à écrire à partir d'un fichier externe.

Créons un fichier et nommez-le "abc.txt". Ensuite, entrez les lignes suivantes dans ce fichier texte: "Bienvenue dans Tutorialspoint. Ici, vous obtiendrez la meilleure ressource pour apprendre Haskell."

Ensuite, nous écrirons le code suivant qui affichera le contenu de ce fichier sur la console. Ici, nous utilisons la fonction readFile () qui lit un fichier jusqu'à ce qu'il trouve un caractère EOF.

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

Le morceau de code ci-dessus lira le fichier "abc.txt" comme une chaîne jusqu'à ce qu'il rencontre un caractère de fin de fichier. Ce morceau de code générera la sortie suivante.

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

Observez que tout ce qu'il imprime sur le terminal est écrit dans ce fichier.

Argument de ligne de commande

Haskell fournit également la possibilité d'exploiter un fichier via l'invite de commande. Revenons à notre terminal et tapez"ghci". Ensuite, tapez l'ensemble de commandes suivant -

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

Ici, nous avons créé un fichier texte appelé "abc.txt". Ensuite, nous avons inséré une instruction dans le fichier à l'aide de la commandewriteFile. Enfin, nous avons utilisé la commandereadFilepour imprimer le contenu du fichier sur la console. Notre code produira la sortie suivante -

I am just experimenting here.

Exceptions

Un exceptionpeut être considéré comme un bogue dans le code. Il s'agit d'une situation où le compilateur n'obtient pas la sortie attendue au moment de l'exécution. Comme tout autre bon langage de programmation, Haskell fournit un moyen d'implémenter la gestion des exceptions.

Si vous êtes familier avec Java, vous connaissez peut-être le bloc Try-Catch où nous lançons généralement une erreur et la détectons dans le catchbloquer. Dans Haskell, nous avons également la même fonction pour détecter les erreurs d'exécution.

La définition de fonction de tryressemble à "try :: Exception e => IO a -> IO (Either ea)". Jetez un œil à l'exemple de code suivant. Il montre comment vous pouvez intercepter l'exception "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

Dans l'exemple ci-dessus, nous avons utilisé la fonction intégrée try fonction de la Control.Exceptionmodule, par conséquent, nous détectons l'exception au préalable. Le morceau de code ci-dessus donnera la sortie ci-dessous à l'écran.

Caught exception: divide by zero

Functordans Haskell est une sorte de représentation fonctionnelle de différents types qui peuvent être mappés. C'est un concept de haut niveau de mise en œuvre du polymorphisme. Selon les développeurs Haskell, tous les types tels que List, Map, Tree, etc. sont l'instance du Haskell Functor.

UNE Functor est une classe intégrée avec une définition de fonction comme -

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

Par cette définition, nous pouvons conclure que le Functor est une fonction qui prend une fonction, disons, fmap()et renvoie une autre fonction. Dans l'exemple ci-dessus,fmap() est une représentation généralisée de la fonction map().

Dans l'exemple suivant, nous verrons comment fonctionne Haskell Functor.

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

Ici, nous avons utilisé les deux map() et fmap()sur une liste pour une opération de soustraction. Vous pouvez observer que les deux instructions donneront le même résultat d'une liste contenant les éléments [1,3,7,15].

Les deux fonctions appelées une autre fonction appelée subtract() pour donner le résultat.

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

Alors, quelle est la différence entre map et fmap? La différence réside dans leur utilisation. Functor nous permet d'implémenter plus de fonctionnalistes dans différents types de données, comme «juste» et «rien».

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

Le morceau de code ci-dessus donnera la sortie suivante sur le terminal -

Just 17
Nothing

Fonction applicative

Un foncteur applicatif est un foncteur normal avec quelques fonctionnalités supplémentaires fournies par la classe de type applicatif.

En utilisant Functor, nous mappons généralement une fonction existante avec une autre fonction définie à l'intérieur. Mais il n'y a aucun moyen de mapper une fonction qui est définie à l'intérieur d'un Functor avec un autre Functor. C'est pourquoi nous avons une autre installation appeléeApplicative Functor. Cette fonction de mappage est implémentée par la classe Applicative Type définie sous leControlmodule. Cette classe ne nous donne que deux méthodes pour travailler: l'une estpure et l'autre est <*>.

Voici la définition de classe du Functor Applicatif.

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

Selon l'implémentation, nous pouvons mapper un autre Functor en utilisant deux méthodes: "Pure" et "<*>". La méthode "Pure" doit prendre une valeur de n'importe quel type et elle retournera toujours un Functor Applicatif de cette valeur.

L'exemple suivant montre comment fonctionne un fonction applicative -

import Control.Applicative 

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

Ici, nous avons implémenté des foncteurs applicatifs dans l'appel de fonction de la fonction f1. Notre programme produira le résultat suivant.

"Just 4"

Monoïdes

Nous savons tous que Haskell définit tout sous forme de fonctions. Dans les fonctions, nous avons des options pour obtenir notre entrée en tant que sortie de la fonction. C'est ce qu'unMonoid est.

UNE Monoidest un ensemble de fonctions et d'opérateurs où la sortie est indépendante de son entrée. Prenons une fonction (*) et un entier (1). Maintenant, quelle que soit l'entrée, sa sortie ne restera que le même numéro. Autrement dit, si vous multipliez un nombre par 1, vous obtiendrez le même nombre.

Voici une définition de classe de type de monoïde.

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

Jetez un œil à l'exemple suivant pour comprendre l'utilisation de Monoid dans Haskell.

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

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

Notre code produira la sortie suivante -

9
7

Ici, la fonction "multi" multiplie l'entrée par "1". De même, la fonction "ajouter" ajoute l'entrée avec "0". Dans les deux cas, la sortie sera la même que l'entrée. Par conséquent, les fonctions{(*),1} et {(+),0} sont les parfaits exemples de monoïdes.

Monadsne sont rien d'autre qu'un type de fonction applicative avec quelques fonctionnalités supplémentaires. C'est une classe Type qui régit trois règles de base appeléesmonadic rules.

Les trois règles sont strictement applicables sur une déclaration Monad qui est la suivante -

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

Les trois lois de base applicables à une déclaration de la Monade sont:

  • Left Identity Law - Le returnne modifie pas la valeur et ne devrait rien changer dans la Monad. Il peut être exprimé par "return> => mf = mf".

  • Right Identity Law - Le returnne modifie pas la valeur et ne devrait rien changer dans la Monad. Il peut être exprimé par "mf> => return = mf".

  • Associativity- Selon cette loi, les Functors et l'instance Monad devraient fonctionner de la même manière. Il peut être mathématiquement exprimé par "(f> ==> g)> => h = f> => (g> = h)".

Les deux premières lois itèrent le même point, c'est-à-dire une return devrait avoir un comportement identitaire des deux côtés de la bind opérateur.

Nous avons déjà utilisé beaucoup de Monades dans nos exemples précédents sans nous rendre compte qu'il s'agit de Monades. Prenons l'exemple suivant où nous utilisons une monade de liste pour générer une liste spécifique.

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

Ce code produira la sortie suivante -

[2,6,10,14,18]

Zippers dans Haskell sont essentiellement des pointeurs qui pointent vers un emplacement spécifique d'une structure de données telle qu'un tree.

Considérons un tree ayant 5 éléments [45,7,55,120,56]qui peut être représenté comme un arbre binaire parfait. Si je veux mettre à jour le dernier élément de cette liste, alors je dois parcourir tous les éléments pour atteindre le dernier élément avant de le mettre à jour. Droite?

Mais que se passerait-il si nous pouvions construire notre arbre de telle manière qu'un arbre N elements est une collection de [(N-1),N]. Ensuite, nous n'avons pas besoin de traverser tous les indésirables(N-1)éléments. Nous pouvons directement mettre à jour le Nième élément. C'est exactement le concept de Zipper. Il se concentre ou pointe vers un emplacement spécifique d'un arbre où nous pouvons mettre à jour cette valeur sans parcourir l'arbre entier.

Dans l'exemple suivant, nous avons implémenté le concept de fermeture éclair dans une liste. De la même manière, on peut implémenter Zipper dans untree ou un file Structure de données.

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]))

Lorsque vous compilez et exécutez le programme ci-dessus, il produira la sortie suivante -

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

Ici, nous nous concentrons sur un élément de la chaîne entière en avançant ou en reculant.