Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extending or implementing Function should be legal #7780

Closed
DartBot opened this issue Jan 8, 2013 · 11 comments
Closed

Extending or implementing Function should be legal #7780

DartBot opened this issue Jan 8, 2013 · 11 comments
Assignees
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language).

Comments

@DartBot
Copy link

DartBot commented Jan 8, 2013

This issue was originally filed by @a14n


According to Gilad Bracha in https://groups.google.com/a/dartlang.org/d/msg/misc/U7HGNg9UYGU/_qERSLxjyJcJ , it should be legal to extend or implement Function.

However, in Dart Editor 0.2.10_r16761 I get errors
 - "'Function' can not be used as superclass" when I try to extend Function
 - "'Function' can not be used as superinterface" when I try to implement Function.

The same kind of error appears when I try to launch in dartium or compile with dart2js.

@lrhn
Copy link
Member

lrhn commented Jan 9, 2013

I think this is actually the most reasonable behavior. There is no good reason to implement Function.
It should at least be a warning if you implement Function without declaring a call method, and if you do implement a call method, you don't need to explicitly implement Function.

I'd prefer this to be fixed in the spec rather than in the implementations (but the language group are ofcourse free to disagree).


Added Area-Language, Triaged labels.

@DartBot
Copy link
Author

DartBot commented Jan 9, 2013

This comment was originally written by @a14n


Thanks for your answer.
I agree when a call method is defined but when call method is implemented by noSuchMethod I need to implement or extend Function otherwise the Editor complains about "xxx is not a function type".

For instance in the following example I simulate varargs :

  typedef dynamic OnCall(List);
  
  class VarargsFunction /extends Function/ {
    OnCall _onCall;
    VarargsFunction(this._onCall);
    noSuchMethod(InvocationMirror invocation) {
      return _onCall(invocation.positionalArguments);
    }
  }
  
  main(){
    final superHeroes = new VarargsFunction((arguments) {
      for (final superHero in arguments) {
        print("There's no stopping ${superHero}");
      }
    });
    superHeroes('UberMan', 'Exceptional Woman', 'The Hunk');
  }

Here the Editor puts a warning '"VarargsFunction" is not a function type' on "superHeroes(...)".

@lrhn
Copy link
Member

lrhn commented Jan 10, 2013

The problem is that it is not a function.
Neither is a class with a field named "call" holding a function value.

Even if you implement Function, you don't implement any actual function type. Example:
  typedef int Foo(int x);
  Foo bar = new myClassUsingNoSuchMethod();
This will fail in checked mode, even if you implement noSuchMethod and extend Function, and it will only work in unchecked mode. There is no way around that short of actually implementing a "call" method.

I can see that using Function.apply, which is functionality not otherwise available, is hard to do in checked mode, even when using otherwise unchecked variables. We could probably change it to not require a Function, but then I fear it will mask other real errors.

On the other hand, making it possible to extend/implement Function is pretty safe. It only breaks something for objects that do so but aren't functions, and they're asking for it.

So, I can see the problem. I'm not yet convinced that a cure is worth its side-effects.


Added Accepted label.

@gbracha
Copy link
Contributor

gbracha commented Jan 22, 2013

Preventing the implementation (or subclassing) of Function is not supported by the spec. It is true that implementing it a pure documentation gesture. I don't see any reason to change the spec however.

@justinfagnani
Copy link
Contributor

Whether it's implementing Function, so that noSuchMethod can be used to catch call() invocations, or it's some other method of allowing the creation of a function that can be used with any invocation, this would be really useful for framework developers, testing, and other cases where generic wrappers for functions are needed. We could get rid of all the expectAsyncX() methods in unittest for example.


Added C1 label.

@DartBot
Copy link
Author

DartBot commented Jul 18, 2013

This comment was originally written by avix...@gmail.com


This feature is extremely important for enabling the FP style in Dart. Without it implementing any generic function decorator is impossible in the checked mode.

The partial function is one example of such a decorator:

    func(pos1, pos2) => "$pos1 $pos2";
    var p = partial(func, ["pos1"]);
    expect(p("pos2"), equals("pos1 pos2"));

The only way to implement it right now is the following:

    partial(Function func, List args) => new PartiallyAppliedFunction(func, args);

    class PartiallyAppliedFunction {
      Function func;
      List posArgs;

      PartiallyAppliedFunction(this.func, this.posArgs);

       noSuchMethod(Invocation c) => c.memberName == s("call") ? _applyFunction(c) : super.noSuchMethod(c);

       _applyFunction(Invocation c){
          var pos = []..addAll(posArgs)..addAll(c.positionalArguments);
          return Function.apply(func, pos);
       }
    }

The problem here is that an instance of PartiallyAppliedFunction is not a function, which means that:

expect(_.partial(func, ["pos1"]) is Function, equals(false));

Obviously, the partial function is not the only example. Another one:

var funcWhereArgsMustBePresent = ensureArgsArePresent(func);
funcWhereArgsMustBePresent("one", null) // throws

@gbracha
Copy link
Contributor

gbracha commented Jul 18, 2013

Just to be totally clear:

Implementing Function has always been allowed by the spec. Any class that implements call is a subtype of Function. In fact, it is a subtype of the function type F, where F is the function type of its call method. See section 15.5 of the spec. If this is not working, then the implementation has a bug and it should be fixed.

@DartBot
Copy link
Author

DartBot commented Jul 18, 2013

This comment was originally written by avix10...@gmail.com


I didn't know that, but implementing call fixes the issue with "is Function" not being true. It is a little bit awkward, cause you have to implement both call and noSuchMethod.

    class PartiallyAppliedFunction {
      Function func;
      List posArgs;

      PartiallyAppliedFunction(this.func, this.posArgs);

       call() => Function.apply(func, posArgs);
       noSuchMethod(Invocation c) => c.memberName == s("call") ? _applyFunction(c) : super.noSuchMethod(c);

       _applyFunction(Invocation c){
          var pos = []..addAll(posArgs)..addAll(c.positionalArguments);
          return Function.apply(func, pos);
       }
    }

but it is not a big deal.

However, it looks like checks against typedefs still don't work.

@justinfagnani
Copy link
Contributor

Any update on this?

I have a case currently where we're deciding on a typedef or single-method interface for an API. I'd like to use the typedef, and it's great that a class can "implement" the typedef via call(), but one of the arguments for the interface is that the interface allows documentation via the implements clause, where a typedef doesn't.

I'm still surprised that this isn't legal:

typedef T Formatter<T>(LogRecord record);

class MyFormatter implements Formatter<String> {
  String call(LogRecord r) => "foo";
}

@gbracha
Copy link
Contributor

gbracha commented Oct 15, 2013

The intent here seems to have morphed a bit over time. I believe the current request is to be able to treat function types as interfaces so that they can be use in implements clauses. This has some benefits for documentation and typechecking. However, mixing function types with interfaces has some issues. Today interfaces are only derived from classes. A function type is not a class, and we don't necessarily want people to be able to extend them etc.


Set owner to @gbracha.

@DartBot
Copy link
Author

DartBot commented Apr 9, 2015

This comment was originally written by @chalin


Btw, this issue (at least the original topic of this issue), has been resolved: a class can extend or implement Function, and a static warning is issued if it does so without defining call() or noSuchMethod().

Hence this issue should probably be closed with a new issue open to track the new request.

@DartBot DartBot added Type-Defect area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). labels Apr 9, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language).
Projects
None yet
Development

No branches or pull requests

5 participants