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 type-checker 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.
return (a,a',b)
means the singleton list[(a,a',b)]
which signals "I found a triple,(a,a',b)
, add it to the result list". Similarly,return ()
means[()]
, i.e. "I found a triple,()
, add it to the result list", triggering a type error since that's no triple. To signal, "I found no triple this time" use the empty list[]
(without anyreturn
). – Referendumreturn []
, but clearly that was wrong too. I think I'm still struggling withdo
-notation. How did you get to a place where you could recognize when to return automatically? – Xylem[]
), so you need to keep in mind what's just a random value and what's a monadic action.return
is not a magic primitive, but a function that turns any valuex
into a boring monadic action that only has that value inside (the singleton list[x]
, in this monad). If you havex = []
, do you really need to wrap it inside a list again? – Referendum[ a | a <- [1,2,3] ]
and[ a | a <- [[1,2,3]] ]
. In the former,a
ranges over1,2,3
, while in the lattera
has only one value, the list[1,2,3]
. That's because we added an extra[.....]
. We probably didn't want that, and that's the same issue that you found between[]
andreturn []
(i.e.,[[]]
). In the list monad, usereturn
only what you want to signal "one value found", not "no values found here". – Referendumreturn
inside ado
-block, do you automatically also stare at the type signature to understand whichreturn
is being called? (That's what I'm starting to do now.) – Xylemaction :: M Int
asaction = do .. ; .. ; .. ; lastEntry
we must havelastEntry :: M Int
. Often,lastEntry
isreturn expression
which turnsexpression :: Int
into anM Int
value. Another commonlastEntry
is a function callfoo x y
wherefoo :: ... -> ... -> M Int
. Ending the block withfoo x y
is equivalent to ending withz <- foo x y ; return z
(the former is the preferred form, the latter is a kind of antipattern). In any case, yes, when I usereturn
I always think "which monad am I using here?". – Referendum