Can I use the pipeline operator in F# to pass an argument to a constructor?
This code reverses a string:
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, this seems more idiomatic:
// Doesn't compile:
let reverse (s : string) = s.ToCharArray() |> Array.rev |> new string
Similarly, why can't I use the string
operator in the following way?
let reverse2 (s : string) = s.ToCharArray() |&开发者_运维技巧gt; Array.rev |> string
Here it is in action:
> reverse2 "foo" ;;
val it : string = "System.Char[]"
It returns the type rather than "oof".
No, the pipe operator may only be used with F# functions, it cannot be used with class constructors, member methods or static methods. The reason being that the overloading supported by these kinds of methods would complicate F#'s type inference. However, if you really want to use piping, you could map each element of the char Array to a String and then pipe that sequence into Seq.concat ""
:
s.ToCharArray() |> Array.rev |> Seq.map(string) |> String.concat ""
Or you could wrap the string constructor call in an 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
can't be used because it is equivalent to
(s.ToCharArray() |> Array.rev).ToString()
and the Array ToString() method is not overridden so it just returns the default reflected type name.
As Stephen mentioned, the best thing to do is to define a new function that calls the constructor. You can place it into a module named String
(in some your namespace), so you'll get similar 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 appeared on SO before, but I cannot find my earlier answer anymore - there is one very crazy trick that gives you the ability, but it is an enormous hack (nobody should ever use it and some next version of F# is hopefuly going to disallow that). Anyway, you can use statically resolved 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 like this:
"ABC".ToCharArray() |> Array.rev |> ctor<string, _>;;
The ctor
function essentially requires that the type specified as the first type parameter has a constructor and it calls the constructor (the other type parameter is the argument to the constructor and is inferred by the compiler). But this is really just a curiosity - defining your own function is the best approach.
精彩评论