8

Hello Haskellers and Haskellettes,

when reading http://learnyouahaskell.com/ a friend of mine came up with a problem:

Is it possible in Haskell to write a recursive function that gives True if all sub-sub-_-sublists are empty. My first guess was - should be - but i have a big problem just writing the type annotation.

he tried something like

nullRec l = if null l
               then True
               else if [] `elem` l
                    then nullRec (head [l]) && nullRec (tail l)
                    else False

which is - not working - :-)

i came up with something like

  • folding with concat - to get a a single long list
    (giving me problems implementing)
  • or making an infinite treelike datatype - and making this from the list
    (haven't implemented yet)

but the latter sound a bit like overkill for this problem. what is your ideas - on a sunny sunday like this ;-)

Thanks in advance


as a reaction to all the comments - this being bad style i'd like to add this is just an experiment!
DO not try this at home! ;-)

7
  • 3
    Think about what the type of such a function should be (if you implement it on normal lists)! Commented Jul 10, 2011 at 14:46
  • 2
    Sick or not, you're on the right track! It's easy enough to write a family of functions nullRec2 :: [[a]] -> Bool, nullRec3 :: [[[a]]] -> Bool and so on (try a couple!), but you can't get them to fit a single type signature easily. You either need a tree type like data Tree a = Branch [Tree a] | Node a or maybe there's something possible with typeclasses (haven't thought much about this approach yet). Commented Jul 10, 2011 at 14:58
  • 1
    stackoverflow.com/questions/5994051 this might help Commented Jul 10, 2011 at 15:31
  • 1
    @FUZxxl's answer will work. I consider the need to solve this problem a red flag -- if it were more than just an experiment, I would suggest rethinking the architecture. Commented Jul 10, 2011 at 16:21
  • 1
    To reinforce luqui's point, think about what the values of nullRec [""] and nullRec [empty :: ByteString] should be. You shall see that there is no good answer. Commented Jul 10, 2011 at 16:30

2 Answers 2

5

How about a typeclass?

{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}

class NullRec a where
  nullRec :: a -> Bool

instance NullRec a => NullRec [a] where
  nullRec [] = True
  nullRec ls = all nullRec ls

instance NullRec a where
  nullRec _  = False

main = print . nullRec $ ([[[[()]]]] :: [[[[()]]]])
Sign up to request clarification or add additional context in comments.

1 Comment

More precisely, you are using not only a type class but also the GHC extension OverlappingInstances. Without this extension, even using a type class cannot achieve what is required in the question. This strongly suggests that if anyone has to implement this (other than as an experiment), the program is probably not designed in the right way.
4

This is not possible using parametric polymorphism only, because of the following.

Consider these values:

x = [8] :: [Int]
y = [3.0] :: [Double]
z = [[]] :: [[Int]]

Obviously, you want your function to work with both x and y, thus its type must be null1 :: [a] -> Bool. (Can someone help me make this argument look formal? How can I show that this is the unique most specific context-less type unifiable with [Int] -> Bool and [Double] -> Bool? Is there a name for that relation between types?)

Now, if you have this type, then null1 z will be equal to null1 x because they differ only in values of the list elements, which are abstracted away from. (Not even close to formal proof again :()

What you want for z is null2 :: [[a]] -> Bool, which will differ in behaviour, and thus giving null1 and null2 the same name will require overloading. (see the FUZxxl's answer)

3 Comments

I find that argument fairly convincing.
I feel like null1 . (:[]) :: a -> Bool brings us closer to formal proof, but I still don't know how to make the last step (showing that a -> Bool is the same as Bool).
It's sort of a free theorem in reverse, but the largest lower bound of Int and Double (on the lattice of Haskell context-free types, with substition as the relationship) is a, as you can't substitute type variables in both of them (there aren't any!) and end up with the same type. Or am I getting my up & down directions mixed up?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.