woodings
woodings

Reputation: 7693

F# - Can I use type name as a function which acts as the default constructor?

To create a sequence of my class,

type MyInt(i:int) =
    member this.i = i

[1;2;3] |> Seq.map(fun x->MyInt(x))

where fun x->MyInt(x) seems to be redundant. It would be better if I can write Seq.map(MyInt)

But I cannot. One workaround I can think of is to define a separate function

let myint x = MyInt(x)
[1;2;3] |> Seq.map(myint)

Is there a better way to do this?

Upvotes: 6

Views: 381

Answers (4)

TeaDrivenDev
TeaDrivenDev

Reputation: 6629

To give an update on this - F# 4.0 has upgraded constructors to first-class functions, so they can now be used anywhere a function or method can be used.

Upvotes: 1

Daniel
Daniel

Reputation: 47914

If gratuitous hacks don't bother you, you could do:

///functionize constructor taking one arg
let inline New< ^T, ^U when ^T : (static member ``.ctor`` : ^U -> ^T)> arg =
  (^T : (static member ``.ctor`` : ^U -> ^T) arg)

type MyInt(i: int) =
  member x.i = i

[0..9] |> List.map New<MyInt, _>

EDIT: As kvb pointed out, a simpler (and less hacky) signature can be used:

let inline New x = (^t : (new : ^u -> ^t) x)

Note, this switches the type args around, so it becomes New<_, MyInt>.

Upvotes: 9

Be Brave Be Like Ukraine
Be Brave Be Like Ukraine

Reputation: 7735

I use static methods for this purpose. The reason is that sometimes your object constructor needs two arguments taken from different sources, and my approach allows you to use List.map2:

type NumRange(value, range) =
    static member Make aValue aRange = new NumRange(aValue, aRange)

let result1 = List.map2 NumRange.Make values ranges

Partial application is not prohibited as well:

let result2 =
    values
    |> List.map NumRange.Make
    |> List.map2 id <| ranges

If you dislike using id here, you may use (fun x y -> x y) which is more readable.

Upvotes: 0

pad
pad

Reputation: 41290

In short, no.

Object constructors aren't first-class functions in F#. This is one more reason to not use classes, discriminated unions is better to use here:

type myInt = MyInt of int
let xs = [1;2;3] |> Seq.map MyInt

If you don't like explicit lambdas, sequence expression looks nicer in your example:

let xs = seq { for x in [1;2;3] -> MyInt x }

Alternatively, your workaround is a nice solution.

Upvotes: 7

Related Questions