Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# use constructors as functions

I have a use case for treating a constructor for a derived type as a delegate and I can't figure out if it's impossible or I'm just incapable of working it out.

type SomeJobEvent(jobId : int, otherThing : string) =
    member this.JobId = jobId
    member this.OtherThing = otherThing

type SomeJobStarted(jobId : int, otherThing : string) =
    inherit SomeJobEvent(jobId, otherThing)

type SomeJobComplete(jobId : int, otherThing : string) =
    inherit SomeJobEvent(jobId, otherThing)

type SomeJobError(jobId : int, otherThing : string) =
    inherit SomeJobEvent(jobId, otherThing)

Lets imagine that this is the model, in real life the model happens to be in C# and my code is in F#, but for brevity it's much easier to type out in F#.

And what I want to do is...

let raise eventObject = 
    // This is just a helper, the raise event takes obj.
    eventRaiser.Raise(eventObject)

let raise jobId otherThing eventConstructor = 
    // Lets treat the 'event' constructor as a function
    raise(eventConstructor(jobId, otherThing))

[<EntryPoint>]
let main args = 

    // Lets curry this function up so I don't have to pass around 1234
    let raiseEventForJob1234 = raise 1234 "other thing"

    raiseEventForJob1234 SomeJobStarted

    raiseEventForJob1234 SomeJobComplete

Now as the constructors passed into the raiseEventForJob1234 have the same signature and are part of the same inheritance chain, it feels possible. It's just I am not sure how to make it work, it's not even that they all quack, they are actually ducks!

Edit: There is a great answer here from @tomas, but also a really useful extension from Piaste in the comments make sure you check out both.

like image 674
tigerswithguitars Avatar asked Mar 12 '23 05:03

tigerswithguitars


1 Answers

The problem you are getting here is called value restriction - basically, if you want to use function as generic, it has to be defined as an explicit function.

In your case raiseEventForJob1234 is generic (it can create and trigger different types of events), but it is defined as a value. Adding an argument solves this:

let raiseEventForJob1234 f = raise 1234 "other thing" f

raiseEventForJob1234 SomeJobStarted
raiseEventForJob1234 SomeJobComplete

Also note that this only works in F# 4.0 (in Visual Studio 2015). Previous version of F# did not support treating constructors as functions.

like image 154
Tomas Petricek Avatar answered Mar 27 '23 07:03

Tomas Petricek