3

Sometimes when I want to use wget, I just end up printing a bunch of lines with Python like so:

>>> for i in range(25):
...   print "http://www.theoi.com/Text/HomerOdyssey", i, ".html"
... 
http://www.theoi.com/Text/HomerOdyssey 0 .html
http://www.theoi.com/Text/HomerOdyssey 1 .html
http://www.theoi.com/Text/HomerOdyssey 2 .html
http://www.theoi.com/Text/HomerOdyssey 3 .html
http://www.theoi.com/Text/HomerOdyssey 4 .html
http://www.theoi.com/Text/HomerOdyssey 5 .html
http://www.theoi.com/Text/HomerOdyssey 6 .html
http://www.theoi.com/Text/HomerOdyssey 7 .html
http://www.theoi.com/Text/HomerOdyssey 8 .html
http://www.theoi.com/Text/HomerOdyssey 9 .html
http://www.theoi.com/Text/HomerOdyssey 10 .html
http://www.theoi.com/Text/HomerOdyssey 11 .html
http://www.theoi.com/Text/HomerOdyssey 12 .html
http://www.theoi.com/Text/HomerOdyssey 13 .html
http://www.theoi.com/Text/HomerOdyssey 14 .html
http://www.theoi.com/Text/HomerOdyssey 15 .html
http://www.theoi.com/Text/HomerOdyssey 16 .html
http://www.theoi.com/Text/HomerOdyssey 17 .html
http://www.theoi.com/Text/HomerOdyssey 18 .html
http://www.theoi.com/Text/HomerOdyssey 19 .html
http://www.theoi.com/Text/HomerOdyssey 20 .html
http://www.theoi.com/Text/HomerOdyssey 21 .html
http://www.theoi.com/Text/HomerOdyssey 22 .html
http://www.theoi.com/Text/HomerOdyssey 23 .html
http://www.theoi.com/Text/HomerOdyssey 24 .html
>>> 

I can paste that output into a new file, remove the spaces, and use wget -i.

But I am sick of Python.

I want to learn Haskell.

Despite spending 10 minutes trying to do the same thing from ghci, I am no further forward.

This is what my attempts looked like:

alec@ROOROO:~/oldio$ ghci
GHCi, version 7.0.4: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> putStrLn

<interactive>:1:1:
    No instance for (Show (String -> IO ()))
      arising from a use of `print'
    Possible fix:
      add an instance declaration for (Show (String -> IO ()))
    In a stmt of an interactive GHCi command: print it
Prelude> putStrLn "hey"
hey
Prelude> putStrLn "hey" [1..10]

<interactive>:1:1:
    The function `putStrLn' is applied to two arguments,
    but its type `String -> IO ()' has only one
    In the expression: putStrLn "hey" [1 .. 10]
    In an equation for `it': it = putStrLn "hey" [1 .. 10]
Prelude> putStrLn "hey" snd [1..10]

<interactive>:1:1:
    The function `putStrLn' is applied to three arguments,
    but its type `String -> IO ()' has only one
    In the expression: putStrLn "hey" snd [1 .. 10]
    In an equation for `it': it = putStrLn "hey" snd [1 .. 10]
Prelude> putStrLn "hey" $ snd [1..10]

<interactive>:1:1:
    The first argument of ($) takes one argument,
    but its type `IO ()' has none
    In the expression: putStrLn "hey" $ snd [1 .. 10]
    In an equation for `it': it = putStrLn "hey" $ snd [1 .. 10]
Prelude> "hello"
"hello"
Prelude> "hello" ++ "world"
"helloworld"
Prelude> "hello" ++ [1..10] ++ " world"

<interactive>:1:16:
    No instance for (Num Char)
      arising from the literal `10'
    Possible fix: add an instance declaration for (Num Char)
    In the expression: 10
    In the first argument of `(++)', namely `[1 .. 10]'
    In the second argument of `(++)', namely `[1 .. 10] ++ " world"'
Prelude> "hello" ++ print [1..10] ++ " world"

<interactive>:1:12:
    Couldn't match expected type `[Char]' with actual type `IO ()'
    In the return type of a call of `print'
    In the first argument of `(++)', namely `print [1 .. 10]'
    In the second argument of `(++)', namely
      `print [1 .. 10] ++ " world"'
Prelude> print [1..10]
[1,2,3,4,5,6,7,8,9,10]
Prelude> map ("hello") [1..10]

