2016-10-23 3 views
3

J'essaie de faire quelque chose de très simple dans le cadre d'un devoir. Tout ce que je dois faire est d'écrire une fonction qui prend dans une liste de 2-tuples de nombres représentant les longueurs de base et de hauteur pour les triangles, et retourne une liste des zones correspondant à ces triangles. L'une des exigences est que je le fasse en définissant une fonction et en déclarant son type dans une clause where. Tout ce que j'ai essayé jusqu'à présent ne compiler, voici ce que j'ai:Type Spécification dans une clause Where

calcTriangleAreas xs = [triArea x | x<-xs] 
    where triArea:: (Num, Num) -> Num --this uses 4 preceding spaces 
triArea (base, height) = base*height/2 

Cela échoue avec l'erreur The type signature for ‘triArea’ lacks an accompanying binding, qui me ressemble triArea n'est pas défini à l'intérieur de la clause where. D'accord, donc nous allons indentera pour correspondre à la where:

calcTriangleAreas xs = [triArea x | x<-xs] 
    where triArea:: (Num, Num) -> Num --this uses 4 preceding spaces 
    triArea (base, height) = base*height/2 --... and so does this 

Celui-ci ne parvient pas à compiler le message d'erreur particulièrement uninformative parse error on input triArea. Juste pour le plaisir, nous allons essayer indenter un peu plus, parce que idk quoi faire d'autre:

calcTriangleAreas xs = [triArea x | x<-xs] 
    where triArea:: (Num, Num) -> Num --this uses 4 preceding spaces 
     triArea (base, height) = base*height/2 --this has 8 

mais pas de dés, échoue avec le même message parse error. J'ai essayé de remplacer l'espacement dans chacun d'entre eux avec des onglets équivalents, à 4 espaces, mais cela n'a pas aidé. Les deux premiers produisent les mêmes erreurs avec des onglets que des espaces, mais le dernier, ici:

calcTriangleAreas xs = [triArea x | x<-xs] 
    where triArea:: (Num, Num) -> Num --this uses a preceding tab character 
     triArea (base, height) = base*height/2 --this has 2 

donne le message d'erreur

Illegal type signature: ‘(Num, Num) -> Num triArea (base, height)’ 
    Perhaps you intended to use ScopedTypeVariables 
In a pattern type-signature 

et je ne sais pas ce qui essaie de dire, mais il semble ignorer les sauts de ligne tout à coup. Je lisais "Learn You a Haskell", et je suis censé pouvoir le faire avec les informations présentées dans les trois premiers chapitres, mais je les ai parcourus et ils ne spécifient jamais le type d'un défini fonctionné. dans une clause where dans ces chapitres. Pour mémoire, leurs exemples semblent être irrévérencieux de l'espacement, et j'ai copié le style d'un d'entre eux:

calcTriangleAreas xs = [triArea x | x<-xs] 
    where triArea:: (Num, Num) -> Num --4 preceding spaces 
      triArea (base, height) = base*height/2 --10 preceding spaces 

mais a également échoué à compiler, crachant le message d'erreur tout à fait incompréhensible:

Expecting one more argument to ‘Num’ 
    The first argument of a tuple should have kind ‘*’, 
     but ‘Num’ has kind ‘* -> GHC.Prim.Constraint’ 
    In the type signature for ‘triArea’: triArea :: (Num, Num) -> Num 
    In an equation for ‘calcTriangleAreas’: 
     calcTriangleAreas xs 
      = [triArea x | x <- xs] 
      where 
       triArea :: (Num, Num) -> Num 
       triArea (base, height) = base * height/2 

Je ne trouve rien quand je google/hoogle, et j'ai regardé this question, mais non seulement il montre beaucoup trop de haskell avancé pour moi de lire, mais basé sur le contenu je ne crois pas qu'ils Avoir le même problème que moi. J'ai essayé de spécifier le type de calcTriangleAreas, et j'ai essayé d'aliaser les types dans la spécification pour triArea pour être Floating et franchement je suis au bout de ma corde. La première ligne de mon fichier est module ChapterThree where, mais au-delà, le code que j'ai montré dans chaque exemple est le fichier entier. Je travaille sur Linux Mint 18 32 bits, et je compile avec ghc ChapterThree.hs Chapter3UnitTests.hs -o Test, où ChapterThree.hs est mon fichier et les tests unitaires sont donnés par mon professeur, donc je peux facilement dire si mon programme fonctionne (Il n'arrive jamais à l'étape de compilation pour ChapterThreeUnitTests.hs, donc je ne pensais pas que le contenu serait important), et ma version ghc est 7.10.3.

