Hello Haskell-beginners,
I'm trying to work through the "Write Yourself a Scheme in 48 Hours" tutorial. One of the first exercises calls for writing code to sum up arguments on command line and display the sum. After a few tries, this worked for me: module Main where import System.Environment main :: IO () main = do args <- getArgs putStrLn ("Hello, " ++ show (sumIt args)) where sumIt x = sum $ map read x However, I have two basic questions: 1. I initially tried putStrLn("Hello, " ++ show $ sumIt args), but that didn't compile. Why not? If I wrap (show $ sumIt args) in parens, it works, but should I have to, if I didn't have to in the original code above? 2. I initially tried where sumIt = sum $ map read (the "point-free" style, I believe it's called?) but that didn't compile. Why not? A friend suggested where sumIt = sum . map read and that does work; I guess my real problem, then, is that I don't really understand the difference between the two and why the former doesn't work. Thanks in advance, Anatoly. -- Anatoly Vorobey, [hidden email] http://avva.livejournal.com (Russian) http://www.lovestwell.org (English) -------------- next part -------------- An HTML attachment was scrubbed... URL: http://www.haskell.org/pipermail/beginners/attachments/20080725/fc8fd05c/attachment.htm |
On Fri, Jul 25, 2008 at 4:45 PM, Anatoly Vorobey <[hidden email]> wrote:
> 1. I initially tried putStrLn("Hello, " ++ show $ sumIt args), but that > didn't compile. Why not? You see, Prelude> :i (++) (++) :: [a] -> [a] -> [a] -- Defined in GHC.Base infixr 5 ++ Prelude> :i ($) ($) :: (a -> b) -> a -> b -- Defined in GHC.Base infixr 0 $ so "Hello, " ++ show $ sumIt args is parsed as ("Hello, " ++ show) $ sumIt args which is an obvious error. > 2. I initially tried > > where sumIt = sum $ map read > > (the "point-free" style, I believe it's called?) but that didn't compile. > Why not? A friend suggested > > where sumIt = sum . map read > > and that does work; I guess my real problem, then, is that I don't really > understand the difference > between the two and why the former doesn't work. It helps to see the types and the definitions ($) :: (a -> b) -> a -> b f $ x = f x (.) :: (b -> c) -> (a -> b) -> (a -> c) f . g = \x -> f (g x) so ($) applies an argument, while (.) composes functions. For example, sumIt x = sum (map read x) so sumIt x = sum $ map read x by ($)'s definition. On the other hand sum $ map read is sum (map read) [by ($)'s definition again] which is, again, a mistake. However, we may write sumIt as sumIt = \x -> sum (map read x) so it becames clear that sumIt = sum . map read Did I help at all? =) -- Felipe. |
In reply to this post by Anatoly Vorobey
On Fri, Jul 25, 2008 at 12:45 PM, Anatoly Vorobey <[hidden email]> wrote:
> Hello Haskell-beginners, > > I'm trying to work through the "Write Yourself a Scheme in 48 Hours" > tutorial. One of the > first exercises calls for writing code to sum up arguments on command line > and display > the sum. > > After a few tries, this worked for me: > > module Main where > import System.Environment > > main :: IO () > main = do args <- getArgs > putStrLn ("Hello, " ++ show (sumIt args)) > where sumIt x = sum $ map read x > > However, I have two basic questions: > > 1. I initially tried putStrLn("Hello, " ++ show $ sumIt args), but that > didn't compile. Why not? > If I wrap (show $ sumIt args) in parens, it works, but should I have to, if > I didn't have to in > the original code above? > > 2. I initially tried > > where sumIt = sum $ map read > > (the "point-free" style, I believe it's called?) but that didn't compile. > Why not? A friend suggested > > where sumIt = sum . map read > > and that does work; I guess my real problem, then, is that I don't really > understand the difference > between the two and why the former doesn't work. > > Thanks in advance, > Anatoly. > > -- > Anatoly Vorobey, [hidden email] > http://avva.livejournal.com (Russian) > http://www.lovestwell.org (English) > > _______________________________________________ > Beginners mailing list > [hidden email] > http://www.haskell.org/mailman/listinfo/beginners > > Hello, The difference between (.) and ($) is that (.) is composition and ($) is application. What this means is that (.) takes two /functions/ and generates a /function/ that is like applying the first function after the second function: (f . g) (x) = f (g x) Where f and g are functions and x is a value. ($), on the other hand, takes a /function/ and a /value/ and applies the function to the value: f $ x = f x So the reason you can't do sum $ map read is because map read is a function and sum is a function, so you need to compose the two functions. If you wanted to use application, you could make a lambda expression like \x -> sum $ map read x, because now map is fully applied and is a value for sum. (Note that I'm oversimplifying a bit here; Haskell doesn't actually make a distinction between functions and values, but it's easier to think about like this for simple cases.) Alex |
In reply to this post by Anatoly Vorobey
On Fri, 2008-07-25 at 22:45 +0300, Anatoly Vorobey wrote:
> 2. I initially tried > > where sumIt = sum $ map read > > (the "point-free" style, I believe it's called?) but that didn't > compile. Why not? A friend suggested > > where sumIt = sum . map read > > and that does work; I guess my real problem, then, is that I don't > really understand the difference > between the two and why the former doesn't work. > The $ is an operator that performs function application. So for example: show $ "Hello" is equivalent to show "Hello" You don't need an operator for function application, normally you just type your arguments after the function. But $ just happens to be right-associative, which means it will "evaluate" (modulo lazy semantics of course) its right-hand-side argument before applying it to its left-hand-side argument. So your expression sum $ map read First "evaluates" (map read) then applies it to sum. That is, it's the same as: sum (map read) This is a type error, as the compiler will tell you. The function composition operator (.) takes two functions f :: (b->c) and g :: (a->b) and returns a function (a->c) that effectively first applies g (yielding a 'b') and then f (yielding a 'c'). That is: f after g. So: sum . map read which should be read as: sum . (map read) which is equivalent to: (\x -> sum ((map read) x)) which is itself a new function that reads a list of strings and sums the result. In my opinion it's often helpful to look at the function types using :t in ghci, e.g. ":t (.)" and ":t ($)" (don't forget the brackets around the operators). I'm not an experienced Haskell programmer myself but I'd suggest not using point free style if you find it confusing, but rather write out parameter names and lambdas explicitly. You style will change as you get more comfortable with the language. Regards, Niels |
Free forum by Nabble | Edit this page |