<interactive>:1:6:
    Couldn't match expected type `a0 -> b0' with actual type `[Char]'
    In the first argument of `map', namely `("hello")'
    In the expression: map ("hello") [1 .. 10]
    In an equation for `it': it = map ("hello") [1 .. 10]
Prelude> greeting :: String --> Int  -> [String, Int]

<interactive>:1:39: parse error on input `,'
Prelude> greeting :: String --> Int  -> [(String), (Int)]

<interactive>:1:41: parse error on input `,'
Prelude> greeting :: String -> Int  -> [(String), (Int)]

<interactive>:1:40: parse error on input `,'
Prelude> greeting :: String -> Int  -> [(String) (Int)]

<interactive>:1:1: Not in scope: `greeting'
Prelude> foreach [1..24] print

<interactive>:1:1: Not in scope: `foreach'
Prelude> import Data.IORef
Prelude Data.IORef> foreach [1..24] print

<interactive>:1:1: Not in scope: `foreach'
Prelude Data.IORef> foreach = flip mapM_

<interactive>:1:9: parse error on input `='
4
  • 3
    As you have discovered, Haskell I/O isn't a 10 minute topic... Commented May 22, 2012 at 15:20
  • 1
    @sacundim putStrLn "hey" vs putStrLn "hey" [1..10] isn't an IO issue -- he's not got the control structures down yet. Commented May 22, 2012 at 15:27
  • 2
    Note that your example is fairly poor python style. Commented May 22, 2012 at 16:25
  • 1
    You can avoid getting the spaces in Python with print "http://www.theoi.com/Test/HomerOdyssey{0}.html".format(i) (among many other ways) Commented May 22, 2012 at 21:39

4 Answers 4

13
for i in range(25):
...   print "http://www.theoi.com/Text/HomerOdyssey", i, ".html"

becomes:

import Control.Monad

so that we can:

 forM_ [1..25] $ \i ->
     putStrLn $ "http://www.theoi.com/Text/HomerOdyssey" ++ show i ++ ".html"
Sign up to request clarification or add additional context in comments.

4 Comments

It might help to explain what $ is.
It might help to explain it in your answer.
The $ operator is for avoiding parenthesis. It brackets anything to the right of it. It avoids Lisp-like clippings.
9
mapM_ (\i -> putStrLn (concat ["http://www.theoi.com/Text/HomerOdyssey", show i, ".html"])) [0..24]

As a bonus, this doesn't print any spaces.

Now some theory:

  • putStrLn is a function that takes a single argument. Python, Perl, etc will slurp up all the arguments you give to print and turn it into a single string. In Haskell you have to do that yourself.
  • mapM_ takes two arguments. Second a list, and first a function which mapM_ passes each element of the list to in turn. The function we've passed here is an anonymous function (like a lambda in Python).

Comments

3

Consider using a list comprehension:

mapM_ putStrLn ["http://www.theoi.com/Text/HomerOdyssey" ++ show i ++ ".html" | i <- [0..24]]

Comments

1

This isn't exactly an answer, but it's too long for a comment. I think a few words about ghci will help a great deal.

In ghci, you can use :t and :info to show the type of something, and information about something, respectively.

Prelude> :t putStrLn
putStrLn :: String -> IO ()
Prelude> :info putStrLn
putStrLn :: String -> IO ()     -- Defined in `System.IO'
Prelude> :t putStrLn "Hello, World"
putStrLn "Hello, World" :: IO ()

:t is useful because you can use it to show the type of arbitrary expressions, which :info won't. But :info can provide information about things that don't have types, such as types themselves or type constructors:

Prelude> :info IO
newtype IO a
  = GHC.Types.IO (GHC.Prim.State# GHC.Prim.RealWorld
                  -> (# GHC.Prim.State# GHC.Prim.RealWorld, a #))
    -- Defined in `GHC.Types'
instance Monad IO -- Defined in `GHC.Base'
instance Functor IO -- Defined in `GHC.Base'

When you're trying to figure out how to make ghci accept something, :t can be very useful.

Next, realize that the ghci prompt gives you line-by-line do-notation, in IO. If you enter something of type IO x, that statement will be evaluated when you press enter. If you want to bind that x, use do-notation arrows. If you want to create a new binding (i.e. create a new function or identifier), use let. If you type in an expression that isn't IO, ghci will attempt to show it to you, which won't work if a Show instance isn't defined.

Prelude> putStrLn "Hello, World"
Hello, World
Prelude> :t getChar
getChar :: IO Char
Prelude> x <- getChar
yPrelude> 
Prelude> show x
"'y'"
Prelude> let g = 10 :: Int
Prelude> :t g
g :: Int
Prelude> show g
"10"
Prelude> g
10

Note that show g and g aren't the same. You can probably figure out why from the behavior I outlined above.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.