RFC for draft DEP: const functions and methods for Dart

273 views
Skip to first unread message

Yegor Jbanov

unread,
Aug 22, 2015, 2:52:36 PM8/22/15
to Dart Misc
Dartisans!

I started working on (my very first) DEP: const functions and methods for Dart. I'd like to get some feedback from the community:

Would you find it useful in your Dart programs?
Do you foresee any potential problems?

It would be especially helpful to hear about use-cases beyond the one I described in the DEP.

Cheers,
Yegor

Brian Wilkerson

unread,
Aug 22, 2015, 4:03:55 PM8/22/15
to General Dart Discussion
Yegor,

This is a cool idea, but I think there's a problem. In particular, I think you did the transformation too quickly between steps 1 and 2.You wrote:

// the example expression from above:
const bind(String).toValue('hello, world!');

// is unwrapped by inlining functions and methods:

// Step 1: inline `bind`
const BindingBuilder(String).toValue('hello, world!');

// Step 2: inline `toValue`
const Binding(String, 'hello, world!');

But I think the result of step 2 is:

const Binding(const BindingBuilder(String).type, value);

Unfortunately, the part in bold isn't a constant expression. In order for this to work we'd need to also extend the language such that the invocation of the getter named 'type' could be considered to be a constant expression. I haven't thought about the implications, but perhaps something along the lines of making constant expression include implicit getters introduced by a final field.

Brian

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Yegor Jbanov

unread,
Aug 22, 2015, 4:28:31 PM8/22/15
to Dart Misc
This is a cool idea, but I think there's a problem. In particular, I think you did the transformation too quickly between steps 1 and 2.

