F#, char seq -> strings

user1158550 picture user1158550 · Feb 3, 2013 · Viewed 13.8k times · Source

A quick question that may be more of a rant (but I hope to be enlightened instead).

In F# a string is compatible with Seq such that "abcd" |> Seq.map f will work on a string.

This is a brilliant facility for working with strings, for example to take the first 5 chars from a string:

"abcdef01234567" |> Seq.take 5

Or removing duplicate characters:

"abcdeeeeeee" |> Seq.distinct

The problem being that once you have the char seq result, it becomes extremely awkward to convert this back to a string again, String.concat "" requires that the members are strings, so I end up doing this a lot:

"abcdef01234567" 
|> Seq.take 5
|> Seq.map string
|> String.concat ""

So much so that I have a function I use in 90% of my projects:

let toString : char seq -> string = Seq.map string >> String.concat ""

I feel this is over the top, but everywhere I look to find an alternative I am met with heinous things like StringBuilder or inlining a lambda and using new:

"abcdef01234567" 
|> Seq.take 5
|> Seq.toArray 
|> fun cs -> new string (cs) (* note you cannot just |> string *)

My (perhaps crazy) expectation that I would like to see in the language is that when Seq is used on string, the type signature from the resulting expression should be string -> string. Meaning, what goes in is what comes out. "abcd" |> Seq.take 3 = "abc".

Is there a reason my expectations of high level string manipulation is mistaken in this case?

Does anyone have a recommendation for approaching this in a nice manner, I feel like I must be missing something.

Answer

Johann Hibschman picture Johann Hibschman · May 9, 2014

I was just researching this myself. I found that System.String.Concat works pretty well, e.g.

"abcdef01234567" |> Seq.take 5 |> String.Concat;;

assuming that you've opened System.