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() |> Array.rev |> string
Here it is in action:
> reverse2 "foo" ;;
val it : string = "System.Char[]"
It returns the type rather than "oof".
The pipe, %>% , comes from the magrittr package by Stefan Milton Bache. Packages in the tidyverse load %>% for you automatically, so you don't usually load magrittr explicitly.
:: Lists. Match Expressions. Creates a list. The element on the left side is prepended to the list on the right side.
The pipe operator is a special operational function available under the magrittr and dplyr package (basically developed under magrittr), which allows us to pass the result of one function/argument to the other one in sequence. It is generally denoted by symbol %>% in R Programming.
The pipe operator, written as %>% , has been a longstanding feature of the magrittr package for R. It takes the output of one function and passes it into another function as an argument. This allows us to link a sequence of analysis steps.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With