Ah, good catch. Fixed (it's now 3 steps).

const Binding(const BindingBuilder(String).type, value);

Unfortunately, the part in bold isn't a constant expression.

You are right, it also requires the new dispatch semantics, same as the unwrapping of toValue. Either we assume that final fields imply static dispatch in getters inside const expressions, or we support const final Type type.

Anders Holmgren

unread,
Aug 22, 2015, 8:03:54 PM8/22/15
to Dart Misc
+1 for this being a cool idea.

Be very useful for some of my packages that use annotations like constrain and shelf_rest

Natalie Weizenbaum

unread,
Aug 24, 2015, 3:04:04 PM8/24/15
to General Dart Discussion
If both const functions and const methods must be preceded with the const keyword, wouldn't your example have to be written as const (const bind(String)).toValue('hello, world!')?

On Sat, Aug 22, 2015 at 5:03 PM, Anders Holmgren <andersm...@gmail.com> wrote:
+1 for this being a cool idea.

Be very useful for some of my packages that use annotations like constrain and shelf_rest

Bob Nystrom

unread,
Aug 24, 2015, 5:12:54 PM8/24/15
to General Dart Discussion
Long reply is long!

Theoretical consistency

I like the proposal in principle. The fact that a semi-arbitrary set of methods/functions is hardcoded in the language as "these are const and nothing else is" has always felt magical and special case-y. Also, any time users can write an expression, they almost always want to be able to create their own abstractions so they don't have to copy/paste the same expression over and over again. const expressions have always been a sort of unloved step-child to normal expressions in Dart.

Pragmatic usefulness

Having said that: in practice, I don't think const has proven to be that useful in Dart. Angular is very metadata and metaprogramming heavy, so it's a bit of an outlier, but in a lot of Dart code I read, const is rarely used. I've seen a number of classes start out with const constructors and then end up becoming non-const over time because the limitations are too onerous. When that happens, honestly, not much is lost. It's not like using const gives you that much benefit in Dart.

Given how much complexity they add to the language, I don't feel they've ever really carried their weight. I could be wrong, but my intuition is that a lack of const functions/methods isn't what's holding them back. The main problem is they just don't do much.

They exist for two reasons (that I know of):
  1. To possibly enable some VM optimizations. Since you know a const expression's result will never change, the VM could cache it. For example, in code like:

    for (var i = 0; i < 9999999; i++) {
      var c = const SomeSlowToCreateClass(...);
    }

    It's safe to hoist that const constructor out of the loop since it will never change. Maybe I'm wrong, but I believe smart VMs can and do do that for regular non-const classes automatically anyway, so I don't find it very compelling to add a whole shadow "const" sublanguage just to enable that. Even if the VM can't optimize this, you know, just hoist it yourself.

  2. To have some values that are reliably computable at "compile time". This is your use case, because metadata annotations require this. That way, the values of the parameters to a metadata annotation can be computed at "compile time."

    I would find this more compelling if "compile-time" really meant something in Dart, but it doesn't right now. We are statically compiled, with dart2js, but it doesn't use annotations for much. We don't have any real static metaprogramming story unlike, say, D or C++. Our runtime reflection story doesn't play nice with the dominant way that Dart is executed in the wild (compiled to JS).

    This puts us in a weird position where annotations are sort of "write-only". You can put them in your code, but it's actually really hard to do anything useful with them. (And I would put transformers in the "really hard" category.) Given that, I find it hard to get excited about making const expressions more expressive without a plan to actually make const expressions more useful to begin with.
For what it's worth, C#'s attributes have always had similar limitations in the kind of parameters you can pass—basically just literal values—and it hasn't caused too many problems.

If you find yourself stuff so much code into metadata annotations that you find yourself wanting the full expressiveness of the language, to me that feels like it's going against the grain of the language. Is there a way to design your API such that that code is just code?

For example, the unittest (now just "test") API maybe could have used metadata annotations for declaring tests and providing descriptions:

@Test("you can add numbers")
testAddition() {
  expect(1 + 2, equals(3));
}

But I put that stuff deliberately as normal parameters in imperative code because it's more expressive than limiting the user to the half-language of const expressions.

Compile time or not, there is no try

My general feeling is Dart needs to either have a real compile-time metaprogramming story or not. If it does get one—something like macros, templates, etc.—then, hell yes, we should have constant functions. We need to make the compile-time language as complete as possible.

If it doesn't—and right now we don't have any concrete plans to give it one—then making const expressions incrementally more expressive without coming up with new ways to do useful things with constant values doesn't really help users very much.

Comments

Here's some more specific comments on the proposal:

For example, this proposal does not include: ... lazy evaluation

For what it's worth, lazy evaluation doesn't need to be mentioned. Since const expressions are pure, it's not visible to a user whether evaluate is lazy, eager, by name, whatever.

const expressions can be syntactically and lexically transformed to the existing syntax that is accepted by current Dart implementations. Anything that requires more than static transformation is beyond the scope of this proposal.

Doing this correctly requires more than a simple textual substitution. The namespace of the code where a const function is declared may be different from where it's invoked. Consider:

// fn.dart
const name = "I'm from fn.dart";

const fn() => name;

// main.dart
import 'fn.dart';

const name = "Oops, I'm from main.dart";

@SomeAnnotation(fn())
main() { ... }

If you just expand the fn() invocation to its body, it's not going to do the right thing. Scope is hard. Of course, this can be implemented correctly, but doing so starts to look a lot more like a little term rewriting language interpreter than like the simple text preprocessor you're going for.

---

You don't specify this, but your semantics require that const functions and methods cannot be recursive or mutually recursive. Otherwise, the expansion doesn't terminate.

If we add constant functions, I think users will almost immediately want some kind of flow control, at least branching. Think how nice it would be to have a const function that allows a single argument or a list of them and can handle both cases appropriately.

At that point, recursion could be allowed as long as there was a reliable base case. But now you're inching towards Turing-completness and all of the scariness that causes in code that runs at compile/static-analysis time.

---

I agree that if you're going to have const functions, you should have const methods too since Dart is so method-oriented. Also, doing so would give a coherent way to explain why "1 + 2" is a valid const expression. I do worry that somehow dynamic dispatch may sneak in. I can't think of any good examples, so maybe not.

As always, this is just my gut feeling. I definitely don't speak for the entire team.

Cheers!

- bob

Anders Holmgren

unread,
Aug 24, 2015, 6:25:51 PM8/24/15
to Dart Misc

> Our runtime reflection story doesn't play nice with the dominant way that Dart is executed in the wild (compiled to JS)

If you make too many language decisions based on how Dart is currently used then you will likely just entrench that use and deter others.

Dart is an excellent language on the server side and it would be sad to see useful language features ignored because Dart on the server is not yet that popular.

Bob Nystrom

unread,
Aug 24, 2015, 7:55:02 PM8/24/15
to General Dart Discussion
On Mon, Aug 24, 2015 at 3:25 PM, Anders Holmgren <andersm...@gmail.com> wrote:
>  Our runtime reflection story doesn't play nice with the dominant way that Dart is executed in the wild (compiled to JS)

If you make too many language decisions based on how Dart is currently used then you will likely just entrench that use and deter others.

Sure, no one wants to rule out tomorrow's platforms. At the same time, it's really hard to succeed on two platforms if you don't succeed on one first.

Right now, we're focused on the web. With Sky, we're also hoping to go somewhere on mobile platforms including iOS. Both of those require static compilation, so a Dart that doesn't work well statically compiled is unlikely to live long enough to succeed on platforms where that's less of a constraint.

Also, it's not just about static compilation as much as it is static tooling. The Smalltalk ideal where you develop inside a live image doesn't seem to be the way most programmers work today. Instead, what you see is lots of static analysis, and programmers spending all day working on code-as-parsed-text, not code-as-live-objects. Given that, it makes a lot of sense for Dart to be amenable to static analysis and to have its metaprogramming story be friendly to that.

Cheers!

- bob

Anders Holmgren

unread,
Aug 24, 2015, 8:51:11 PM8/24/15
to General Dart Discussion
Yes tooling for me is one of the main parts of what I find attractive about dart. It's a key part of the overall dev experience as is really fast dev loop (thanks to no compiling and fast startup) at least in cases where you don't compile it to js.

I'm not really obsessed with classes and type annotations per se but I am obsessed with the dev experience that facilitates. If that same or better experience came by different means that would potentially be fine by me.

I'm constantly amazed with the terrible dev experience that js, php and Python devs not only put up with but seem to almost take pride in.

I had that cutting c code in Emacs at the start of my career and was very happy when ides started pulling their weight in conjunction with more toolable languages. I'm very unhappy when forced to code in languages where I have to go back to such a primitive experience.
--

Daniel Joyce

unread,
Aug 24, 2015, 9:25:06 PM8/24/15
to General Dart Discussion

Dart was sold as as a web language. The fact it is useful server side somewhat seems pointless given python exists. It's not even close between python and dart in terms of mature libraries especially db access.

Dart promised to bring sanity to web development to replace it and now seems withering in that regard.


To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.
--
Daniel Joyce

The meek shall inherit the Earth, for the brave will be among the stars.

Anders Holmgren

unread,
Aug 25, 2015, 1:21:27 AM8/25/15
to General Dart Discussion
Totally disagree. Yes Python, JS/Node, Java etc etc have much more libraries but that is always the case for new languages, just as it was for go a few years earlier.

I've recently had to do Python code after coding in Dart for a year or two and I find Python a very poor dev experience in comparison. Admittedly I haven't tried python 3 and I heard they have type annotations there. Maybe that would narrow the gap but life's too short for languages without typing.

Lasse R.H. Nielsen

unread,
Aug 25, 2015, 2:09:50 AM8/25/15
to mi...@dartlang.org
On Mon, Aug 24, 2015 at 11:12 PM, 'Bob Nystrom' via Dart Misc <mi...@dartlang.org> wrote:
Long reply is long!

Theoretical consistency

I like the proposal in principle. The fact that a semi-arbitrary set of methods/functions is hardcoded in the language as "these are const and nothing else is" has always felt magical and special case-y. Also, any time users can write an expression, they almost always want to be able to create their own abstractions so they don't have to copy/paste the same expression over and over again. const expressions have always been a sort of unloved step-child to normal expressions in Dart.


I agree, and I would like const functions.

That said, the current hardwired functions are almost exclusively ones that could not be expressed with the proposed const function syntax. They all do meaningful computation (e.g., arithmetic or toString).

A const function would only be able to do what a potential compile-time constant expression can do, like the ones we have in initializer lists. One of the use cases I can see is exactly moving such code out of initializer lists.

 
Pragmatic usefulness

Having said that: in practice, I don't think const has proven to be that useful in Dart. Angular is very metadata and metaprogramming heavy, so it's a bit of an outlier, but in a lot of Dart code I read, const is rarely used. I've seen a number of classes start out with const constructors and then end up becoming non-const over time because the limitations are too onerous. When that happens, honestly, not much is lost. It's not like using const gives you that much benefit in Dart.

Given how much complexity they add to the language, I don't feel they've ever really carried their weight. I could be wrong, but my intuition is that a lack of const functions/methods isn't what's holding them back. The main problem is they just don't do much.

True. The guaranteed canonicalization is the only real advantage. Apart from that, generating the object at compile time would be just a compiler optimization. 
I still like the concept, and think it actually works very, even if it doesn't do *much*.
 

They exist for two reasons (that I know of):
  1. To possibly enable some VM optimizations. Since you know a const expression's result will never change, the VM could cache it. For example, in code like:

    for (var i = 0; i < 9999999; i++) {
      var c = const SomeSlowToCreateClass(...);
    }

    It's safe to hoist that const constructor out of the loop since it will never change. Maybe I'm wrong, but I believe smart VMs can and do do that for regular non-const classes automatically anyway, so I don't find it very compelling to add a whole shadow "const" sublanguage just to enable that. Even if the VM can't optimize this, you know, just hoist it yourself.
There are few slow-to-create const classes because of the restrictions on const constructors.
Where I see this used is mainly on tables (lists or maps) where you can inline a const table without creating it every time the function is called. That is, function hoisting rather than loop hoisting.
 

  1. To have some values that are reliably computable at "compile time". This is your use case, because metadata annotations require this. That way, the values of the parameters to a metadata annotation can be computed at "compile time."
It's also used for the default values of parameters, which I think is a more common use (even if we tend to use null as default and replace it with the actual default value using code). 
True, you can't just move the const expression - it might depend on private const variables from its original scope, nothing will make that work. However, it's just a matter of how deeply you need to expand the const expression before all the variables are either basic values or visible from where it's expanded.
Worst case, it will expand to a complete const expression with no variables.

Internally in a compiler, scope should not be a problem, you can probably just refer to the const value.

 

---

You don't specify this, but your semantics require that const functions and methods cannot be recursive or mutually recursive. Otherwise, the expansion doesn't terminate.

It is important to avoid having more than a finite number of compile-time constants (and preferably at most linear in the actual source code size), so any recursion of a const function should be prohibited.

Currently we avoid arbitrarily recursive const constructors because the arguments to a const constructor call in an initializer list must be compile-time constants, not just potential compile-time constants, so any call will have a value fixed value by the call site, and therefore any recursive call will diverge.
(Well, and because we just prohibit it in the VM, dart2js gets a stack overflow). 


If we add constant functions, I think users will almost immediately want some kind of flow control, at least branching. Think how nice it would be to have a const function that allows a single argument or a list of them and can handle both cases appropriately.

Using ?: is already a compile time constant if the operands are, so we have that. What we might want is a compile-time constant "is" check.
 

At that point, recursion could be allowed as long as there was a reliable base case. But now you're inching towards Turing-completness and all of the scariness that causes in code that runs at compile/static-analysis time.

Yes, let's not go down that road. It does matter that it's undecidable, even if there are efficient algorithms.
 

---

I agree that if you're going to have const functions, you should have const methods too since Dart is so method-oriented. Also, doing so would give a coherent way to explain why "1 + 2" is a valid const expression. I do worry that somehow dynamic dispatch may sneak in. I can't think of any good examples, so maybe not.

It shouldn't - const methods will only make sense on const objects, so it's the actual method on that object that needs to be const, and if it is, you are fine.

We do need to consider whether overriding a non-const method with a const method (or vice versa) is a warning or error. It might not need to be.

We probably also want to allow access to final fields on const objects. That breaks the field/getter symmetry, so we might want to define const instance fields (the ones you are allowed to read at compile-time) vs non-const (just plain final) fields. That way, you can see that changing a const instance field to a non-const getter will break code. If a field is just final, you keep the option of making it a non-const getter later.


As always, this is just my gut feeling. I definitely don't speak for the entire team.

I like this, but you are probably right that it's not a big gain. 
It *is* doable as a preprocessor.
I should write a preprocessor [img: procrastinator cat].

/L
 

Cheers!

- bob


On Mon, Aug 24, 2015 at 12:03 PM, 'Natalie Weizenbaum' via Dart Misc <mi...@dartlang.org> wrote:
If both const functions and const methods must be preceded with the const keyword, wouldn't your example have to be written as const (const bind(String)).toValue('hello, world!')?

On Sat, Aug 22, 2015 at 5:03 PM, Anders Holmgren <andersm...@gmail.com> wrote:
+1 for this being a cool idea.

Be very useful for some of my packages that use annotations like constrain and shelf_rest

--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.



--
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84

Günter Zöchbauer

unread,
Aug 25, 2015, 3:37:37 AM8/25/15
to Dart Misc
I used Go on the server at first and switched to Dart later because it's such a big advantage to use the same language on client and server.

Günter Zöchbauer

unread,
Aug 25, 2015, 3:38:17 AM8/25/15
to Dart Misc

Bob Nystrom

unread,
Aug 25, 2015, 6:50:11 PM8/25/15
to General Dart Discussion
On Mon, Aug 24, 2015 at 11:09 PM, 'Lasse R.H. Nielsen' via Dart Misc <mi...@dartlang.org> wrote:
I do worry that somehow dynamic dispatch may sneak in. I can't think of any good examples, so maybe not.

It shouldn't - const methods will only make sense on const objects, so it's the actual method on that object that needs to be const, and if it is, you are fine.

Since you mentioned ?: can be used in const expressions, then consider:

class Foo {
  const method() => "Foo";
}

class Bar {
  const method() => "Bar";
}

const fn(bool condition, Foo foo, Bar bar) => (condition ? foo : bar).method();

Is this OK? If we evaluate the ternary operator at compile time, then after that, we know which branch was chosen and can then dispatch the method() call at compile time too, and it works. But the least upper bound of the ?: operands gives a result type of Object, which doesn't have method().
 
I guess this would be a static warning, but could work by "dynamically dispatching at compile time"! This feels like a rathole to me.


We do need to consider whether overriding a non-const method with a const method (or vice versa) is a warning or error. It might not need to be.

We probably also want to allow access to final fields on const objects. That breaks the field/getter symmetry, so we might want to define const instance fields (the ones you are allowed to read at compile-time) vs non-const (just plain final) fields. That way, you can see that changing a const instance field to a non-const getter will break code. If a field is just final, you keep the option of making it a non-const getter later.

Oof, this also feels really complex. I'd rather keep const dead simple like it is now and like C# attributes are, or just use regular Dart expressions. It feels like we have to add a ton of complexity to const just to get an incremental increase in expressiveness otherwise.

Cheers,

- bob

Lasse R.H. Nielsen

unread,
Aug 26, 2015, 3:49:50 AM8/26/15
to mi...@dartlang.org
On Wed, Aug 26, 2015 at 12:49 AM, 'Bob Nystrom' via Dart Misc <mi...@dartlang.org> wrote:

On Mon, Aug 24, 2015 at 11:09 PM, 'Lasse R.H. Nielsen' via Dart Misc <mi...@dartlang.org> wrote:
I do worry that somehow dynamic dispatch may sneak in. I can't think of any good examples, so maybe not.

It shouldn't - const methods will only make sense on const objects, so it's the actual method on that object that needs to be const, and if it is, you are fine.

Since you mentioned ?: can be used in const expressions, then consider:

class Foo {
  const method() => "Foo";
}

class Bar {
  const method() => "Bar";
}

const fn(bool condition, Foo foo, Bar bar) => (condition ? foo : bar).method();

Is this OK? If we evaluate the ternary operator at compile time, then after that, we know which branch was chosen and can then dispatch the method() call at compile time too, and it works. But the least upper bound of the ?: operands gives a result type of Object, which doesn't have method().
 
I guess this would be a static warning, but could work by "dynamically dispatching at compile time"! This feels like a rathole to me.

True.
Compile-time constants are independent of static type checking, they just happen to be mostly type-safe so you don't see warnings like this much.

We designed compile-time constant expressions to have the same meaning as if they were executed at runtime - except for the canonicalization, so if it works at runtime, it should work at compile-time.
You could argue that you should get the same warnings too - the static type analysis doesn't have to do constant evaluation, so it doesn't know the actual type of the constant.

We already have the problem of the actual type not matching the static type in other places, even at compile-time.

class C {
  const factory C.a() = A;
}

class A implements C, Z {
  const A();
}

class Z {}

main() {
  const Z z = const C.a();  // Warning, compile-time safe.
}

It's just that the ?: operator is very good at loosing the static type.

Another example:
class A {
  const factory A() = C.a;
}
class B {
  const factory B() = C.b;
}
class C implements A, B {
  final int id;
  const C.a() : id = 0;
  const C.b() : id = 1;
}
main() {
  const C c = (const bool.fromEnvironment("foo") ? const A() : const B());
  print(c.id);
}



We do need to consider whether overriding a non-const method with a const method (or vice versa) is a warning or error. It might not need to be.

We probably also want to allow access to final fields on const objects. That breaks the field/getter symmetry, so we might want to define const instance fields (the ones you are allowed to read at compile-time) vs non-const (just plain final) fields. That way, you can see that changing a const instance field to a non-const getter will break code. If a field is just final, you keep the option of making it a non-const getter later.

Oof, this also feels really complex. I'd rather keep const dead simple like it is now and like C# attributes are, or just use regular Dart expressions. It feels like we have to add a ton of complexity to const just to get an incremental increase in expressiveness otherwise.

Agree, it's probably not worth the complexity it introduces
/L

Erik Ernst

unread,
Aug 26, 2015, 4:46:57 AM8/26/15
to Dart Misc
On Wed, Aug 26, 2015 at 9:49 AM, 'Lasse R.H. Nielsen' via Dart Misc <mi...@dartlang.org> wrote:
On Wed, Aug 26, 2015 at 12:49 AM, 'Bob Nystrom' via Dart Misc <mi...@dartlang.org> wrote:
On Mon, Aug 24, 2015 at 11:09 PM, 'Lasse R.H. Nielsen' via Dart Misc <mi...@dartlang.org> wrote:
I do worry that somehow dynamic dispatch may sneak in. I can't think of any good examples, so maybe not.

It shouldn't - const methods will only make sense on const objects, so it's the actual method on that object that needs to be const, and if it is, you are fine.

Since we are discussing compile-time evaluation of expressions, everything we do will be done "statically", so in that sense there is no dynamic dispatch. ;)

