0

Constructors are just functions

 3 years ago
source link: https://www.compositional-it.com/news-blog/constructors-are-just-functions/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

In this post, I would like to compare and contrast constructors and functions from both an OO and FP perspective.

Constructors in the OO World

In the OO world, we create instances of classes through a constructor, which optionally can take in a number of arguments; typically those arguments will be stored as state within the class instance. Once we have that instance, we can call methods on that object, whose behaviour typically depends on the constructor arguments.

public class ReportGenerator {
    private IPrinter _printer;
    public ReportGenerator (IPrinter printer) {
        _printer = printer;
    }

    public void Generate(ReportParams args) {
        var report = // report generation code elided...
        printer.Print(report);
    }
}

// Usage
var generator = new ReportGenerator(inkjetPrinter);
generator.Generate(args);

Functions in the FP World

In the FP world, we tend to define classes less often; instead, we rely on (typically stateless) functions. Let's compare the class definition above with a function instead, using F#.

let generate (printer:IPrinter, args:ReportParams) =
    let report = // report generation code elided...
    printer.Print(report)

// Usage
generate (inkjetPrinter, reportParams)

Partial application

The issue with the function above is that there is no way to "re-use" the first argument. Every time we want to call the function, we have to provide both values. F# provides a solution to this with a feature known as partial application (another related term to this is currying). Using partial function application, we can re-write our function as follows:

let generate (printer:IPrinter) (args:ReportParams) =
    ...

Now we can use it like this:

let generateWithInkjet = generate inkjetPrinter // gives back a new function that only requires the second arg
generateWithInkjet reportParams

Partial functions with classes

C# doesn't support partial function application natively like F#, but we can do the same thing using lambda syntax:

static class ReportGenerator
{
    public static Func<IPrinter, Action<ReportParams>> generate =
        printer => reportParams =>
        {
            // do stuff
            printer.Print(reportParams);
        };
}

// Usage
ReportGenerator.generate(inkjetPrinter)(reportParams);

Hopefully you can now see the similarity with what we started with except with a slightly different syntax: A constructor is just a method. Think about it: a constructor can have optional arguments, and be overloaded, just like other methods. However, a constructor cannot return void, but must return a list of at least one or one other functions (or in OO terms, an object with named methods) - just like a Factory.

Constructors as functions

As if to prove the above assertion, F# treats constructors of objects just like a normal function - you normally don't even need to use the new keyword:

let generator = ReportGenerator (inkjetPrinter)
generator.Generate (report)

Conclusion

This post has shown a different way of thinking of constructors - rather than as some "special" language feature, you can just think of them as a function that provides state to another function. For single-method classes, this is a direct mapping to F# functions with partial application.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK