I need some help understanding a Haskell template of the "List Replication" Hackerrank challenge. I do not understand the order in which the functions are executed.
f = concatMap . replicate
-- This part handles the Input and Output and can be used as it is.
main :: IO ()
main = getContents >>=
mapM_ print . (\(n:arr) -> f n arr) . map read . words
The getContents
function should produce an IO String
like "2\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
. I understand roughly what happens next, but I do not understand in what order and with which precedence. I tried to execute words "2\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10
in ghci and to feed the result into map read
. But then I get the result "[*** Exception: Prelude.read: no parse"
. If I try map read . words "2\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
I get the result "Couldn't match expected type ‘a -> [String]’ with actual type ‘[String]’"
. How is it that the whole composed main function throws no errors?
I would very much appreciate if someone could help me understand the whole main function. Especially which functions get executed in which order with which input, and how I could slice it in smaller (working!) parts to understand it better.
Many thanks!
Defining at GHCi prompt
g = (\(n:arr) -> f n arr)
where
f = concatMap . replicate
we get the derived type back as g :: [Int] -> [Int]
. This defines the type of read
in the map read
(feeding into g
) as String -> Int
, and everything works. It could be easier to follow in the more readable form,
g :: [Int] -> [Int]
g (n:arr) = concatMap (replicate n) arr
The type Int
is derived for n :: Int
because of replicate :: Int -> a -> [a]
, and the type for arr
as well as (n:arr) :: [Int]
follows from that because Haskell lists are homogeneous, i.e. having all the elements of the same type.
The most general type of read
is read :: Read a => String -> a
. Indeed Int
is in Read
type class.
But what if we don't know whether this is an Int
, a Double
, or something else? Then read
does not know which format to parse, and so we get the "read: no parse"
error message back.
This is what happens when we try
(map read . words) "2\n3\n4\n" = map read $ words "2\n3\n4\n"
= map read (words "2\n3\n4\n")
The $
appears there instead of the .
when we open the parentheses. Due to the operator precedence your main
function is actually
main :: IO ()
main = getContents >>=
(mapM_ print . (\(n:arr) -> f n arr) . map read . words)
= getContents >>= (\s ->
(mapM_ print . g . map read . words) s)
= getContents >>= (\s ->
mapM_ print $ g $ map read $ words s)
Using .
instead of $
there as you did is wrong, because just omitting those parentheses is wrong, causes that other error message related to functions, as .
expects a function as its argument on the right but ["2","3","4"]
is not a function.