Timotheus
Timotheus

Reputation: 111

Incorrect where indentation? Short code

I tried to make a very short code, because we have a reglementation that favors the shortest code. We have to create a function that turns a list into a new list of ascending and descending lists in the same order: For example. [1,6,2,1,7,3,2,8,4], becomes [[1,6],[2,1],[7],[3,2],[8],[4]]

So i tried to do the following:

func :: Ord a => [a] -> [[a]]
func xs = f1 d [a]
f1 [] ys = [c]
f1 xs ys    | a >= b = d `f1` a:ys
            | otherwise = c: d `f2` [a]
f2 [] ys = [c]
f2 xs ys    | a < b = d `f2` a:ys
            | otherwise = c : d `f1` [a]
    where   a = head xs
            b = head ys
            c = reverse ys
            d = tail xs

But i get

parse error on input '=' 

on the line "b = head ys".

I thought it is possible to define multiple functions in the where block?

Other indentations created errors like a lot of

not in scope 'a'
not in scope 'b'
not in scope 'c'
not in scope 'd'

or

parse error on input 'b'

I have to make it that way to save some tokens/ have shorter code.

Upvotes: 2

Views: 168

Answers (3)

AndrewC
AndrewC

Reputation: 32455

Your error messages are because you're mixing tabs and spaces. It's best to just use spaces.

Now, if you're writing

a = head xs
b = head ys
c = reverse ys
d = tail xs

then

xs = (a:ds)
ys = (b:es)

Let's rewrite your function with pattern matching:

func :: Ord a => [a] -> [[a]]
func [] = []
func (a:ds) = f1 ds [a]
f1 [] ys = [reverse ys]
f1 (a:ds) (b:es) | a >= b = ds `f1` (a:b:es)
                 | otherwise = reverse (b:es): ds `f2` [a]
f2 [] ys = [reverse ys]
f2 (a:ds) (b:es) | a < b = ds `f2` (a:b:es)
                 | otherwise = reverse (b:es) : ds `f1` [a]

I know this is longer, but bear with me. f1 is really the same as f2, but with the comparison changed. Let's get a function no to negate a comparison, so that (no (>=)) x y = not (x >= y):

no cmp x y = not (cmp x y)

In fact, we can write this as

no = ((not.).)
r = reverse

func' (a:ds) = f (>=) ds [a]
f :: Ord a => (a -> a -> Bool) -> [a] -> [a] -> [[a]]
f _ [] ys = [r ys]
f cp (a:ds) ys@(b:es) | cp a b = f cp ds (a:ys)
                      | True = r ys : f (no cp) ds [a]

Now that is shorter.

Upvotes: 0

Tikhon Jelvis
Tikhon Jelvis

Reputation: 68152

You have a more fundamental problem than indentation: a where block is local to a single function case. You're trying to use your where block to provide bindings (e.g. a, b, c, d) to a whole bunch of functions. This won't work.

To clarify, this correctly indented code won't work:

foo :: Int -> Int
foo 0 = a
foo 1 = b
  where a = 2
        b = 3

You'll get an error like Not in scope: `a'. This is because the where only extends over the foo 1 case; it doesn't even go to the foo 0 case, much less any other function.

Your code, on the other hand, seems to expect the where block to work for all your functions. To have bindings that can be seen by different functions, you have to put them at the same level of scope as the functions themselves.

Also, Haskell indentation is a little finnicky. You really should avoid tabs; it also really helps to have an editor that understands Haskell properly. I've found Emacs is very good here--I never have to worry about Haskell indentation with Emacs.

Emacs might have a bit of a learning curve (you should do the tutorial), but I think it's well worth it. You'll also have to install the Haskell mode. If you get the newest version of Emacs, you should be able to do this using the package manager.

Upvotes: 2

Daniel Fischer
Daniel Fischer

Reputation: 183878

That's how it looks to the compiler:

func :: Ord a => [a] -> [[a]]
func xs = f1 d [a]
f1 [] ys = [c]
f1 xs ys    | a >= b = d `f1` a:ys
                | otherwise = c: d `f2` [a]
f2 [] ys = [c]
f2 xs ys    | a < b = d `f2` a:ys
                | otherwise = c : d `f1` [a]
            where    a = head xs
                        b = head ys
                        c = reverse ys
                        d = tail xs

So for the compiler, the lines in the where clause after the first look like continuations of that line, and of course you can't have multiple = without an intervening semicolon on one line.

You should never mix tabs and spaces (you shouldn't ever use tabs, actually). And if you use tabs, configure your editor to interpret them as eight spaces.

And, the where clause only scopes over the last equation, so there are no a, b, c, d in scope in the first four equations for func.

Upvotes: 2

Related Questions