Hugo Peters
Hugo Peters

Reputation: 53

Cannot implement Foldable instance due to wrong kind

I am learning haskell and trying to make a pretty print program. At some point I want to get the length of a row (i.e. number of columns in that row). To be able to do that on my datatype I understand I have to implement Foldable, which relies on Monoid.

Previously my row was just a type alias for a list but for sake of learning I want to make this move

    import System.IO
    import System.Directory
    import Control.Monad
    import Data.Maybe
    import Data.Monoid
    import Data.Foldable
    import Data.Functor
    import Data.List.Split

    type Field = String
    data Row = Row [Field]

    instance Monoid Row where
        mempty = Row []


    instance Foldable Row where
        foldMap f (Row fs) = foldMap f fs

But I get the following compiler error (on ghci 8.0.2)

main.hs:20:19: error:
    • Expected kind ‘* -> *’, but ‘Row’ has kind ‘*’
    • In the first argument of ‘Foldable’, namely ‘Row’
      In the instance declaration for ‘Foldable Row’

Now I am not familiar with what the kind of a datatype is. I was expecting this to simply defer to Row's only property of type List

Upvotes: 2

Views: 188

Answers (2)

Will Ness
Will Ness

Reputation: 71065

what is the "kind" of a datatype?

Types "of kind *" are types of things that can appear in a Haskell program.

Example: Int.

Not an example: Maybe.

a :: Int ; a = 1 can appear in a Haskell program but b :: Maybe ; b = Just 1 can't. It must be b :: Maybe Int ; b = Just 1, to be valid for it to appear in a Haskell program.

What is Maybe Int? It is a type of kind * just as Int is. So what is Maybe? It is a type of kind * -> *. Because Maybe Maybe is also invalid.

The type t appearing after the Maybe must itself be of kind * for Maybe t to be of kind *. Thus the kind of Maybe is * -> *.

Haskell now calls the kind * by new name, Type. Calling it Thing or something could have been more intuitive.

Upvotes: 0

chi
chi

Reputation: 116139

When we have Foldable T, T must be a parametric type, i.e. we must be able to form types T Int, T String, etc.

In Haskell we write T :: * -> * for "a type parametrized over a type", since it resembles a function from types to types. The syntax * -> * is called the kind of T.

In your case, Row is not parametrized, it is a plain type, something of kind *, not * -> *. So, Foldable Row is a kind error. In a sense, a foldable must be a generic list-like container, not one that only carries Field as in your case.

You could instead define data Row a = Row [a], and use Row Field when you need that specific case.

Alternatively, you could try MonoFoldable Row from the mono-traversable package, but note that this is a more advanced option, involving type families. Do not take this path lightly before considering its consequences. It ultimately boils down to why you need a Foldable instance.

Upvotes: 4

Related Questions