19

I'm trying to write a function that takes a String and an Int and returns that string "int" times. That is:

duplicate :: String -> Int -> String

If I were to write duplicate "Hello" 3 the output should be "HelloHelloHello".

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
benharris
  • 587
  • 3
  • 10
  • 19

5 Answers5

22

Easily:

duplicate :: String -> Int -> String
duplicate string n = concat $ replicate n string

The $ is a function of type (a -> b) -> a -> b. The language allows the functions with non-alpha-numeric names to be used in infix form (as operators). I.e., the body of the function above is absolutely identical to the following expression:

($) concat (replicate n string)

What $ does is just allows you to get rid of braces. Meaning that the above expressions are just an alternative to the following expression:

concat (replicate n string)
Nikita Volkov
  • 42,792
  • 11
  • 94
  • 169
  • That's great thanks! I've seen concat used before and that was my first guess... but what does the $ sign do in this situation? – benharris Nov 27 '13 at 15:36
  • 1
    @benharris The `$` operation is simply function application. Only it has the lowest precedence and it's right associative instead of left associative. – Aadit M Shah Nov 27 '13 at 15:37
  • 2
    You should change the type signature of `duplicate` to `[a] -> Int -> [a]`. That way you can use it for any list. – Aadit M Shah Nov 27 '13 at 15:38
  • 1
    @AaditMShah It would make sense to flip the arguments too. Not creating such a basic compositional function at all is also an option. The question is how many subjects we want to touch while explaining trivial stuff. It's pretty easy to overburden a beginner. – Nikita Volkov Nov 27 '13 at 15:51
  • 1
    True. It makes sense to have `n` as the first argument as that allows you to compose functions like `putStrLn . duplicate 3`. On that note, @benharris, I think you should read [Learn You A Haskell](http://learnyouahaskell.com/). – Aadit M Shah Nov 27 '13 at 16:10
  • Am I missing something? Tried this exact thing and did not work. Not in ghci nor loading script. – Jessie Richardson Sep 22 '17 at 01:06
  • @JessieRichardson Most likely you have an issue in something else. Post a dedicated question. – Nikita Volkov Sep 22 '17 at 07:49
8

A String is just a synonym for a list of Char, and the list type is a Monad. Therefore

duplicate :: Int -> String -> String
duplicate n str = [1..n] >>= const str

Or, if you wanted to get all point-free

duplicate = (. const) . (>>=) . enumFromTo 1

Edit

As suggested in the comments

duplicate n str = [1..n] >> str

or

duplicate = (>>) . enumFromTo 1
Chris Taylor
  • 46,912
  • 15
  • 110
  • 154
5

You can use replicate and concat as follows:

duplicate :: [a] -> Int -> [a]
duplicate = flip $ (concat .) . replicate

-- or as larsmans suggested:

duplicate :: [a] -> Int -> [a]
duplicate = (concat .) . flip replicate

Then use it as duplicate "Hello" 3.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • 1
    Shorter: `(concat .) . flip replicate`. – Fred Foo Nov 27 '13 at 16:37
  • 1
    Yes, you're right, I confused it with something to be. I'll delete the comment. – Ingo Nov 27 '13 at 16:52
  • 3
    This is a classic example of pointless pointfree. Can you tell me, at a glance, why the `(.)` are there in the places they are? What are they doing? Why are they necessary? On the other hand, `let (.:) = fmap fmap fmap in concat .: flip replicate` is an example of the use of `fmap fmap fmap`, or `(.).(.)`, to compose functions with two arguments. This is an idiom you can reuse elsewhere, not just the output of running `pointfree`. – Rein Henrichs Nov 28 '13 at 20:09
  • @ReinHenrichs Indeed. If you're interested in taking pointfree programming to the extreme then take a look at the following answer: http://stackoverflow.com/a/20279307/783743 – Aadit M Shah Nov 29 '13 at 05:59
3

You can use pattern matching.

duplicate _ 0 = []
duplicate xs n = xs ++ duplicate xs (n-1)

or

duplicate xs n  | n==0 = []
                | otherwise = xs ++ duplicate xs (n-1)
Shanthakumar
  • 757
  • 6
  • 23
  • 1
    This is exactly equivalent to `concat .: flip replicate` except inlined. I suspect that they'd compile to the same core when optimized. – daniel gratzer Nov 27 '13 at 16:16
  • Actually that's a great answer for a beginner who doesn't know about monads, point-free style, etc. – Daniel Nov 28 '13 at 20:00
1

Again a beginners attempt, using recursion

duplicate s n = if n <= 1 then s else duplicate (n-1) s ++ s

though it is a little unclear what the function should do if n is negative or zero. So I chose to return the string itself.