Koz Ross
Koz Ross

Reputation: 3140

Text.Tabular.Table type errors - what is the type system asking from me?

I have the following code, using this tabulation library:

{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}

import ClassyPrelude
import qualified Text.Tabular as T

data Category = Age | Gender | Usual | Years
  deriving (Show, Read, Eq, Enum, Bounded)

 tabulate :: Text -> [[Int]] -> T.Table (T.Header Int) (T.Header Text) Int
 tabulate lbl tab = T.Table (T.Group T.NoLine (map T.Header leftcoll)) (T.Group T.DoubleLine [T.Header lbl, T.Header "All", T.Header "Cluster0", T.Header "Cluster1"]) rest
   where leftcoll = map (`indexEx` 0) tab
         rest = map (drop 1) tab

When I try to compile it, I get the following errors:

Couldn't match type ‘Int’ with ‘T.Header Int’
Expected type: [T.Header Int]
  Actual type: [Int]
In the second argument of ‘map’, namely ‘leftcoll’
In the second argument of ‘T.Group’, namely
  ‘(map T.Header leftcoll)’


Couldn't match expected type ‘T.Header Text’
            with actual type ‘Text’
In the first argument of ‘T.Header’, namely ‘lbl’
In the expression: T.Header lbl

I have absolutely no clue why this is the case. From the Table type documentation and example, it would appear that I simply have to apply the Header constructor to a list of values and then put them inside the Group constructor to get a whole row (or column) of values, but the errors there seem to suggest that the lists (and data) I'm passing as arguments to the Header constructor already need to be Headers. Thus, I'm confused about what the type system is telling me here, and how to get what I want.

Essentially, the table should look something like this:

Foo All Cluster0 Cluster1
=========================
1   10  3        7
2   11  10       1
....

Upvotes: 0

Views: 31

Answers (1)

madjar
madjar

Reputation: 12961

It took me a while to find this one, because there is no problem in the definition of your function: it's the type declaration that is wrong.

Let's take a look at the definition of Table:

data Table rh ch a = Table (Header rh) (Header ch) [[a]]

The column header and row header type paramater are already warped in the Header type, so there no need to do it once more.

Instead of defining:

tabulate :: Text -> [[Int]] -> T.Table (T.Header Int) (T.Header Text) Int

you can defined:

tabulate :: Text -> [[Int]] -> T.Table Int Text Int

To debug that kind of things, I usually extract part of the expression to a where clause, along with a type annotation that tells the compiler what each the expression should be. This often gets me better error messages. For example, here, I extracted:

where tagada :: [T.Header Int]
      tagada = map T.Header leftcoll

In the error, I could read:

Expected type: [T.Header (T.Header Int)]
  Actual type: [T.Header Int]

That's when I realized that the Header type was maybe used twice.

Upvotes: 1

Related Questions