5

Some languages (Javascript, Python) have the notion that a function is an object:

//Javascript
var fn = console.log;

This means that functions can be treated like any other object (first-class functions), e.g. passed in as an argument to another function:

var invoker = function(toInvoke) {
    toInvoke();
};
invoker(fn); //will call console.log

Other languages (C++, C#, VB.NET) do not define functions as real objects:

//C#
Type t = Console.WriteLine.GetType();
//This code will not compile, because:
//"'Console.WriteLine()' is a method, which is not valid in the given context"

Rather these languages may have objects which can point to a function (such as C++ function pointers) and can be passed around just like any other object. In the CLI, these wrapper objects are called delegates or delegate instances:

//C#
void Invoker(Action toInvoke) {
    toInvoke();
}

Action action = Console.WriteLine;
Invoker(action);

//also valid, and the toInvoke argument will now contain a delegate which points to Console.WriteLine
//Invoker(Console.WriteLine);

What differences in capability arise from these two mechanisms -- "function object" vs "pointer-to-function as object"?

  • I'm pretty sure you COULD write var f = Console.WriteLine (note the dropped parenthesis), although I'm not sure type inference works here, you might have to specify the type as Action<string>, or similar depending on the overload you want. – sara Mar 29 '16 at 6:59
  • 1
    update: I checked, and Action<string> f = Console.WriteLine; works just fine. you can also send methods as parameters to other methods like this (a central piece in LINQ). e.g. bool Predicate(A a) { return true; } new A[] {}.Where(Predicate); – sara Mar 29 '16 at 7:04
  • @kai The assignment of Console.WriteLine (to either to a variable or a parameter) creates a delegate instance which internally points to Console.WriteLine. The method itself is not being passed. Otherwise, if Action<string> f2 = Console.WriteLine;, why does Object.ReferenceEquals(f,f2); return false? – Zev Spitz Mar 29 '16 at 7:41
  • @kai The same is true of .Where -- the Predicate method is being wrapped in a delegate instance which has its own properties and methods. You cannot write var a = Predicate.Target; but you can write Func<A,bool> predicateDelegate = Predicate; var a = predicateDelegate.Target; – Zev Spitz Mar 29 '16 at 7:54
4

The two pieces of code are not equivalent to each other. A language can be implemented in a way so that each method can be directly used as a first class object. This impacts the ABI, calling convention, and linking mechanism, but is not extraordinarily special. Most modern language implementations already attach a lot of metadata to each function.

However, the meaning of object.method differs substantially between its use in delegate assignment vs its use in simple variable assignment:

  • In C#, the code Func<…> m = obj.method; m() is equivalent to obj.method(). That is, a Func<…> some kind of object that knows which object it belongs to (i.e. the method is “bound” to a specific object). This is exactly equivalent to a closure. The resulting Func<…> must therefore remember both the method and the target object. Since a method may be bound to more than one object, each binding results in a new value.

  • In contrast, obj.method in JavaScript merely resolves the method without binding it to an object. We have to do that ourselves: obj.method() would be equivalent to var m = obj.method.bind(obj); m(). You will see that binding a method to different objects will result in values that are not equal to one another, while obviously the unbound method is identical.

In general, I prefer the C# approach, where there is equivalent semantics between obj.method() and all available usages of obj.method. On the other hand, in Javascript, obj.method() introduces different semantics from the similar obj.method.

  • 2
    As much as I like Javascript, I have to agree here. In general I only do obj.method when I know the method is pure/stateless (i.e. has no this references) and thus safe to pass around without anyone having to keep track of what it's bound to. – Ixrec Mar 25 '16 at 10:58
  • @ZevSpitz good point, as the semantics of C# aren't quite clear to me. I've edited the sentence to refer to Func instances instead (which are equivalent to delegates? Again, I'm not sure of the implementation details, though I understand and explain in my answer the underlying language design and language implementation concepts.) – amon Mar 26 '16 at 21:05
  • @ZevSpitz My answer assumes that your question stems from seeing similar syntax in two languages that actually has dissimilar behaviour. (Also, from different concepts of equality.) It is therefore important that the syntax in question obj.method continues to be mentioned in the answer. Your suggested edit was a good summary of my answer, but removed a couple of important concepts. Incidentally, there is no occurrence of the expression obj.method in a C#-but-not-delegate context in my answer. – amon Mar 28 '16 at 17:53
  • @amon My question doesn't stem from syntax; the question is which behaviors arise from different models of first-class functions -- Javascript has one, and C#/VB.NET have another. I have tried to clarify my question; also please see my answer. – Zev Spitz Mar 28 '16 at 20:22
2

I see two areas of different behavior that arise from the two mechanisms:

  1. The pointer object is a separate object from the referenced function, and will have a separate identity from the function, and from other pointers to the function
  2. The pointer object can have behaviors beyond that of the original function. For example, in the CLI:
    • Delegate instances know about the relevant this (and can bind it to the method)
    • Delegate instances can contain pointers to multiple functions

(Note: This is from my experience in C# / VB.NET and Javascript. Other languages may have different variants of either mechanism.)

Object reference equality

If a function is a "real object" then any variables pointing to the function are actually pointing to the same object:

//Javascript
var fn = console.log;
var fn1 = console.log;
console.log(fn === fn1); //prints true

Pointer objects have their own identity, even when both point to the same function:

//C#
Action action = Console.WriteLine;
Action action2 = Console.WriteLine;
Console.WriteLine(Object.ReferenceEquals(action, action2)); //prints False

Additional behaviors of the pointer object

Target binding

Function objects have no knowledge of the class to which they are attached:

//Javascript
var a1 = {
    data: 5,
    writeData: function() {
        'use strict'; //otherwise `this` would be the global object; `this.data` would probably return `undefined`
        console.log(this.data);
    }
};

var action = a1.writeData;
action(); //Uncaught TypeError: Cannot read property 'data' of undefined

Therefore, part of calling the function as an instance method, is the implict binding of this within the function to the object:

a1.writeData(); //prints 5

We can also explicitly bind this with bind, apply, or call:

action = a1.writeData.bind(a1);
action(); //prints 5

However (as @amon pointed out in this answer), the delegate instance retains that information:

//C#
public class A {
    public int Data;
    public void WriteData() {
        Console.WriteLine(this.Data);
    }
}

var a1 = new A() {Data=4};
Action action = a1.WriteData;

because action contains knowledge of the target of methods:

Console.WriteLine(action.Target == a1); //prints True

Multicast delegate

Javascript variables / properties that refer to a function object, work just like references to any other object, and therefore cannot refer to multiple function objects simultaneously.

On the other hand, a delegate instance in .NET can point to multiple functions:

//C#
public static class Writers {
    public static void WriteOne() {
        Console.WriteLine(1);
    }
    public static void WriteTwo() {
        Console.WriteLine(2);
    }
}

action = Writers.WriteOne;
action += Writers.WriteTwo;
action(); //prints 1, and then prints 2
0

While there are differences as you note, it is perhaps important to consider that a closure (i.e. a function with captured locally scoped variables, as for example exists in Javascript) is computationally equivalent to an object with a single method. From the perspective of what can be done with them, at least on a theoretical level, there is no difference between them. In practice, there are two categories of actual difference:

  • Syntactic differences (i.e. how they interact with language features such as how they are created, whether they need special syntax to invoke, what operations -- such as function composition -- can be performed automatically on them without having to write adapters, and so on)

  • Compatibility differences (i.e. whether or not they can interact cleanly with preexisting code).

Both classes of difference can be overcome by writing trivial adapters that convert either between a closure or an object as required.

  • computationally equivalent -- except that in Javascript, this is not bound based on the local scope, but rather depends on the form of the invocation. In contrast, a method defined on an object (in C#/VB.NET) has a this binding which derives from the local scope. – Zev Spitz Jul 8 '16 at 11:15
  • closure === object with single method -- This is precisely how closures are implemented in .NET -- as instances of internal classes with fields holding the values of the local variables, and the closure as a method on this object. – Zev Spitz Jul 8 '16 at 11:18
  • I don't understand the points you are trying to make. Could you illustrate with some example code? – Zev Spitz Jul 8 '16 at 11:21
  • @ZevSpitz - right, but this just means that in Javascript, this is simply a parameter that has a different syntax for specifying it from the rest. You can control this to ensure it has whatever value you really want, you just need to do a bit of extra work. Computationally equivalent doesn't necessarily mean that it's as easy to do things, just that you can do them. – Jules Jul 8 '16 at 11:22
  • this is simply a parameter that has a different syntax for specifying it -- In other words, you are saying that specifying this is conceptually equivalent to setting the value of the local variable which the function closes over? – Zev Spitz Jul 8 '16 at 11:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.