Convert a list of characters (or array) to a string
Asked Answered
O

4

28

how does one convert from a list of characters to a string?

To put it another way, how do I reverse List.ofSeq "abcd"?

UPDATE: new System.String (List.ofSeq "abcd" |> List.toArray) |> printfn "%A" seems to work fine, with or without new, but List.ofSeq "abcd" |> List.toArray) |> new System.String |> printfn "%A" fails. Why?

Ogee answered 28/2, 2010 at 17:1 Comment(0)
D
37

I asked a similar question once before. It seems object constructors aren't composable so you can't pass them as a function.

List.ofSeq "abcd" |> List.toArray |> (fun s -> System.String s) |> printfn "%A"
List.ofSeq "abcd" |> List.toArray |> (fun s -> new System.String(s)) |> printfn "%A"

Update Constructors are first-class functions as of F# 4.0

List.ofSeq "abcd" |> List.toArray |> System.String |> printfn "%A"
Dao answered 28/2, 2010 at 17:23 Comment(6)
OK, this limitation makes sense performance-wise (creating a lambda that's probably hard to optimize away can probably hurt performance in way that's difficult to detect). Any pointer on why 'new' is optional? To make creating objects by reflection blend in better with the Ocaml/F# syntax? Thanks!Ogee
Kind of hard to pick one of the answers, I'll pick this one because it references another related question. :-)Ogee
"It seems object constructors aren't composable so you can't pass them as a function." This is finally changing. github.com/fsharp/FSharpLangDesign/blob/master/FSharp-4.0/…Backswept
I get a compiler error when trying the third snippet, it says "Invalid use of a type name and/or object constructor...."Choroiditis
@Choroiditis that error means you are running an older version of F#. You need to update to F# 4.0 which comes with VS 2015.Dao
> Constructors are first-class functions as of F# 4.0 is a gem. Thanks!Carabin
J
15

Working with strings in F# is sometimes a bit uncomfortable. I would probably use the same code as Dario. The F# grammar doesn't allow using constructors as first class functions, so you unfortunately cannot do the whole processing in a single pipeline. In general, you can use static members and instance methods as first class functions, but not instance properties or constructors.

Anyway, there is a really nasty trick you can use to turn a constructor into a function value. I would not recommend actually using it, but I was quite surprised to see that it actually works, so I thought it may be worth sharing it:

let inline ctor< ^R, ^T 
  when ^R : (static member ``.ctor`` : ^T -> ^R)> (arg:^T) =
   (^R : (static member ``.ctor`` : ^T -> ^R) arg)

This defines a function that will be inlined at compile time, which requires that the first type parameter has a constructor that takes a value of the second type parameter. This is specified as a compile-time constraint (because .NET generics cannot express this). Also, F# doesn't allow you to specify this using the usual syntax for specifying constructor constraints (which must take unit as the argument), but you can use the compiled name of constructors. Now you can write for example:

// just like 'new System.Random(10)'
let rnd = ctor<System.Random, _> 10
rnd.Next(10)

And you can also use the result of ctor as first-class function:

let chars = [ 'a'; 'b'; 'c' ]
let str = chars |> Array.ofSeq |> ctor<System.String, _>

As I said, I think this is mainly a curiosity, but a pretty interesting one :-).

Joannejoannes answered 28/2, 2010 at 23:25 Comment(0)
D
6

Your approach:

new System.String (listOfChars |> List.toArray)

is the solution I usually end up with too.

F#'s grammar/type inference system simply seems unable to recognize a .NET constructor like new String as a curried function (which prevents you from using pipelining).

Dives answered 28/2, 2010 at 17:15 Comment(0)
P
2

Just faced similar problem, and came up with this solutions:

List.fold (fun str x -> str + x.ToString()) "" (List.ofSeq "abcd") 
Piet answered 3/2, 2012 at 19:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.