2

So I have searched different ways of approaching the "if" statements in Haskell and have a doubt about guards, say I have a tuple and want to perform +,-,*,/ checking the conditions:

given (x,y) if x < y only +,* as I only want integers, furthermore check division so that x mod y == 0 or not, this compiles but I can-t get it to run

operaciones (x,y) = (x,y)
x,y | x < y = [(x, y, '+', x+y), (x, y, '*', x*y)]
    | (x > y) && (x `mod ` y == 0) = [(x, y, '+', x+y), (x, y, '*', x*y), (x, y, '-', x-y) , (x, y, '/', x/y)]
    | (x > y) && (x `mod ` y /= 0) = [(x, y, '+', x+y), (x, y, '*', x*y), (x, y, '-', x-y)]
    | otherwise  = [(x, y, '+', x+y), (x, y, '*', x*y), (x, y, '-', x-y) , (x, y, '/', x/y)]

i took the idea from

Haskell: Multiple Case Statements in Single Function

but failed, otherwise is if x == y

Cœur
  • 37,241
  • 25
  • 195
  • 267

2 Answers2

8

What you have written is not legal syntax.

You probably want this:

operaciones (x,y)
    | x < y = [(x, y, '+', x+y), (x, y, '*', x*y)]
    | (x > y) && (x `mod ` y == 0) = [(x, y, '+', x+y), (x, y, '*', x*y), (x, y, '-', x-y) , (x, y, '/', x/y)]
    | (x > y) && (x `mod ` y /= 0) = [(x, y, '+', x+y), (x, y, '*', x*y), (x, y, '-', x-y)]
    | otherwise  = [(x, y, '+', x+y), (x, y, '*', x*y), (x, y, '-', x-y) , (x, y, '/', x/y)]
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • I could be mad, but I don't think this is legal syntax either. What is `(x, y, '+', x+y), (x, y, '*' x*y)` supposed to evaluate to? – Chuck Apr 05 '13 at 18:02
  • Ah yes, I blindly copied your code. I didn't notice that your syntax is also incorrect on the right hand side of each case. Presumably it's supposed to be a list of tuples. – Peter Hall Apr 05 '13 at 18:03
  • I've changed my answer to include that. Also found you were using `!=` which is not the operator in haskell, it's `/=` instead. – Peter Hall Apr 05 '13 at 18:05
  • Okay so this works however i'm only printed (x, y, '+', x+y), (x, y, '*', x*y), (x, y, '-', x-y) , (x, y, '/', x/y) but not the actual values that i introduced, how can i fix this? I've eddited my code and works but as you correctly point out returns "garbage" – Pablo Dominguez Lyons Apr 05 '13 at 18:08
  • compiles fine but i get the following error message: Ambiguous tybe variable `t` in the constraints: `Fractional t ` – Pablo Dominguez Lyons Apr 05 '13 at 18:12
  • Yes, you have a lot of problems in this short code snippet! This is a slightly annoying problem though. Division `/` requires `Fractional` instances, while addition `+` requires only `Num`. To make this reconcile, you need to explicitly convert to a fractional type, using `fromIntegral`. – Peter Hall Apr 05 '13 at 18:16
  • but what would i be converting? my original tuple or the tuples generated after evaluating the guards? – Pablo Dominguez Lyons Apr 05 '13 at 18:19
  • @PabloDominguezLyons: You'd be converting the numbers from the original tuple in order to use them in your calculations. – Chuck Apr 05 '13 at 18:22
  • operaciones fromIntegral(x,y) added on to the snippet, produces a GHCi yell aka Ambiguous type variable t1 in the contraints: integral t1 and Fractional t1 – Pablo Dominguez Lyons Apr 05 '13 at 18:25
  • 1
    solved, changed all the / for ´div´ thanks to this: http://stackoverflow.com/questions/4203178/haskell-problem-converting-result-of-division-to-integral-type – Pablo Dominguez Lyons Apr 05 '13 at 19:18
  • @PabloDominguezLyons that's probably easier! – Peter Hall Apr 05 '13 at 19:21
