开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