0

I was trying to code mergesort in Haskell, but something goes wrong. I think the following code should be correct

-- UPDATE : COMPLETE PROGRAMM'S CODE
merge xs [] = xs
merge [] ys = ys
merge (x:xs) (y:ys) 
    | x <= y = x:merge xs (y:ys)
    | otherwise = y:merge (x:xs) ys

mergeSort xs
    | length xs < 2 = xs
    | otherwise  = merge (mergeSort (myleft xs)) (mergeSort (myright xs))
       where myleft xs = take (half xs) xs
             myright xs = drop (half xs) xs
             where half = \xs -> div (length xs) 2 

Since first where is referred to "otherwise" line it's indented farther than that. The second where is referred to all the first "where block", so it should be nested in the first one. When compiling it gives me this error

 Variable not in scope: mergeSort
   |
26 |  mergeSort xs
   |  ^^^^^^^^^

So there's something wrong with the function's definition. I tried to fix the layout in many different ways, for example

 mergeSort xs
    | length xs < 2 = xs
    | otherwise  = merge (mergeSort (myleft xs)) (mergeSort (myright xs))
    where myleft xs = take (half xs) xs
          myright xs = drop (half xs) xs
          where half = \xs -> div (length xs) 2 

But none of them works.

I also looked for solution here and I found something which is pretty close to what I was doing --> Haskell Merge Sort It also provides a correct code

mergeSort merge xs
        | length xs < 2 = xs
        | otherwise = merge (mergeSort merge first) (mergeSort merge second)
        where first = take half xs 
              second = drop half xs 
              half = length xs `div` 2

But why does this work? Isn't it needed a second where to specify "half"? Why is it absent? Can't understand why mine isn't working anyway.

  • 1
    It looks like you are *calling* mergesort with `mergeSort xs` can you share the full file. Likely there is a syntax error immediately before this. – Willem Van Onsem Feb 24 '21 at 19:34
  • Sure, I'll update the question immediatlly – Flora Angileri Feb 24 '21 at 19:40
  • 1
    In the first snippets your `half` is available inside `myright` but not `myleft`, because the second `where` attaches to the definition of `myright`. Also, please don't post code in the comments: rather, edit your question so that it can be properly formatted/indented. – chi Feb 24 '21 at 19:41
  • There's no way i can attach "half" 's definition to both the two lines? Do i have to write "where half (..)" twice? – Flora Angileri Feb 24 '21 at 19:46
  • See my answer. I will add @chi explanation. – pedrofurla Feb 24 '21 at 19:47
  • @FloraAngileri You could use `where` twice, but that would be inconvenient. When you write `where x = something1 ; y = something2 ; z = something3` all the expressions `something1, something2, something3` can see all the variables `x,y,z` (possibly leading to a recursive definition). E.g. `where x=y+2 ; y=7` defines `x` as `9`. The same effect is achieved by `where y=7; x=y+2`, order does not matter. If you need `half` in multiple places, just define it in the same `where` group as done in the last snippet you posted. – chi Feb 24 '21 at 19:54
  • Yeah, I guess I made some other mistake when I tried to compile it with half as the last. Fixed now. – pedrofurla Feb 24 '21 at 20:02
  • 1
    Thanks a lot!! Now I understand how "where" properly works – Flora Angileri Feb 24 '21 at 20:09

1 Answers1

1

In your code half is only "visible" in the myright definition.

Removing the second where works:

merge :: [a] -> [a] -> [a]
merge = undefined

mergeSort :: [a] -> [a]
mergeSort xs
  | length xs < 2 = xs
  | otherwise  = merge (mergeSort (myleft xs)) (mergeSort (myright xs))
     where myleft xs = take (half xs) xs
           myright xs = drop (half xs) xs
           half = \xs -> div (length xs) 2 
pedrofurla
  • 12,763
  • 1
  • 38
  • 49