1

I am having some trouble implementing this function in Haskell. Here is what I have so far:

--Extend coprime to a function on lists:
--coprime_with n list = True exactly when n is coprime with
--every element of list.

coprime_with :: Integer -> [Integer] -> Bool
coprime_with a [] = False
coprime_with a (b:bs) = if ((coprime a b) == True) then 
                          (coprime_with a bs)
                        else False

Hopefully this isn't too confusing. Please don't just give me the answer. Just let me know what I'm doing wrong and how to fix it. Thanks!

EDIT: Okay so I figured it out! Thank you so much for the response. It was incredibly fast. I am a Comp Sci major and this is my first semester. My goal is to actually learn and not just get through it. I appreciate your response and how you worded it.

My problem was this:

coprime_with a [] = False

when it should have been this:

coprime_with a [] = True

Now it is working properly. Phew... I feel pretty stupid that it just took me five hours to write this function, but at least I actually learned something. Thanks again!

dfeuer
  • 48,079
  • 5
  • 63
  • 167
  • Could you provide an example input with an expected output, along with the current output? – bheklilr Sep 16 '14 at 05:21
  • 1
    A side note, `if ((coprime a b) == True) then ...` is the same as `if coprime a b then ...`. The parentheses are not needed, and once you have a `Bool` there's no need to compare it to another `Bool`. – bheklilr Sep 16 '14 at 05:23
  • 1
    I think I see your mistake. As a hint: what does `and []` return in GHCi? Why does it return that? How does that compare to your function? – bheklilr Sep 16 '14 at 05:25
  • Hint: your function definition never mentions `True`, nor it performs operations on booleans, hence... – Bakuriu Sep 16 '14 at 05:38
  • 1
    You should add the answer to this question yourself (as you found it) so that you can mark the answer later (let's clean up a bit) – Random Dev Sep 16 '14 at 05:45
  • 3
    Take it to the next level and write it with [`all`](http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html#v:all)! :-) – luqui Sep 16 '14 at 05:52

2 Answers2

5

Great that you've found your problem. There's few more lessons you can take from this.

so you now have

coprime_with :: Integer -> [Integer] -> Bool
coprime_with a []     = True
coprime_with a (b:bs) = if ((coprime a b) == True) then 
                          (coprime_with a bs)
                        else False

whenever you see if A then B else False, you can replace it with A && B. Do you see why?

whenever you see A == True, you can replace it with just A. Do you see why?

So now we have

coprime_with a (b:bs) = (coprime a b) && (coprime_with a bs)

The parameter a doesn't ever change, but we pass it on and on to every recursive invocation, needlessly. On the other hand, we must have it, as it is what coprime_with is all about. The solution is to introduce an internal function,

coprime_with a bs = g bs      -- "g" is "coprime_with a"
   where
     g []     = True
     g (b:bs) = coprime a b && g bs

(we can write it without the parentheses as functional application in Haskell has the highest priority).

this is known as worker/wrapper transform. It makes our code cleaner, and lets compiler to perform more optimizations, potentially. Our new g is the worker, and coprime_with is the wrapper as it wraps g inside. g has a different type, and coprime_with takes care of feeding it the proper arguments (here just the second one), and transforming its results back into what expected (here just passing them along).

Next, we can give a new name, in Haskell, to whatever (sub-)expression we want. Here, we'll do

coprime_with a bs = g pred bs    -- "pred" is for "predicate"
   where
     g pred []     = True
     g pred (b:bs) = pred b && g pred bs
     pred = coprime a

what's the point to this? we're back at passing an extra parameter on recursion, you ask? Correct! This is not an optimization, but just a re-write, aiming to get somewhere. Next, we can even substitute back the definition of pred

coprime_with a bs = g (coprime a) bs
   where
     g :: (a -> Bool) -> [a] -> Bool
     g pred []     = True
     g pred (b:bs) = pred b && g pred bs

and that's our destination. What's left is to realize that we've reinvented a wheel (one of many). You can find what it is by searching for g's type on Hoogle and examining the sources for the few top matches there to see which one is our g exactly. Hint: you already were suggested its use, in the comments.


Now we can see clearly why it is True and not False that must be returned in the empty list case. Our g checks to see whether the predicate holds for all elements of the list. In other words, it must fail (meaning here, return False) if some element in the list fails the predicate — and so, return True otherwise! But there are no elements in the empty list, to fail the predicate.

(another explanation for this issue that I like basically says, because g (a++b) == g a && g b and a == a ++ [], it must hold g a == g (a++[]) == g a && g []).

Community
  • 1
  • 1
Will Ness
  • 70,110
  • 9
  • 98
  • 181
2

The answer you are looking for is to change

coprime_with a [] = False

to

coprime_with a [] = True

When the list was getting to the final recursive stage, it had turned into an empty list. The fact that it made it to the empty stage meant that all the numbers inside the list had been coprime. However, the parameters were set up in such a way that when the list was empty, it was set to read a "False". Simply changing that condition to "True" fixed the issue.