3

I wouldn't recommend this style; you're repeating yourself way too much. As I understand it, you want this function:

operaciones (x, y) = [
    (x, y, '+', x + y),
    (x, y, '*', x * y),
    (x, y, '-', x - y),
    (x, y, '/', x / y) ]

but with the result list filtered to only include positive integer results. (Incidentally, at least in English, the convention is that 'integer' includes negative numbers and 0, so your condition on including the difference is stricter than just 'integer results').

I would do the filtering by concatenating list comprehensions:

operaciones (x, y) =
    [ (x, y, '+', x + y) ] ++
    [ (x, y, '*', x + y) ] ++
    [ (x, y, '-', x + y) | x > y ] ++
    [ (x, y, '/', x `div` y) | x >= y, x `mod` y == 0 ] -- I'm assuming x and y have type Int or Integer, so you should use div not /

One other note: it's not usual in Haskell to combine multiple arguments into a tuple like (x, y); so if x and y are separate arguments, your function head should be written like

operaciones x y =

instead. (The way the compiler is written, the optimizer actually has to do extra work to combine the tuple for (x, y) into the separate form anyway, so better to save it the work.)

Update: I can't think of as clean a way to do error reporting. I would probably end up with a hybrid style, using guards for error checking and concatenation for the success case:

operaciones x y
    | x <= 0 || y <= 0 = Left "Use positive numbers"
    | otherwise = Right $
        [ (x, y, '+', x + y) ] ++ -- etc. as above

You could use error instead of Left and omit the Right if you really wanted to.

Jonathan Cast
  • 4,569
  • 19
  • 34
  • Thanks, yes, this does make it much easier to read and function i guess, how would i add an error "clause" such as if x or y are less than 0 print something out, i tried adding a condition such as x < 0 || y < 0 = error "Use positive numbers!" but it does not work – Pablo Dominguez Lyons Apr 11 '13 at 17:26
  • Hmm. I admit, I can't think of a clean way to incorporate error reporting. I don't recommend `error` for error checking, by the way, since there's no reliable way to recover from it; it's normally better to use `Maybe` or `Either` (I love `Either String` for error reporting) instead. – Jonathan Cast Apr 11 '13 at 18:51
  • Also, my input is always a tuple, from a previous function that i made that places a string of integers into all the possible tuples! – Pablo Dominguez Lyons Apr 12 '13 at 09:15
  • Actually, i have one more question @jcast , i tried using the double or with guards on my first attempt but did not get it to work, with this code snippet same thing applies, haskell yells at the 1st | of the double || for some reason... idk why! – Pablo Dominguez Lyons Apr 12 '13 at 16:22
  • @PabloDominguezLyons Can you cut/paste the code GHC is complaining about? I would guess you've got a space between the two |s, but I'd rather see exactly what you've typed in. – Jonathan Cast Apr 12 '13 at 18:04
  • operaciones x y | x <= 0 || y <= 0 = Left "Use positive numbers" | otherwise = Right $ [ (x, y, '+', x + y) ] ++ [ (x, y, '*', x + y) ] ++ [ (x, y, '-', x + y) | x > y, (x/=4 && y/=2) ] ++ [ (x, y, '/', x `div` y) | x >= y, x `mod` y == 0] can't set the code to be shown decently -.- – Pablo Dominguez Lyons Apr 22 '13 at 07:55
  • Cutting/pasting that code into ghci doesn't give any error. You can edit other people's answers, so you could try appending your code to the end of my answer if you want to get the formatting correct. – Jonathan Cast Apr 22 '13 at 17:19
  • Hmmm thats what i did, i will have anothr shot see what happens, thank you for your time! – Pablo Dominguez Lyons Apr 23 '13 at 17:59