I am learning Haskell.
I am trying to find elements of a list as which sum to elements of a list bs, returning the elements as a tuple:
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = [(a, a', b) | a <- as, a' <- as, b <- bs, a a' == b]
The code works. But in order to learn Haskell, I'm trying to rewrite it as do-notation:
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = do
a <- as
a' <- as
b <- bs
if a a' == b then return (a, a', b) else return ()
The typechecker then complains at me:
• Couldn't match type ‘()’ with ‘(Int, Int, Int)’
Expected type: [(Int, Int, Int)]
Actual type: [()]
In all fairness, I knew it would. But since I can't skip the else clause in Haskell, what should I put in the return statement in the else clause?
Thanks.
CodePudding user response:
You can not return the unit (()), since that means that the return (a, a', b) and the return () have different types: the first one is [(Int, Int, Int)], whereas the latter is [()].
What you can do is use an empty list in case the guard fails, so:
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = do
a <- as
a' <- as
b <- bs
if a a' == b then return (a, a', b) else []
CodePudding user response:
You must return something of the correct type in the else clause. It could be the empty list [], or one of the abstracted values like mzero or empty.
Or you could remove the if expression and use the guard function.
import Control.Monad (guard)
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = do
a <- as
a' <- as
b <- bs
guard (a a' == b)
return (a, a', b)
With this implementation you could now also generalize your function signature to:
findSum2 :: MonadPlus m => m Int -> m Int -> m (Int, Int, Int)
