Home > Software design >  Do-notation and the list monad
Do-notation and the list monad

Time:11-17

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)
  • Related