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.
List as a data structure is not a Monad, but the fact that Scala's List implements flatMap is what gives it its monadic super-powers. It also needs to fulfil associativity, left unit and right unit laws to qualify as a Monad.
Lists are a fundamental part of Haskell, and we've used them extensively before getting to this chapter. The novel insight is that the list type is a monad too! As monads, lists are used to model nondeterministic computations which may return an arbitrary number of results.
As a syntactical convenience, do notation does not add anything essential, but it is often preferable for clarity and style. However, do is not needed for a single action, at all. The Haskell "Hello world" is simply: main = putStrLn "Hello world!"
A Monad consists of two interrelated functions: bind and unit. Bind takes a non-composable function f and returns a new function g that accepts the monadic type as input and returns the monadic type. g is composable. The unit function takes an argument of the type that f expected, and wraps it in the monadic type.
Each monad provides a mechanism for composing such monadic functions. As we have seen, the do notation simplifies the syntax of composing multiple monadic functions.
A monad describes the way of transforming the return type of a particular kind of computation into a fancier monadic type. Functions that return a monadic type are called monadic functions. Each monad provides a mechanism for composing such monadic functions.
Combining monadic values Using the List monad Using the Monad class constraint This section contains a few simple exercises to hone the reader's monadic reasoning skills and to provide a solid comprehension of the function and use of the Maybe and List monads before looking at monadic programming in more depth.
A monad that is an instance of the Monadclass can be used with do-notation, which is syntactic sugar that provides a simple, imperative-style notation for describing computations with monads. The monad laws The tutorial up to now has avoided technical discussions, but there are a few technical points that must be made concerning monads.
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)
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 []
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With