Skip to content

Inline value declarations: where / let...in... #54292

Closed
@lukehutch

Description

@lukehutch

Many languages have the ability to declare named values inline, in an expression context. In Haskell, you have two options -- where and let...in...:

https://wiki.haskell.org/Let_vs._Where

(Sorry, I know there's an issue about this somewhere, but I looked hard and couldn't find it.)

Dart really needs this, because in Flutter, you end up building these massive expression trees when building a UI. If you need a named value in the middle of the tree, you have to precompute it in a separate statement before the expression tree that you're building.

If, however, in your expression tree, you're using an inline list-builder for statement, then you have no way to declare the value derived from the thing you're iterating over, before using it.

This is particularly problematic when calculating the value is very computationally expensive, and you need to use it twice.

For example, I am building a UI that calculates a number (which is a costly operation), for each item in a list, and then if and only if the value is greater than zero, it adds the number in a bubble to the end of the item's derived UI.

Right now I have to do something like the following:

Iterable<T> conditionalYield<T, V>({
  required V Function() calcValue,
  required bool Function(V value) ifTrue,
  required T Function(V value) thenYield,
}) sync* {
  final value = calcValue();
  if (ifTrue(value)) {
    yield thenYield(value);
  }
}

then I can use it like this:

Column(
  children: [
    for (final item in items)
      Row(
        children: [
          ItemLabelWidget(item),
          ...conditionalYield(
            calcValue: () => item.calcExpensiveValue(),
            ifTrue: (value) => value > 0,
            thenYield: (value) => ValueBubble(value),
          ),
        ],
      ),
  ],
),

But I wish I could do something like

Column(
  children: [
    for (final item in items)
      Row(
        children: [
          ItemLabelWidget(item),
          let value = item.calcExpensiveValue()
            in if (value > 0)
              ValueBubble(value),
        ],
      ),
  ],
),

(Yes, I know build is not supposed to be doing a lot of computational work, but the general principle here is "Don't Repeat Yourself" (DRY), and let...in... is useful in many other situations and for many other reasons too..)

I should add that I far prefer let...in... over where, because where is not as readable (values are used before they appear in the source).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions