Юмористический ответ на вопрос, заданный в сабреддите Flutter о лучшем языке для мобильных приложений.

Лучший язык программирования — Форт по понятным причинам. Следовательно, это также лучший язык программирования для разработки мобильных приложений. Мы можем использовать Dart для его реализации.

Давайте определим

typedef Impl = void Function(Forth f);

и создайте класс с именем Forth, который инкапсулирует stack, для которого мы можем push() значения и из которого мы можем pop() значения

class Forth {
  final stack = <dynamic>[];
  void push(dynamic value) => stack.add(value);
  dynamic pop() => stack.removeLast();

и это определяет так называемый словарь предопределенных слов (типа Impl)

  final Map<String, Impl> vocab = {
    '+': (f) => f.push(f.pop() + f.pop()),
    '.': (f) => print(f.pop()),
  };

Также определите метод run() для выполнения слов одно за другим:

  List<String> words = [];
  void run(String input) {
    words = input.split(' ');
    while (words.isNotEmpty) {
      final word = words.removeAt(0);
      final impl = vocab[word] ?? lit(num.parse(word));
      impl(this);
    }
  }

Метод lit автоматически преобразует слова, которые выглядят как числа, в экземпляры Impl, которые помещают значение этого числа в стек, так что все числа неявно предопределены.

  Impl lit(dynamic value) => (f) => f.push(value);

что завершает определение класса Forth

}

Теперь мы можем выполнять такие выражения, как Forth().run(‘1 2 3 + + .’), чтобы напечатать 6.

Новые слова

Нам нужен способ определить наши собственные слова, такие как : twice dup + ;, для которых нам нужно расширить класс Forth, добавив : и ;.

class Forth {
  ...
  List<Impl>? code;
  Map<String, Impl> vocab = {
    ...
    ':': (f) {
      final name = f.words.removeAt(0);
      final code = <Impl>[];
      f.vocab[name] = (f) => code.forEach((impl) => impl(f));
      f.code = code;
    },
    ';': (f) => f.code = null,

а также dup, который также использовался в моем примере выше

    'dup': (f) => f.push(f.stack.last),
  }

и нам нужно изменить run, чтобы не сразу вызывать каждый экземпляр Impl при компиляции кода, а собирать эти объекты в списке, настроенном :

  ...
  void run(String input) {
    words = input.split(' ');
    while (words.isNotEmpty) {
      final word = words.removeAt(0);
      final impl = vocab[word] ?? lit(num.parse(word));
      if (code != null && word != ';') {
        code!.add(impl);
      } else {
        impl(this);
      }
    }
  }

На самом деле, мы должны различать обычные Impl и немедленные Impl, такие как ;, которые должны выполняться даже при компиляции, но я жестко запрограммировал их. Через минуту мне также нужно будет сделать ", [ и ] немедленными.

Новые приложения

Тем не менее, я хвастался использованием Forth для создания мобильного приложения. Вот код популярного приложения-счетчика

V: count 5 !
: inc dup @ 1 + ! ;
[ [ text ] count builder
[ count inc ] " Increment text button ] list column app run

что должно быть легко понять. Я определяю переменную с именем count, присваивая 5. Затем я определяю слово inc, которое можно применить к такой переменной, чтобы увеличить ее значение на 1, а затем я определяю виджет column с виджетом text, построенным из значения переменной, и виджетом button который делает приращение, обернутое как приложение.

Начнем с V:. который определяет слово, которое помещает ValueNotifier в стек, а также помещает этот экземпляр, чтобы его было легче инициализировать

    'V:': (f) {
      final name = f.words.removeAt(0);
      final value = ValueNotifier<dynamic>(null);
      f.vocab[name] = f.lit(value);
      f.push(value);
    },

Слово ! ожидает ValueNotifier и значение в стеке, удаляет оба и присваивает значение уведомителю, например:

    '!': (f) => ((dynamic v) => (f.pop() as ValueNotifier<dynamic>).value = v)(f.pop()),

Слово @ также ожидает ValueNotifier в стеке, которое заменяется значением уведомителя.

    '@': (f) => f.push((f.pop() as ValueNotifier<dynamic>).value),

Слова [ и ] снова являются непосредственными словами, которые работают даже при составлении слова с :. Однако вместо компиляции Impl экземпляров в текущее определяющее слово создается новый список, так что run записывается в так называемую цитацию, которая затем формирует блок кода, который помещается в стек и может быть явно выполнен позже.

    '[': (f) {
      f.push(f.code);
      f.code = <Impl>[];
    },
    ']': (f) {
      final quot = f.code;
      if (quot == null) throw Exception('] without [');
      f.code = f.pop() as List<Impl>?;
      f.add(quot);
    },

Слово " также является немедленным словом, которое помещает следующее слово в стек, не выполняя его, то есть помещает только строку.

    '"': (f) => f.add(f.words.removeAt(0)),

Вот новый метод add:

  void add(dynamic value) => code != null ? code!.add(lit(value)) : push(value);

Чтобы иметь дело с расширяемым списком непосредственных слов, давайте изменим run

  ...
  final immediate = {';', '"', '[', ']'};
  void run(String input) {
    words = input.split(' ');
    while (words.isNotEmpty) {
      final word = words.removeAt(0);
      final impl = vocab[word] ?? lit(num.parse(word));
      if (immediate.contains(word) || code == null) {
        impl(this);
      } else {
        code!.add(impl);
      }
    }
  }

Чтобы позже выполнить котировку, мы используем этот метод exec:

void exec(List<Impl> quot) {
    for (final impl in quot) {
      impl(this);
    }
  }

Давайте создадим виджеты.

Слово builder ожидает ValueNotifier и кавычку и настраивает виджет ValueListenableBuilder Flutter, чтобы кавычка выполнялась каждый раз, когда изменяется значение уведомителя.

    'builder': (f) {
      final vl = f.pop() as ValueNotifier<dynamic>;
      final quot = f.pop() as List<Impl>;
      f.push(ValueListenableBuilder(
        valueListenable: vl,
        builder: (context, value, _) {
          f.push(value);
          f.exec(quot);
          return f.pop() as Widget;
        },
      ));
    },

Затем нам нужны слова для создания виджетов Flutter Text, Column и MaterialApp, которые на данный момент не должны содержать сюрпризов.

    'text': (f) => f.push(Text('${f.pop()}')),
    'button': (f) {
      final child = f.pop() as Widget;
      final quot = f.pop() as List<Impl>;
      f.push(ElevatedButton(onPressed: () => f.exec(quot), child: child));
    },
    'column': (f) => f.push(Column(children: (f.pop() as List).cast<Widget>())),
    'app': (f) => f.push(MaterialApp(home: Scaffold(body: f.pop() as Widget))),

И последнее, но не менее важное: мы можем запустить виджет Flutter в стеке:

    'run': (f) => runApp(f.pop() as Widget),

И это все, что нам нужно для создания работающего приложения Flutter в Forth.

Если вам интересен пример с бегом, вот коврик для дротиков.