bfieck
bfieck

Reputation: 1079

Type Specification in a Where Clause

I'm trying to do something very simple as part of a homework. All I need to do is write a function that takes in a list of 2-tuples of numbers representing base and height lengths for triangles, and return a list of the areas corresponding to those triangles. One of the requirements is that I do that by defining a function and declaring its type in a where clause. Everything I've tried so far fails to compile, here's what I've got:

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

This fails with the error The type signature for ‘triArea’ lacks an accompanying binding, which to me sounds like triArea is not defined inside of the where-clause. Okay, so let's indent it to match the 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

This one fails to compile the particularly uninformative error message parse error on input triArea. Just for fun, let's try indenting it a bit more, because idk what else to do:

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

but, no dice, fails with the same parse error message. I tried replacing the spacing in each of these with equivalent, 4-space tabs, but that didn't help. The first two produce the same errors with tabs as with spaces, but the last one, shown here:

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

gives the error message

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

and I have no idea what that's trying to say, but it seems to be ignoring newlines all of a sudden. I've been reading through "Learn You a Haskell", and I'm supposed to be able to do this with the information presented in the first three chapters, but I've scoured those and they never specify the type of a functioned defined in a where clause in those chapters. For the record, their examples seem to be irreverent of spacing, and I copied the style of one of them:

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

but this also failed to compile, spitting out the utterly incomprehensible error message:

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

I can't find anything when I google/hoogle it, and I've looked at this question, but not only is it showing haskell far too advanced for me to read, but based on the content I don't believe they're having the same problem as me. I've tried specifying the type of calcTriangleAreas, and I've tried aliasing the types in the specification for triArea to be Floating and frankly I'm at the end of my rope. The top line of my file is module ChapterThree where, but beyond that the code I've shown in every example is the entire file.

I'm working on 32-bit Linux Mint 18, and I'm compiling with ghc ChapterThree.hs Chapter3UnitTests.hs -o Test, where ChapterThree.hs is my file and the unit tests are given by my teacher so I can easily tell if my program works (It never gets to the compilation step for ChapterThreeUnitTests.hs, so I didn't think the content would be important), and my ghc version is 7.10.3.

EDIT: Note that if I just remove the type specification altogether, everything compiles just fine, and that function passes all of its associated unit tests.

Please, save me from my madness.

Upvotes: 3

Views: 494

Answers (2)

chi
chi

Reputation: 116174

spitting out the utterly incomprehensible error message:

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

To complement Bakuriu's answer, let me decode that for you.

The error says that -- line by line:

  • Num is expecting one more argument -- we should write Num a from some a
  • A tuple type such as (,) expects a type as argument. The statement "should have kind *" means "should be a type". The kinding system of Haskell associates * as the "kind of types". We have e.g. Int :: *, String :: *, and (Maybe Char, [Int]) :: *. Unary type constructors such as Maybe and [] are not types, but functions from types to types. We write Maybe :: *->* and [] :: *->*. Their kind *->* makes it possible to state that, since Maybe :: *->* and Char :: *, we have Maybe Char :: * ("is a type") similarly to ordinary value-level functions. The pair type constructor has kind (,) :: *->*->*: it expects two types and provides a type.
  • Num has kind *-> Constraint. This means that, for every type T, the kind of Num T will be Constraint, which is not * as (,) expects. This triggers a kind error. The kind Constraint is given to typeclass constraints such as Eq Int, Ord Bool, or Num Int. These are not types, but are requirements on types. When we use (+) :: Num a => a->a->a we see that (+) works on any type a, as long as that type satisfies Num a, i.e. is numeric. Since Num T is not a type, we can not write Maybe (Num T) or [Num T], we can only write e.g. Maybe a and require in the context that a belongs to typeclass Num.

Upvotes: 0

Bakuriu
Bakuriu

Reputation: 102039

Your last example is correct, but the type you wrote doesn't make sense. Num is a class constraint not a type. You probably wanted to write:

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

The rule is: assignments must be aligned.

Moreover (/) requires the Fractional class:

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

Note that the indentation level is not related in any way with the indentation level of the where. For example you could write that code in this way:

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

The indentation level is defined by the first assignment in a where/let or the first line of a do block. All the other lines must align with that one.

So all of these are correct:

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

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

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

Upvotes: 6

Related Questions