The real distinction is whether or not we will accept potential non-termination. If we accept it, there is no significant difference between allowing dynamic dispatch (including potentially-recursive invocations like `other.foo(..)` inside an implementation of `foo`) and plain recursion.

If, on the contrary, we insist that each method implementation is provably terminating (among other things, we'd rule out all kinds of recursive invocation), then it's easy to see that dynamic dispatch does not introduce any non-terminating computations.

Since you mentioned ?: can be used in const expressions, then consider:

class Foo {
  const method() => "Foo";
}

class Bar {
  const method() => "Bar";
}

const fn(bool condition, Foo foo, Bar bar) => (condition ? foo : bar).method();

Is this OK? If we evaluate the ternary operator at compile time, then after that, we know which branch was chosen and can then dispatch the method() call at compile time too, and it works. But the least upper bound of the ?: operands gives a result type of Object, which doesn't have method().
 
I guess this would be a static warning, but could work by "dynamically dispatching at compile time"! This feels like a rathole to me.

Maybe it would be useful to make the threats concrete? If we decide that non-termination is a rare problem that we can just handle pragmatically, what else is dangerous about dynamic dispatch? We don't require programs in general to avoid all these fancy Turing-completeness related mechanisms, so why should we be scared of them when they are running a bit earlier?

True.
Compile-time constants are independent of static type checking, they just happen to be mostly type-safe so you don't see warnings like this much.

We designed compile-time constant expressions to have the same meaning as if they were executed at runtime - except for the canonicalization, so if it works at runtime, it should work at compile-time.
You could argue that you should get the same warnings too - the static type analysis doesn't have to do constant evaluation, so it doesn't know the actual type of the constant.

Good idea! No need to make constant evaluation different from plain Dart, it should just be a subset (where, of course, "runtime errors" will be detected and hence processed at compile-time).
 
We already have the problem of the actual type not matching the static type in other places, even at compile-time.

class C {
  const factory C.a() = A;
}

class A implements C, Z {
  const A();
}

class Z {}

main() {
  const Z z = const C.a();  // Warning, compile-time safe.
}

A warning makes sense for const expressions as well, because the code is less easy to read and understand when it contains such a twisted "forget it's a Z, then require that it's a Z" data flow. Should be refactored for clarity!
 
It's just that the ?: operator is very good at loosing the static type.

Another example:
class A {
  const factory A() = C.a;
}
class B {
  const factory B() = C.b;
}
class C implements A, B {
  final int id;
  const C.a() : id = 0;
  const C.b() : id = 1;
}
main() {
  const C c = (const bool.fromEnvironment("foo") ? const A() : const B());
  print(c.id);
}

So it gets more complex, but not nearly as complex as Dart code in general, and we insist that a successful compilation includes a finite evaluation of all const values in the program (somehow we'll stop with a compile-time error if there is an infinite loop), so we will actually be able to investigate the result in detail if we wish to do so (just inspect const values in a debugger). I don't really see why that should be a problem.

We do need to consider whether overriding a non-const method with a const method (or vice versa) is a warning or error. It might not need to be.

If we keep const evaluation strictly as a sublanguage (same behavior as plain Dart if you replace `const` by `final`) then these overriding relations could be allowed (so a const computation could end up attempting to call a non-const method, which is a const evaluation error) or they could be disallowed (built-in safety in return for less flexibility). The discussions about exactly where to go for safety and where to go for flexibility can go on forever, of course, but I'm on the flexible side as long as we rule out side-effects on the (non-existing) heap.

We probably also want to allow access to final fields on const objects. That breaks the field/getter symmetry, so we might want to define const instance fields (the ones you are allowed to read at compile-time) vs non-const (just plain final) fields. That way, you can see that changing a const instance field to a non-const getter will break code. If a field is just final, you keep the option of making it a non-const getter later.

One could hope that it would be possible to avoid final fields on const objects, given that there is more flexibility in the computation of const values (and hence we might be able to change `final` to `const` on those fields).

Oof, this also feels really complex. I'd rather keep const dead simple like it is now and like C# attributes are, or just use regular Dart expressions. It feels like we have to add a ton of complexity to const just to get an incremental increase in expressiveness otherwise.

Agree, it's probably not worth the complexity it introduces

That's definitely one of the reasonable conclusions. I think another one is "let's allow for recursion and dispatch, and catch infinite loops pragmatically".

  best regards,

--
Erik Ernst  -  Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 København K, Denmark
CVR no. 28866984

tatumizer-v0.2

unread,
Aug 26, 2015, 10:22:26 AM8/26/15
to Dart Misc
Can someone explain why const constructors are so important? To me, the whole "const" thing looks like arbitrary sub-language inside dart that serves unknown purpose.
Constant expressions in java are much simpler, and no one complained:

I think overly complicated, unmotivated "const" sub-language subtracts from the overall quality of dart. Developing it further will make the effect worse.



Erik Ernst

unread,
Aug 26, 2015, 11:09:41 AM8/26/15
to Dart Misc
I've heard that critique before, and it is certainly an area of the language which tends to pull in some complexity because it includes a subset of several semantic areas in Dart. But if you want to make sure that certain expressions will have a single value that we can compute at compile-time, e.g., because we want to inline it during compilation, then you need something like `const`.

In Java you can only have compile-time constant values if they are (1) of a primitive type or of type String, and (2) provided by a `final` declaration that (3) is initialized by an initializer in the declaration (not by a constructor); finally, (4) that initializer must be a constant expression (see http://docs.oracle.com/javase/specs/jls/se8/jls8.pdf, 15.28 for that).  That doesn't include any objects, so we need to blend in most of the chapter about annotations (p294-321 in jls8.pdf) in order to get something which is similar to Dart `const` values. In the end it's not obvious to me that it is so much simpler in Java.

But, of course, if there is a way to avoid a lot of complexity that turns out to be unnecessary then lets go for it!

Bob Nystrom

unread,
Aug 26, 2015, 12:18:50 PM8/26/15
to General Dart Discussion

On Wed, Aug 26, 2015 at 8:09 AM, 'Erik Ernst' via Dart Misc <mi...@dartlang.org> wrote:
But if you want to make sure that certain expressions will have a single value that we can compute at compile-time, e.g., because we want to inline it during compilation, then you need something like `const`.

It seems like we added an awful lot of complexity to the language just for that. (Though I do appreciate that constructor initialization lists make it easier to add non-nullable types.) All of this baggage is odd given that it seems like a little constant folding + escape analysis would accomplish the same thing automatically. Or, hell, just tell users to cache things themselves if they're worried about redundant computation.

Dart's love for const seems particularly out of character to me given how tepid its feelings towards static types are. I really don't need to be able to express arbitrary recursive computation at compile time. But I sure as hell could use the ability to express "this function returns a value of the same type as its argument".

Cheers!

- bob

Alex Tatumizer

unread,
Aug 26, 2015, 12:27:23 PM8/26/15
to mi...@dartlang.org
Perhaps we need to find a different perspective here, too? For example:
"const" sub-language is just a euphemism for pre-processor. Very limited one, but still. In another thread, Lasse's comment indicates that dart might have warmed up to the idea of preprocessing (if my understanding is correct). This would make things simpler in many areas - e.g, right now "configurable imports" proposals are on their way to create yet another weird sub-language. Preprocessor makes this unnecessary.

My point is that the idea of eschewing preprocessor is getting more costly by the day.  Maybe it makes sense to  look at nim language for inspiration: Quote:
"Nim meta-programming is done with the Nim language itself. That means that most Nim code can be executed at compile time, and Nim's ability to generate Nim-code at compile time is much more sophisticated"


Bob Nystrom

unread,
Aug 26, 2015, 12:57:01 PM8/26/15
to General Dart Discussion
On Wed, Aug 26, 2015 at 9:27 AM, Alex Tatumizer <tatu...@gmail.com> wrote:
Perhaps we need to find a different perspective here, too? For example:
"const" sub-language is just a euphemism for pre-processor. Very limited one, but still. In another thread, Lasse's comment indicates that dart might have warmed up to the idea of preprocessing (if my understanding is correct). This would make things simpler in many areas - e.g, right now "configurable imports" proposals are on their way to create yet another weird sub-language. Preprocessor makes this unnecessary.

We have considered this for handling configuration-specific code. The main problem is that it makes your tooling story much harder. Having decent static analysis—hell, even syntax highlighting—is much more complex when a preprocessor gets involved.
 
My point is that the idea of eschewing preprocessor is getting more costly by the day.  Maybe it makes sense to  look at nim language for inspiration: Quote:
"Nim meta-programming is done with the Nim language itself. That means that most Nim code can be executed at compile time, and Nim's ability to generate Nim-code at compile time is much more sophisticated"

Yeah, there are a number of languages that go down this path. You'll note that almost all of them don't have any kind of IDE story, just plain text editors.

It's not an unreasonable path, but I don't think it's the right path for Dart, which is trying to appeal to application programmers used to and happy working inside static analysis-based IDEs.

Cheers!

- bob

Anders Holmgren

unread,
Aug 27, 2015, 4:40:35 PM8/27/15
to General Dart Discussion
To me tool-ability combined with dev speed is Dart's secret sauce. That's why I find it the most productive and enjoyable language to work with.

Languages that concentrate on the features and aesthetics of the language without regard for how that works with tooling are great for academia, but I prefer to not use them day to day. 

I hope Dart continues to keep developer experience at the forefront of it's focus

Randal L. Schwartz

unread,
Sep 1, 2015, 4:33:44 PM9/1/15
to mi...@dartlang.org
>>>>> "Daniel" == Daniel Joyce <daniel....@gmail.com> writes:

Daniel> The fact it is useful server side somewhat seems pointless given
Daniel> python exists. It's not even close between python and dart in
Daniel> terms of mature libraries especially db access.

Dart 1.0: this year
Python 1.0: January 1994.

A bit unfair, don't you think?

I'm just happy that I can use

#!/usr/bin/env dart

And it *works*. Awesome.

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<mer...@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix consulting, Technical writing, Comedy, etc. etc.
Still trying to think of something clever for the fourth line of this .sig

Yegor Jbanov

unread,
Sep 15, 2015, 6:45:36 PM9/15/15
to Dart Misc
Thanks, everyone, for feedback! This discussion was great. I turned the feedback into a set of GitHub issues and will address them in the DEP. Let's continue the discussion on GitHub.

Cheers!

Yegor

Reply all
Reply to author
Forward
0 new messages