EDIT: Notez que si je supprime simplement la spécification de type, tout compile bien, et cette fonction passe tous ses tests unitaires associés.

S'il vous plaît, sauvez-moi de ma folie.

Répondre

6

Votre dernier exemple est correct, mais le type que vous avez écrit n'a pas de sens. Num est une contrainte de classe n'est pas un type. Vous avez probablement voulu écrire:

calcTriangleAreas xs = [triArea x | x<-xs] 
    where triArea:: Num a => (a, a) -> a 
      triArea (base, height) = base*height/2 

La règle est: les affectations doivent être alignées.

De plus (/) exige la classe Fractional:

calcTriangleAreas xs = [triArea x | x<-xs] 
    where triArea:: Fractional a => (a, a) -> a 
      triArea (base, height) = base*height/2 

Notez que le niveau de retrait est ne aucun lien de parenté avec le niveau de retrait du where. Par exemple, vous pouvez écrire que le code de cette façon:

calcTriangleAreas xs = [triArea x | x<-xs] where 
    triArea:: Fractional a => (a, a) -> a 
    triArea (base, height) = base*height/2 

Le niveau de retrait est défini par la première affectation dans un where/let ou la première ligne d'un bloc do. Toutes les autres lignes doivent s'aligner sur celle-là.

Donc tous ces éléments sont corrects:

f x = y where 
    a = b 
    y = ... 

f x = y 
    where a = b 
     y = ... 

f x = y 
    where 
    a = b 
    y = ... 
+0

Les onglets/espaces ne sont-ils pas importants? Aussi: maaaan, j'ai essayé ça mais avec "Floating" au lieu de "Fractional" et ça m'a lancé. Quoi qu'il en soit, merci pour la réponse rapide. – bfieck

+5

@bfieck s'il vous plaît ne pas utiliser les onglets. Utilisez deux espaces par indentation. Tout le monde sera plus heureux avec vous :-) – Yawar

+1

@bfieck Les tabulations ne sont pas un bon moyen de mettre du code en retrait pour les langages sensibles à l'indentation. Il y a * beaucoup * de personnes qui ont des problèmes avec certaines erreurs à cause de cela parce que le mélange des onglets et des espaces lors de l'utilisation de l'indentation d'onglet est extrêmement facile et catastrophique. En outre: le compilateur considère un onglet comme 8 espaces, mais votre éditeur le considère probablement comme 2 ou 4, ce qui signifie que ce que vous voyez dans l'éditeur n'est probablement pas ce que le compilateur voit, ce qui augmente les risques d'erreurs. Il y avait même une proposition pour interdire complètement les onglets pour l'indentation dans Haskell. – Bakuriu

0

crachant le message d'erreur tout à fait incompréhensible:

Expecting one more argument to ‘Num’ 
    The first argument of a tuple should have kind ‘*’, 
    but ‘Num’ has kind ‘* -> GHC.Prim.Constraint’ 

Pour compléter la réponse de Bakuriu, laissez-moi décode pour vous.

L'erreur dit que - ligne par ligne:

  • Num attend un argument - que nous devrions écrire Num a de certains a
  • Un type de tuple, comme un type (,) attend comme argument. L'instruction "devrait avoir le type *" signifie "devrait être un type". Le système de kinding de Haskell associe * comme "type de types". Nous avons par exemple Int :: *, String :: * et (Maybe Char, [Int]) :: *. Les constructeurs de type unaire tels que Maybe et [] ne sont pas des types, mais fonctionnent de types à types. Nous écrivons Maybe :: *->* et [] :: *->*. Leur type *->* permet d'indiquer que, depuis Maybe :: *->* et Char :: *, nous avons Maybe Char :: * ("est un type") de manière similaire aux fonctions de niveau de valeur ordinaires. Le constructeur de type de paire a le type (,) :: *->*->*: il attend deux types et fournit un type.
  • Num a le type *-> Constraint. Cela signifie que, pour chaque type T, le type de Num T sera Constraint, ce qui n'est pas * comme le prévoit (,). Cela déclenche une erreur de type. Le type Constraint est donné aux contraintes de classe de type telles que Eq Int, Ord Bool ou Num Int. Ce ne sont pas des types, mais des exigences sur les types. Lorsque nous utilisons (+) :: Num a => a->a->a, nous voyons que (+) fonctionne sur tout type a, tant que ce type satisfait Num a, c'est-à-dire est numérique.Puisque Num T n'est pas un type, nous ne pouvons pas écrire Maybe (Num T) ou [Num T], nous ne pouvons écrire que par ex. Maybe a et exiger dans le contexte que a appartient à la classe Num.