Can I use the pipeline operator in F # to pass an argument to a constructor?

This code changes the line:

let reverse (s : string) = new string(s.ToCharArray() |> Array.rev) 

Can this be rewritten using the pipeline operator to pass the required argument to the string() constructor? For example, it looks more idiomatic:

 // Doesn't compile: let reverse (s : string) = s.ToCharArray() |> Array.rev |> new string 

Similarly, why can't I use the string operator as follows?

 let reverse2 (s : string) = s.ToCharArray() |> Array.rev |> string 

Here it is in action:

 > reverse2 "foo" ;; val it : string = "System.Char[]" 

It returns a type, not "oof".

+7
f #
source share
2 answers

No, the pipe operator can only be used with F # functions, it cannot be used with class constructors, member methods, or static methods. The reason is that the overload supported by these types of methods will complicate F # type inference. However, if you really want to use the pipeline, you can map each element of the char array to a string and then pass this sequence to Seq.concat "" :

 s.ToCharArray() |> Array.rev |> Seq.map(string) |> String.concat "" 

Or you can wrap the string constructor call with the F # method:

 let stringCArrCtor (carr: char[]) = new string(carr) s.ToCharArray() |> Array.rev |> stringCArrCtor 

And to answer your last question,

 s.ToCharArray() |> Array.rev |> string 

cannot be used as it is equivalent

 (s.ToCharArray() |> Array.rev).ToString() 

and the Array ToString () method is not overridden, so it just returns the name of the reflected type by default.

+9
source share

As Stephen said, it's best to define a new function that calls the constructor. You can put it in a module called String (in some namespace), so you get the same feeling as when working with other F # functions. I would probably use:

 module String = let create (c:char[]) = new string(c) 

The question of using constructors as first-class values ​​has appeared on SO before, but I can no longer find my earlier answer - there is one very crazy trick that gives you the opportunity, but this is a huge hack (no one should ever use this and some next version of F # surely going to ban it). In any case, you can use statically permitted type parameters to write the following:

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

And use the function as follows:

 "ABC".ToCharArray() |> Array.rev |> ctor<string, _>;; 

The ctor function essentially requires that the type specified as a parameter of the first type has a constructor and calls the constructor (another type parameter is a constructor argument and is output by the compiler). But it's really just curiosity - defining your own function is the best approach.

+9
source share

All Articles