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)