|Haskell] Type dans signature de fonction

a marqué ce sujet comme résolu.

Bonjour,

je débute en Hasell et il y a un truc que je ne comprends pas.

Pourquoi dans la signature de la fonction length :

1
2
Prelude> :t length 
length :: Foldable t => t a -> Int

il y a un paramètre "t" et un paramètre "a" alors que la fonction prend qu’un paramètre? Est-ce que ça ne serait pas correcte comme ceci :

1
2
Prelude> :t length 
length :: Foldable t => t -> Int

Merci

+0 -0

Salut,

Je ne connais pas l’haskell, mais il semble que t soit un type générique (un trait ?) et a un paramètre de type  : cela stipulerait donc que la fonction length prend un unique paramètre, une séquence de type t composée d’objets de type a.

Salut Freeza,

Ce que entwanne a dit est correct. Juste pour rappel, la signature d’une fonction comporte 3 éléments: nom :: contexte => type . Le contexte (ou les contraintes) donne des informations sur les types présents dans la signature. Ici on apprend donc que t doit obligatoirement appartenir à la classe de type Foldable. Foldable représente des structures de données sur lesquelles on peut itérer (c’est-à-dire utiliser la famille de fonctions fold*), comme par exemple les listes ou bien les arbres. Si tu es débutant en Haskell je te conseillerais de ne pas trop t’arrêter sur la définition de la typeclass Foldable (que tu peux trouver ici), tu verras les folds et les typeclass plus en détail par la suite.

Pour ce qui est de ta question à proprement parler, la réponse est que t est un type paramétré, c’est-à-dire un type dont le constructeur prend lui-même d’autres types en argument. On peut prendre l’exemple des listes, voici comment on peut les définir en Haskell:

1
2
3
data List a = Cons a (List a)
            | Nil
            deriving (Show) -- `deriving` permet d'indiquer la typeclass

Notre liste ici est donc paramétrée par un autre type, on peut ainsi avoir des listes d’entiers, des listes de caractères, etc. Les listes étant extrêmement utilisées en Haskell, on a du sucre syntaxique pour pouvoir alléger le code: List a devient simplement [a], Cons est l’opérateur (:) et Nil est noté [].

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
foo :: List Int
foo = Cons 1 (Cons 2 (Cons 3 Nil))

foo_ :: [Int]             -- equivalent a
foo_ = 1 : (2 : (3 : [])) -- foo_ = [1,2,3]

bar :: List Char
bar = Cons 'h' (Cons 'e' (Cons 'l' (Cons 'l' (Cons 'o' Nil))))

bar_ :: [Char]                                  -- bar_ = ['H','e','l','l','o'] 
bar_ = 'H' : ('e' : ('l' : ('l' : ('o' : [])))) -- ou bar_ = "Hello"

Pour résumer, la signature de la fonction length est bien correcte. length opère sur un argument qui est obligatoirement un Foldable (t) et qui peut lui contenir n’importe quel type (a). Si tu veux définir une fonction my_length qui travaillerait uniquement sur des listes d’entiers, tu t’y prendrais comme ceci par exemple en le spécifiant dans la signature:

1
2
3
4
5
my_length :: [Int] -> Int
my_length list = length list

foo = my_length [1, 2, 3] -- ==> 3
bar = my_length "Hello"   -- ==> erreur de typage ([Char] au lieu de [Int])
+2 -0

Hello, merci pour vos réponses.

Enfaite je me suis mal exprimé, je me doute bien que la signature est correcte, je voulais savoir si on pouvait l’écrire comme je l’ai fait. :D

Enfaite, on peut comparer Foldable comme une classe générique en java n’est-ce pas? Donc il faut lui passer un type en paramètre, en locurence ici, peu importe le type. Donc si je voulais écrire cette méthode pour un Foldable de Int par exemple, je devrait faire :

1
length :: (Foldable t, Int a) => t a -> Int

?

Salut,

C’est une affaire de sorte (kind en anglais). C’est un peu comme les types des types. En Haskell les types ont donc eux mêmes des signatures. Par exemple, Int :: ** désigne un type sans argument. Pour un type avec arguments, comme Maybe, on aura comme sorte * -> *, que l’on peut interpréter comme une fonction de type qui à un type associe un type. Les classes de Haskell comme Foldable correspondent comme tu l’as dit à un équivalent des interfaces en Java ou les traits en Rust. Foldable a pour kind (* -> *) -> Constraint, donc quand tu écris Foldable t => ..., t est inféré comme * -> *. Ici, on peut comprendre que t est une sorte de conteneur, donc si ton conteneur contient des Int tu écriras t Int. Ici le type contenu n’intervient pas dans le code de length donc tu peux simplement écrire t a.

1
length :: (Foldable t, Int a) => t a -> Int
Freeza

Attention, Int est un type, pas une classe. Ceci ne devrait pas compiler, puisque Int est inféré à * -> Constraint alors qu’il est de sorte *. À la rigueur tu pourrais écrire length :: Foldable t => t Int -> Int mais cela restreindrait ta fonction à un conteneur d’Int ce que tu ne souhaites pas.

Pour comprendre les kinds:

+3 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte