Hacker News new | ask | show | jobs
by jawher 5411 days ago
To the best of my understanding, lambdas-as-objects would mean providing structural types, i.e. generic function types that are able to describe the structure of a lambda (arguments and result type). e.g. (a fictive syntax):

    Function0<Int> f = () => 5
f here is a lambda that take no args and returns an Int. It's type is Function0 (0 for no args) and parameterized with the return type.

Another example with 2 args:

    Function2<String, Float, Int> f = (String name, Float rate) => 5
The Function* types are the generic structural types able to describe lambdas. C# has them, and so does Scala.

Java however decided not to have them, because they don't adhere well with Java's history with nominative types (a type has a name that carries sense). They rather came up with the SAM auto conversion, which is a neat idea in itself, but a bit less flexible than structural types, and may (and will lead to a Cambrian explosion in the number of types, e.g. Reducer, Mapper, Folder, Filter, Predicate, etc.)

What's even worse though is erasure: Java generics are erased at compile time, and this fucks up things really bad with overloading, so in a code like this (in the type List for example):

    public <S> List<T> stupidOpName(Function1<T, S> f) { ... }

    public List<T> stupidOpName(Function1<T, Boolean> f) { ... }
After erasure, we'll end up with :

    public List stupidOpName(Function1 f) { ... }

    public List stupidOpName(Function1 f) { ... }
i.e. 2 methods with the same name and same signature. This is bad. You don't ever want that to happen. And fortunately the java compiler won't let it happen.

So, the alternative is I said SAM (Single Abstract Method) types: no structural types, but lambdas can be automatically converted to a SAM if compatible, so you could do this for example:

    Runnable r = () => { ... }
The lambda gets converted to the Runnable type.
2 comments

> C# has them, and so does Scala.

With the slight difference that its generics are reified, so all `Function` generic arities have the same name (it's `Function<T>`, `Function<T1, T2>`, ...), although because there is no `void` or `Unit` type in C# it needs two base function types, to handle functions with a return value (`Function`) and functions with no return value (`Action`).

I believe the Kotlin project fixes this issue by having a first-class `Unit` type, so C#'s `Action` becomes `Func<Unit>` in Kotlin.

C#'s type system is a bit messy over there. Besides Func<T> and Action<T>, there's also Predicate<T> , which works the same as Func<T, bool> except for being an entirely different type that's non-trivial to cast to Func<T, bool>. And the whole lot look a lot like delegates.

If they were all just Func<T> (or aliases for that, or wrappers over that when multicast is really needed), it would be a lot simpler.

Even Java has the Void type, meaning you can write:

    Function<Void>
It's a bit shady though as you still have to return a value from the body of the method, and that value has to be null.
That sort of this is also easy to do in C# or any strongly typed language. Similar things in c# are the DBNull type which "Represents a nonexistent value" from a database (http://msdn.microsoft.com/en-us/library/system.dbnull.aspx ) and Type.Missing which "Represents a missing value in the Type information"
> It's a bit shady though

'bit of an understatement there.

Are lambdas always created as SAM types or do they have some other form?

I assume there is some way to specify the SAM type explicitly, for cases where it can't be inferred properly?

The answer to your first question is yes, a lambda is always assigned to a SAM type.

As to your second question, yes, you can cast a lambda to explicitly specify it's target SAM type:

    obj.method((Runnable)(()=> println("oh hai !")));