20
\$\begingroup\$

I'll be using Java in this post, but the following also applies to C#, and likely other languages.

We require that submissions must be a first-class function, or a full program to be valid. In Java, many users have started using untyped lambdas as their submission. The problem with untyped lambdas is that they have no meaning without their types.

For example, given a theoretical challenge of Add Two Numbers, in Java, a submission would look like:

(a,b) -> a + b

which seems relatively reasonable. However, the problem lies with the following solution:

(a,b) -> a.add(b)

The problem with the above statement is that we have absolutely no idea which function is getting called. The only way the above statement could ever work in code is if we say in the description that a and b are BigInteger.

To extend this problem further, lets say I'm answering a sorting challenge. You could submit:

list -> list.sortThis()

Now wait, sortThis() isn't a function on arrays, or on ArrayList, or anywhere in the JDK. I'd have you tell you that list is a MutableList from Eclipse Collections for you to ever use the code. Furthermore, if I did use that code, you don't need to import a MutableList. Java is able to deduce the types, no import needed.

We require the submissions be self-contained, but right now, these lambdas are not self-contained, because they require outside input to indicate the types.

How do we solve this problem?

\$\endgroup\$
3
  • 2
    \$\begingroup\$ I'd like to point out VisualMelon's past post about this issue that never really got a well-upvoted answer. \$\endgroup\$ Commented Jan 10, 2017 at 20:07
  • 1
    \$\begingroup\$ Related \$\endgroup\$
    – Poke
    Commented Jan 10, 2017 at 20:27
  • \$\begingroup\$ Closely related. (I still think that question is not a dupe of its dupe target but that's not really relevant here) \$\endgroup\$
    – CAD97
    Commented Jun 9, 2017 at 5:25

4 Answers 4

18
\$\begingroup\$

TL;DR: Lambdas should not require their types.


I think that the lambdas shouldn't require types. A lambda does not need the type to be specified when you assign it to the function. For example, in C#:

Func<int, int, int> f = (a,b) => a + b;

Compiles just fine. In fact I didn't even know you could specify the type in the lambda before I saw this post.

Another reason to allow this is that these languages are never going to beat a golfing language and requiring types only increases the gap. As code compiles just fine without them, there is no need to add them in.

I don't buy the argument about not knowing what function is called. To solve it you can either a) Ask the author or b) Require people state what the anonymous function compiles to:

s=>s.Length

Compiles to a Func<string, int>.

Lastly, if we require types for the inputs it is only logical that we then have to require types for outputs. This makes no sense in the case of a lambda as there is no way to do this without showing what it compiles to. At that point it just becomes shorter to use a normal method and do away with lambdas on PPCG altogether.

\$\endgroup\$
54
  • 2
    \$\begingroup\$ @Mego No it is lambdas should never require types, not sometimes they can, sometimes not. I can show you what it compiles to and s=>l.Length compiles fine so what's the point in adding it for a site about minimising byte count? \$\endgroup\$ Commented Jun 7, 2017 at 15:36
  • 3
    \$\begingroup\$ @Mego Yes it does... with the appropriate context. \$\endgroup\$ Commented Jun 7, 2017 at 15:38
  • 1
    \$\begingroup\$ @NathanMerrill Exactly but then you are telling us what it compiles to, a challenges spec usually states something like Input in any reasonable format, assuming the input is what you compile the lambda to is then a fine assumption surely? If the spec states input must be an int, I shouldn't have to do (int n)=>... we already know it is an int from the challenges specs. \$\endgroup\$ Commented Jun 7, 2017 at 15:48
  • 1
    \$\begingroup\$ @OlivierGrégoire no! Java can deduce the types in that case. \$\endgroup\$ Commented Jun 7, 2017 at 16:01
  • 3
    \$\begingroup\$ Oh, so Java can or cannot? Which is it? You said it can't, but then you say it can. So it depends on context. Exactly what I said from the start. Java is entirely able to contextualize stuff, which is enough to be accepted. \$\endgroup\$ Commented Jun 7, 2017 at 16:03
  • 1
    \$\begingroup\$ @OlivierGrégoire read my post again. If Java can deduce the types from the code you post, then its totally fine. We cannot depend on types outside of our submission. \$\endgroup\$ Commented Jun 7, 2017 at 16:05
  • 1
    \$\begingroup\$ Arrays.asList("0","1","2").stream().map(s->s+"0") is valid, s->s+"0" is not. The first Java can compile just fine. The second, Java has no idea what it means. That + literally has no meaning. \$\endgroup\$ Commented Jun 7, 2017 at 16:07
  • 4
    \$\begingroup\$ @NathanMerrill Yes and it can handle n->n. So I don't see your point. \$\endgroup\$ Commented Jun 7, 2017 at 16:12
  • 2
    \$\begingroup\$ 1. We require submission that are functions or full programs. 2. A valid Java function is typed. 3. If Java doesn't know those types then it isn't a valid Java function. 4. Providing those types in the surrounding code means that the function gains functionality based on the surrounding code. 5. If a function changes depending on the surrounding code, then it isn't self contained. \$\endgroup\$ Commented Jun 7, 2017 at 16:16
  • 3
    \$\begingroup\$ @Mego, you're mainly wrong about C#. Yes, it has dynamic types, but they require using the dynamic keyword where you would otherwise expect the name of a type. There is also type inference in some contexts, but that's compile-time. Type inference is not incompatible with strong static typing. \$\endgroup\$ Commented Jun 7, 2017 at 16:17
  • 2
    \$\begingroup\$ 1. "A lambda does not need the type to be specified when you assign it to the function" is true, but the point of lambdas is that you can use them for things other than assigning them to a variable, so without explicit types they may be ambiguous. I'm on the fence about this point. 2. "Another reason to allow this is that these languages are never going to beat a golfing language and requiring types only increases the gap" on the other hand is just arguing for a handicap system, and discussions on meta have consistently rejected handicapping. \$\endgroup\$ Commented Jun 7, 2017 at 16:22
  • 3
    \$\begingroup\$ We already agreed long time ago that lambdas are accepted. And now when we write lambdas, they're not anymore... I don't know Python, but they have lambdas. If I give an number parameter to an array accepting lambda, it will fail, right? Then, where is it contextualized that the lambda accepts only arrays and not ints? "Oh, but it's in the question". Where in all there is the difference with Java? We use information from the question to contextualize and golf bytes. We can decide to be more strict on the lambda definition, but to force entirely the burden of complete definition on Java is bad. \$\endgroup\$ Commented Jun 7, 2017 at 16:28
  • 5
    \$\begingroup\$ Another argument for it is being pragmatic that all Java golfers use and understand this. And get lots of reputation out of those lambda-based answers. This topic is 5 months old, yet there are everyday lambda answers with 10+ rep in Java, that no one is policing. Java answers mostly compete between them and everybody's happy! Sometimes one is really good and forcing unnecessary burden only annoys everybody. That was the reason for "function or full program". Where is that spirit now? \$\endgroup\$ Commented Jun 7, 2017 at 16:43
  • 6
    \$\begingroup\$ Fun and slightly frustrating read.. First of all I don't get why c=>... in C# might be valid, but c->... in Java would not be, since they are exactly the same. Secondly, I understand the point of not knowing whether n->... is an int, long, byte, etc. without context. BUT, the same can be said about other languages that aren't strongly typed. I see answers ALL the time stating: "input is ...", like input is a list of two Strings in CJam, input is a cell array of strings in MATL. Why is Java/C# stating this any different? \$\endgroup\$ Commented Jun 9, 2017 at 7:59
  • 3
    \$\begingroup\$ @KevinCruijssen I totally agree that it should be true for all languages (even the weakly typed). That said, in strongly typed languages, I'm still of the opinion that n->n+1 has absolutely no meaning without the types. In Python, that is well defined: take an object, and call the __add__ function. In Java it isn't defined at all. So, I can see how we make a difference between the two. \$\endgroup\$ Commented Jun 9, 2017 at 12:37
-1
\$\begingroup\$

Types should be included but not in the bytecount

The main argument is that a person wishing to use the lambda may not know the datatype just by looking at the function since it's implicitly set within the context of the lambda. This is a good argument for requiring that the datatype be specified in the answer... but this is code golf, people; if bytes aren't needed for a line of code to be valid, they're going to be omitted.

It seems like until recently people have been content with the untyped lambdas. This is likely because on PPCG there's almost always a rule where you can assume input is valid. Part of valid input includes the datatype in this example. The part that makes Java or C# different from Python or Javascript is that Java and C# are strongly typed. If you don't have the correct datatype, the code won't compile let alone run. In Python or Javascript you can call a function with any datatype you want... but it will likely error if you use one that's unintended.

Consider a javascript object a = {key: 'value'}. Suppose I expect this format for parameters to my function. In classic javascript fashion I'm able to invoke console.log(a.key); If my program relies on that functionality then it's going to error if you pass in 1.

> 1.key
Uncaught SyntaxError: Invalid or unexpected token

I would need to specify in my answer that input is expected to be in a certain format or datatype but that wouldn't affect my bytecount.

\$\endgroup\$
1
  • \$\begingroup\$ "This is a good argument for requiring that the datatype be specified in the answer" -- I don't think requiring arbitrary additional information based on specific languages is good. But I do think answers should be testable. Sidebar: "until recently" -- doing the wrong thing in the past doesn't make it the right thing in the present. I think this detracts from you idea. \$\endgroup\$ Commented Mar 26, 2017 at 11:44
-2
\$\begingroup\$

Java already allows contextual imports. So contextual information is allowed.

Take the two files:

A.java

import java.util.*;
class A {
  static List<String> list = Arrays.asList("foo");
}

B.java

// NO IMPORTS!!
class B {
  public static void main(String[] args) {
    System.out.println(A.list.get(0));
  }
}

Here, B depends on java.util.List, but java.util.List isn't declared in the imports.

One of my previous answer's one of my previous golfed answer showed that no imports are required in the context of a lambda. You can call it black magic, but it works. Types can be contextual in Java, and Lambda are no exception to this.

Here is the answer, retyped, for clarity of the example:

LookMaNoImports.java

class LookMaNoImports {
  static Main.F f = s -> // transform a Stream<Boolean>
    s.map(               // by applying its map method
      b ->               // which in turns transforms a boolean 
        !b               // by applying its negation.
    );
}

Main.java

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {
    interface F {
        Stream<Boolean> f(Stream<Boolean> s);
    }

    public static void main(String[] args) {
        F f=LookMaNoImports.f;

        test(f, new Boolean[]{true}, new Boolean[]{false});
        test(f, new Boolean[]{false}, new Boolean[]{true});
        test(f, new Boolean[]{true, false}, new Boolean[]{false, true});
        test(f, new Boolean[]{true, true}, new Boolean[]{false, false});
    }

    static void test(F f, Boolean[] param, Boolean[] expected) {
        List<Boolean> result = f.f(Arrays.stream(param)).collect(Collectors.toList());
        if (result.equals(Arrays.asList(expected))) {
            System.out.printf("%s: OK%n", Arrays.toString(param));
        } else {
            System.out.printf("%s: NOT OK, expected %s%n", Arrays.toString(param), Arrays.toString(expected));
        }
    }
}
\$\endgroup\$
6
  • 1
    \$\begingroup\$ So, LookMaNoImports is valid as long as you post both files (and they both count towards your byte count). However, Java needs types to compile, and you need to declare them for your submission to be self contained. \$\endgroup\$ Commented Jun 7, 2017 at 15:24
  • 4
    \$\begingroup\$ I don't really understand how this is an answer to lambdas needing types or not. Yes all required imports are required but that is a different thing to what this post is about. \$\endgroup\$ Commented Jun 7, 2017 at 15:31
  • \$\begingroup\$ @TheLethalCoder No, all imports aren't required. I just showed two examples where it's not the case, including one Lambda. The atom in Java is the class. Classes allow contextual information as shown. Twice. \$\endgroup\$ Commented Jun 7, 2017 at 15:32
  • 1
    \$\begingroup\$ @OlivierGrégoire I don't know Java well but as far as I can tell you have all the required imports, you've just hidden them in a different file. So as far as code golf is concerned you'd need to include the extra file as well into your byte count \$\endgroup\$ Commented Jun 7, 2017 at 15:35
  • \$\begingroup\$ @TheLethalCoder In Java, the unit is the class. The class contains methods. Codegolf says "hey, you can use Java methods or lambdas". So I'm showing Java methods and lambdas as they are written in their unit, the class. Taking rules, combining them. \$\endgroup\$ Commented Jun 7, 2017 at 15:43
  • 1
    \$\begingroup\$ I agree with @TheLethalCoder. When you have a class A and B, you'll have to include both in your answer. Yes, A lack's B's import, but including/excluding imports aren't the only thing that are mandatory when posting answers. One of the forbidden loopholes is to put everything in a file/executable/plugin/etc. and simply call that; or to create a language with a single byte-solution for the exact challenge posted. Hiding code inside another class without including it in your answer is a closely related forbidden loophole. (Also, this question is about function-types, not imports..:S) \$\endgroup\$ Commented Jun 9, 2017 at 7:46
-3
\$\begingroup\$

Require types to be added if a method/function/operator cannot be deduced

This answer only applies to languages with manifest typing.

Java and C# both have the ability to define types in the definition:

(int a, int b) => a + b   // C#
(int a, int b) -> a + b   // Java

This gets immensely useful when dealing with objects:

(BigInteger a, BigInteger b) -> a.add(b)
(MutableList a) -> a.sortThis()

Furthermore, because the above two examples only involve a single function, you can shorten them to:

BigInteger::add    
MutableList::sortThis

Another acceptable format would be:

Function<MutableList, MutableList> f = a -> a.sortThis()

Even though the lambda itself does not contain the type, the method sortThis() can still be deduced.

In all of these examples, imports would still be required because the types BigInteger or MutableList are explicitly listed. The key here is that the submissions are self-contained; They contain all of the type information you need to run.

\$\endgroup\$
9
  • \$\begingroup\$ Other languages have lambda expressions which fail if you provide the wrong types. Why is lamda f: allowed in python but f-> is banned in Java? It's the same thing. If you write lambda f:f.sort() in python, it will fail if you pass it an int. If you write f->java.util.Arrays.sort(f), it will also fail if you pass it an int. \$\endgroup\$
    – Pavel
    Commented Jan 10, 2017 at 21:31
  • 5
    \$\begingroup\$ The key difference here is that Java is manifestly typed. Python doesn't require that the definition exists (you could simply implement __attr__). However, for a Java program to be compilable, that type must declared somewhere. We are simply requiring that the submission has that type. \$\endgroup\$ Commented Jan 10, 2017 at 21:47
  • 1
    \$\begingroup\$ Yeah, but the type doesn't have to be declared. For example, the function castToDouble() take an expression in the form of n->n as a parameter without the need for a type declaration. \$\endgroup\$
    – Pavel
    Commented Jan 10, 2017 at 21:59
  • \$\begingroup\$ The type is still required: it's just located wherever you declare the type of the variable being passed in. I'm simply moving that type to the lambda \$\endgroup\$ Commented Jan 10, 2017 at 22:02
  • \$\begingroup\$ Maybe I'm not understanding the situation, but shouldn't I expect the types of the inputs to be declared in whatever hypothetical test framework is being used to test the function? \$\endgroup\$
    – xnor
    Commented Jan 11, 2017 at 0:19
  • 3
    \$\begingroup\$ Yes, but the difference is that the lambda depends on those types being declared. It isn't standalone, because it is obtaining functionality based on surrounding code \$\endgroup\$ Commented Jan 11, 2017 at 0:23
  • \$\begingroup\$ Is there an actual C# example where the type cannot be deduced since you can just use dynamic to cover almost everything? Off the top of my head, only pointer types that specifically use the pointer operators can't be dynamic in which case you can deduce it's a pointer type. \$\endgroup\$
    – milk
    Commented Jan 12, 2017 at 19:01
  • \$\begingroup\$ I don't know C# very well, but I imagine that a C# lambda would only compile if it knew the types of the variables or that the variables were declared dynamic. Meaning that you still have the dependency to outside code. \$\endgroup\$ Commented Jan 12, 2017 at 19:58
  • 1
    \$\begingroup\$ @milk C# will never assume dynamic unless it's the natural consequence of the type inference, but you can indeed write almost anything in a lambda as long as you assign it to a lambda with the right number of (dynamic) parameters. C# also never guess pointer types (even if it could in theory know it's a pointer, it has no idea what type it points to). \$\endgroup\$ Commented Jan 12, 2017 at 22:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .