2

I am making the pilgrimage from Java to Haskell. Broadly speaking, I get the main concepts behind Haskell. Reading all the tutorials and books 'makes sense' but I am getting stuck writing my own code from scratch.

I want to create 1000 files on the file system with names

"myfile_1.txt" ... "myfile_1000.txt"

and each containing some dummy text.

so far I have worked out the whole IO thing, and realise I need to build a list of Strings 1000 elements long. So I have:

buildNamesList :: [] -> []
buildNamesList ???

Once I have the List I can call the writefile method on each element. What I can't figure out is how to add a number to the end of a String to get each fileName because I can't have an int i = 0, i ++ construct in Haskell.

I am a bit out of my depth here, would appreciate some guidance, thanks

2
  • 2
    [] -> []? That doesn't make any sense, [] is not a type but a type constructor. You probably mean [a]->[a], but that's not really right either. — Generally, it seems to me you have not understood the main concepts behind Haskell. You don't want to "build" a list, you just define it. You don't "call the writefile method on each element", but traverse a list (e.g. with mapM_). You also don't really "add something to the end of a string", you rather define a new postfixed string – though, actually, we do call this adding to the end. Commented Jul 8, 2013 at 11:59
  • thanks, that is the bit I don't get. Now I have this list of Strings how do I then traverse over it, pluck out the String at element n and then pass it into another function? Commented Jul 8, 2013 at 12:20

4 Answers 4

7

One possible solution:

buildNamesList = map buildName [1..1000]
  where buildName n = "myfile_" ++ show n ++ ".txt"
Sign up to request clarification or add additional context in comments.

6 Comments

Ah. I see, many thanks. But how come this does not need to define the type of buildNamesList using the double colon :: operator?
the "double colon" introduce a type signature. The type signature should be written but is not mandatory. Here it woueld be : buildNamesList :: [String] since the function provies a list of String.
Incidentally, there seem a lot of people to believe that strong static typing means declaring types eveywhere. But the truth is, of course, that standard Haskell almost never needs this.
People are not used to type inference. However, ghc emits a warning if you don't provide type signature for top level declaration.
@Nicolas I've never seen that. Do you have to say -Wall or something?
|
5
import Control.Applicative

fileNames = ("myFile_"++) <$> (++".txt")  <$> show <$> [1..1000]

Comments

2

how do I then traverse over it, pluck out the String at element n and then pass it into another function?

No! "Plucking out" something from a list in inefficient. You don't want to worry about how to get to each element, then do something with it. That's necessary in imperative languages because they don't have a proper abstraction over what "sequencing actions" means – it's just something magical built into the language. Haskell has much more well-specified, mathematically sound and type-safe magic for that; as a result you don't need loops and suchlike.

You know what to do with each element (String -> IO ()), and you know where the data comes from ([String]). You also know what should eventually happen (IO ()). So the combinator you're looking for has type ( String -> IO() ) -> [String] -> IO (), though obviously it doesn't really depend on the data being Strings, so let's simplify that to (a -> IO()) -> [a] -> IO(). You can look that up on Hoogle, which offers amongst sume rubbish mapM_ and forM_, both of which do what you want:

mapM_ (\filename -> writeFile filename "bla") filenamesList

or

forM_ filenamesList $ \filename ->
   writeFile filename "bla"

2 Comments

thanks! makes sense, so because the writeFile function takes two arguments we need to use a lamda function (\filename) to "hold" the value of the mapped element of filenamesList and insert it correctly in the call to writeFile? Is this the correct way to look at it? Thanks again.
Yes. Experienced Haskellers might shorten this a bit: mapM_ (`writeFile` "bla") filenamesList (notice the back ticks). This is known as an operator section, a case of partial application.
0

Sometimes I think of foldr as bearing some resemblance to a for loop. Here's something kind of like the i++ construct, applying i inside a loop:

foldr (\i accum -> ("myfile_" ++ show i ++ ".txt") : accum) [] [1..1000]


Another way could be zipWith, which applies a function to combine two lists:

zipWith (\a b -> a ++ show b ++ ".txt") (repeat "myfile_") [1..1000] 

or

zipWith ($) (repeat (("myfile_" ++) . (++ ".txt") . show)) [1..1000]


And here's a recursive example, too, applied as fileList "myfile_" ".txt" [1..1000]:

fileList _     _   []     = [] 
fileList fName ext (x:xs) = (fName ++ show x ++ ext) : fileList fName ext xs

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.