-
Notifications
You must be signed in to change notification settings - Fork 203
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
Add the concept of Functional Interface to adopt the concept of Higher Order Function #3889
Comments
Not that I'm qualified to make a decision here, but wouldn't we want to have as few ways as possible to express the exact same idea? If functional interfaces introduced new features over |
@Levi-Lesches Greetings friend, the point is that If we review the reason why the The reason why This is how in Typescript programmers use the We need to implement solutions like these to improve performance, also so that projects developed in Dart have greater robust consistency and can age in a good way like Java or C# projects that are more than 15 years old, and that continue to work to date, This also allows the SOLID principles to be permanently implemented. Thanks to this we can implement abstractions through interfaces that are durable over time. I think the oversimplification of syntax does not generate anything positive. I think it generates ambiguities and poor maintainability. I think that if this is approved there will be no need to choose between one or the other because |
You're asking for a Java feature which was created to mimic a feature Dart properly supports. |
Dart already have higher order functions as first-class values, so there is no introduction.
I don't think
As I read this, the only value proposition is to allow function values to be assignable to interface types with a single abstract method, if that methd has a compatible function type. The benefit is that the interface type could have other methods too. That is, a functional interface like: functional interface class Foo {
void log(String text);
void logNamed(String name String text) { log("$name: $text"); }
} would be assignable from a function type like It would be like the class had an implicit constructor, and field and implementation of the abstract function like: abstract interface class Foo {
final void Function(String) _$log;
/*implicit*/ _Foo$fromFunction(this._$log);
void log(String text) => _$log(text);
void logNamed(String name String text) { log("$name: $text"); }
} that got used to wrap a function when it was assigned to the interface type. Maybe the interface can be assigned to a function type too, implicitly tearing off the Not seeing a big value-add here. Today you could also use an extension type: extension type Foo(void Function(String) _log) {
void log(String name) { _log(name); }
void logNamed(String name String text) { log("$name: $text"); }
} which would allow easy wrapping, I'm not seeing any actual problem that exists today being solved here. It's not introducing higher order functions. Dart has had those since day 1.
That's ... words. Would this feature be solving any concrete problem that you can describe? And how will it be better than what Dart has today, or could have with features that have a similar implementation cost to this one? |
This is a fine way to start a comment, but then you didn't bring up any actual differences between the two!
I don't know, I don't think comparing the "reasons" why a feature was created to be a very useful exercise compared to looking at the feature as it exists today.
I'm not sure what you mean when you say "dynamic" here -- dynamic already has a meaning in Dart, usually associated with runtime behavior. Neither typedef ListOfNumbers = List<num>;
void main() {
final ListOfNumbers nums = [1, 2, 3];
print(nums.runtimeType); // List<num>
} And they're not wildcards either -- you have to explicitly declare exactly what your types or extension methods are before using them. They're both very specific features you have to work with at compile-time. I don't see how functional interfaces aren't the same.
Again, you haven't really explained why they're different. As lrhn says, you can do everything you're describing today, and I think in general with the way you're using |
@lrhn This is exactly this, it is having the ability to assign a function to an interface of a single abstract method that has exactly the same type signature. This would allow us to standardize some functions that exist in the base SDK with the concepts of Consumer, Predicate BiConsumer, among others. others. This allows you to avoid repeated code because you can reuse the same functional interface that maintains the same type signature, avoiding declaring the same thing with a different name. In addition, they will have other functions with a body that serve as support to generate composition of functions or any other functionality that is useful for the functional interface. All this allows us to move from the functional world to the world of objects in a transparent way, that is the balance I am talking about. Returning to the question of what problem am I solving? I could be solving a problem of:
This is all about type signature compatibility between functions and objects, something fundamental to using the functional programming paradigm. |
@lrhn If extension types on |
@lrhn @mmcdon20 Greetings on this new day, I think what you are proposing is interesting and I believe that both proposals are necessary and are not mutually exclusive for the following reasons: For the In the event that the existing object-oriented design in the base SDK with the |
The direct assignability, and probably the implicit implementation, is the new thing. I'm not absolutely sure that syntax is a bad thing. So, let's assume we introduce the feature, with a syntax reminiscent of extension types. functional class Foo(this.print) extendsSuper {
void print(String text);
// Other members
} which desugars to (using the macro-related augment feature): class Foo {
void Function(String) _$print;
Foo(void Function(String) print) : _$print = print; // Implicit constructor.
void print(String text);
// members
augment void print(String text) => _$print(text);
} and with implicit assignment between Foo foo = print;
void Function(String) func = foo; effectively desugars to: Foo foo = Foo(print);
void Function(String) func = foo._$print; Why only one function, though. If you could similarly define: functional class Bar(this.log, [this.namedLog]) {
void log(String text);
void namedLog(String name, String text) => log("$name: $text");
} and that effectively desugars to: class Bar(this.log, [this.namedLog]) {
final void Function(String) _$log;
final void Function(String, String)? _$namedLog;
Bar(void Function(String) log , [void Function(String, String)? _$namedLog])
: _$log = log, _$namedLog = namedLog;
void log(String text);
void namedLog(String name, String text) => log("$name: $text");
sugment void log(String text) => _$log(text);
augment void namedLog(String name, String text) =>
_$namedLog == null ? augmented(name, text) : _$namedLog(name, text);
} That is, it's a general way to allow abstract signatures to be implemented using functions. The assignability is less obvious here, though. Something like But again, I'm not sure the assignability is selling itself to me. @functional
class Foo {
void print(String text);
// members, with no constructors and no `call` method.
}
// Generated by `@functional` macro:
augment class Foo {
void Function(String) _$print;
implicit Foo(void Function(String) print) : _$print = print; // Implicit constructor.
augment void print(String text) => _$print(text);
void call(String text) => _$print(text);
} That is, all you need for this is macros and implicit constructors, then you can create everything needed, the macros for the implicit code generation, the Can with for an extension type too, since this is all about an object whose entire state is defined by a function (or potentially more functions): @functional
extension type Qux._(void Function(String) _) {
void log(String text);
// members
} generates: augment extension type Qux {
implict Qux(void Function(String text) log) : this._(log);
augment void log(String text) => _(text);
void call(String text) => _(text);
} The entire concept of a "functional interface" isn't needed here. It's the operational behavior which matters: Will have to figure out how generics work, but that's mainly for assignment to the type, in which case it's just a problem that implicit constructors need to handle in general. (There is no chance that a class type would be a subtype of a function type. It's not impossible that we could allow extension types to "implement" function types and record types, but that will require some twiddling with the upper-bound algorithm first, maybe a generic variance feature too to really make it useful.) |
From what I see it is trying to replicate this change through a macro which could simulate the behavior, I have come to the conclusion that dart is not prepared for this because we need other concepts to achieve functional interfaces. We need the ability to create anonymous classes. This is common in Java. Let's see an example: public interface Runnable {
void run();
}
public class Example {
public static void main(String[] args) {
String s = "Hello world!";
// declarate
Runnable r = new Runnable() {
//Assumes an implicit constructor with no arguments "Runnable()"
@Override
public void run() {
System.out.println(s);
}
};
// call
r.run();
}
} This syntax is simplified using a Lambda expression public interface Runnable {
void run();
}
public class Example {
public static void main(String[] args) {
String s = "Hello world!";
// declarate
Runnable r = () -> System.out.println(s);
// call
r.run();
}
} If we try to replicate this in the Dart language syntax it would look like this abstract interface class Runnable {
void run();
}
void main() {
String s = "Hello world!";
// declarate
Runnable r = Runnable() {
//Assumes an implicit constructor with no arguments "Runnable()"
@override
void run() {
print(s);
}
}
// call
r.run();
} This syntax could be simplified like this abstract interface class Runnable {
void run();
}
void main() {
String s = "Hello world!";
// declarate
Runnable r = () => print(s);
// call
r.run();
} This leads me to the conclusion that Dart is not prepared for this because to represent higher order functions the type To resolve this issue we must first decide whether to open another issue to introduce the anonymous classes and then return here or simply close it and not implement this or any alternative that you propose I think that functional interfaces are a very necessary feature in the Dart language, we must assume the functional programming paradigm is what all the competition is doing and they have been trying for years and many languages already have these characteristics |
According to the proposed form we could have a generic abstract interface class Function<T, S> {
S apply(T value);
}
void main() {
// declarate
Function<int, int> fn = (n) => n + 2;
// call
int result = fn.apply(10);
// result
print(result); //print: 12
} |
I don't see how this can represent any existing function. What about functions with more than one argument? What about functions with named arguments? The existing int add(int a, int b) => a + b;
void greet({String name = 'World'}) => print('Hello $name!');
void main() {
int Function(int, int) function1 = add;
void Function({String name}) function2 = greet;
} |
@mmcdon20 I think you are right, I must be more specific, this represents all anonymous functions or lambda expressions with a single parameter that have the following form: (n) => print(n + 2);
(n) {
print(n + 2);
} To represent any other type of function, the type I am only referring to anonymous functions or callbacks, the |
Something important that I have not mentioned is that abstract interface class Runnable {
void run();
}
class Example implements Runnable {
void run() {
print("hello dart");
}
}
void main() {
// Example 1
Runnable fn = Example();
fn.run(); //print: hello dart
// Example 1
Runnable fn2 = () => print("hello flutter");
fn2.run(); //print: hello flutter
} The flexibility of this new feature is essential for a good development experience |
You can already represent this subset of typedef Callback<T, S> = S Function(T); dart already has some support for treating objects like functions in the form of callable objects. This allows you to call an object like a class Counter {
int _count = 0;
int call() => ++_count;
}
void main() {
// you can call an object with a call method
Counter c = Counter();
print(c());
print(c());
// you can assign a callable object to a function type
int Function() f = Counter(); // implicit tear-off on .call
print(f());
print(f());
// you can not assign a function type to a callable class
int count = 0;
Counter x = () => ++count; // error
} |
I think that this debate has become very extensive, I think that the team already has enough information for them to make a decision or if they have any questions they can ask, we should think about how to close the thread Regarding callable objects, it is a way that exists, the point is the lack of standardization with a good object-oriented design to access a callable object should be done through an Interfaces are necessary for a good object-oriented design and long-term maintainability. This simplifies the work of the maintenance team and allows greater scalability, eliminating technical debt among other benefits. I think I'm already taking up your valuable time in tribal conversations, greetings and grateful for everything. |
No plans to implement this. |
Greetings, today I bring you a proposal to bring functional characteristics to the syntax of the Dart language, for this we must explain some things to get into context.
What is a functional interface?
A functional interface is a concept invented for Java 8 that allowed the introduction of Lambda expressions to the language, something fundamental to adopt functional programming.
The concept of a functional interface is that any interface that has a single abstract method can be considered a functional interface that allows it to be declared as a higher order type similar to the
typedef
to which a lambda function can be assigned. I will show you an example in code.The jdk provides several functional interfaces in our case we will only talk about Comsumer
Use the functional interface
What is a Higher Order Function?
It is the ability of a programming language that supports functional programming to declare functions as variables, they can also be passed as a parameter to another function or that a function returns another function. You can research more about this on the internet.
What problem do we want to solve?
The introduction of functional interfaces will allow the Dart language to introduce higher order functions in the Dart language, as an alternative to the
typedef
, something fundamental for functional programming.Another effect that functional interfaces bring is to create a balance between paradigms since it will allow cohesion between object-oriented programming and functional programming in a consistent manner, which is the ideal balance point.
The concept of functional interface is a non-intrusive concept because it was designed for Java, a class-based, inflexible, object-oriented language. This allows it to be consistent and usable for the Dart language that shares some characteristics, for example in Flutter and any designed project. with an object-oriented design it can be used.
What is a functional interface in Dart?
Currently Dart allows an approach to the creation of higher order functions thanks to the reserved word
typedef
that allows creating a higher order type that can be used if there are problems, let's see some examples.This is a real example of a StreamBuilder in Flutter
To define a higher order function like Consumer we must do it this way
To define a Consumer with the alternative syntax based on functional interfaces
The
@functionalInterface
annotation is necessary so that the compiler can more easily identify the functional interfaces. Something very similar happens with the@override
annotation for method overloading in class inheritance. I think I don't need to explain further. The idea is simple.About innovation in the syntax of the Dart language
Personally, I think that the syntax of the language is at a fairly high point of maturity and finding room to improve and optimize has become a real challenge.
I think that this proposal could be rejected because
typedef
already exists and I think that it is good, the point of the proposal is to have alternative ways to reach the same result that allow us to enrich the experience of using the languageI think they should return to the idea of Optional since it is one of those features that are really innovative. We will see if functional interfaces are accepted as an alternative to
typedef
The text was updated successfully, but these errors were encountered: