Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

f#

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".

like image 854
royco Avatar asked Nov 06 '10 03:11

royco


People also ask

In which package the pipe operator %>% is available?

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.

What is :: in F#?

:: Lists. Match Expressions. Creates a list. The element on the left side is prepended to the list on the right side.

How to write pipe operator in R?

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.

What is true about pipe %>% operator in R?

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.


2 Answers

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.

like image 148
Stephen Swensen Avatar answered Oct 08 '22 21:10

Stephen Swensen


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.

like image 24
Tomas Petricek Avatar answered Oct 08 '22 20:10

Tomas Petricek