Kevin
Kevin

Reputation: 2277

Couldn't match expected type in Haskell code that print nested list?

Here is my function

data Item a = One a | Many [Item a]
let flat (One x) = show x
let flat (Many xs) = show xs

Here is the output

Prelude> flat [[3]]

<interactive>:21:6:
Couldn't match expected type ‘Item t0’ with actual type ‘[[t1]]’
In the first argument of ‘flat’, namely ‘[[3]]’
In the expression: flat [[3]]
In an equation for ‘it’: it = flat [[3]]

It seems like flat doesn't recognize Item as its function signature so I tried redefine the function signature

flat :: Item a -> [a]

<interactive>:22:1:
    Couldn't match type ‘a1’ with ‘Char’
  ‘a1’ is a rigid type variable bound by
       an expression type signature: Item a1 -> [a1] at  <interactive>:22:1
Expected type: Item a1 -> [a1]
  Actual type: Item a1 -> String
In the expression: flat :: Item a -> [a]
In an equation for ‘it’: it = flat :: Item a -> [a]

But Haskell does not let you redefine function signature in ghci, is there a way around this?

Upvotes: 1

Views: 406

Answers (2)

gallais
gallais

Reputation: 12123

Answering a follow-up question you asked in the comments of @jtobin's answer: yes there is a way to automatically dectect that [3] needs to be wrapped into a Maybe constructor. However you'll probably need to add type annotations (cf. example to help Haskell figure out what to do=.

We start with a bunch of language extensions.

{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances      #-}

Then comes your definition of Item.

module Item where

data Item a = One a | Many [Item a]

We introduce a class of things which may be reified to an Item a and declare two instances: the Many one and the base case. You can see that we now have overlapping instances (e.g. for Itemable [Int] [Int]) so you're playing with fire here.

class Itemable b a where
  item :: b -> Item a

instance Itemable b a => Itemable [b] a where
  item = Many . fmap item
instance Itemable a a where
  item = One

You can finally define flat as a function that turns a b into an Item a first and then flattens it:

flat :: Itemable b a => b -> [a]
flat = go . item where
  go (One a)   = [a]
  go (Many as) = concatMap go as

Which works as the following example typechecking and evaluating to [2,43,7,8,1] shows:

example :: [Int]
example = flat [[[[[[2::Int],[43]],[[7],[8]],[[1]]]]]]

However, as soon as you try to use the overlapping instances it'll blow in your face. E.g.:

example' :: [[Int]]
example' = flat [[[[[[2::Int],[43]],[[7],[8]],[[1]]]]]]

Upvotes: 1

jtobin
jtobin

Reputation: 3273

flat [[3]] yields a type error. [[3]] has type Num a => [[a]], not Show a => Item a that you can pass into flat.

flat (Many [3]) will return "[3]".

Upvotes: 4

Related Questions