Pull to refresh

Comments 42

>откроет огромные возможности для программистов
Не откроет. Думаете, вы первый? Таких идей уже было миллион. В лучшем случае из них как-то работает и не умерло — от силы процентов 5. Почему? Да потому что у этой идеи есть куча очевидных и менее очевидных недостатков. Например, самый очевидный — раз код не текст, значит вам не годятся все те инструменты, которые были сделаны человечеством для текстов, включая программистские типа diff, например. Значит вам придется сделать свое версионирование, допустим.

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

А самое главное — вы пытаетесь решать несуществующие проблемы. Или несущественные. Отличия в синтаксисе языка? Это такая неважная хрень. Я обычно пишу в одном проекте примерно на 4-5 языках, и у них у всех разный синтаксис в вашем смысле. Не напрягает ну совсем, для этого как раз давно есть текстовые редакторы и IDE.

Скорость разработки, путем подстановки цикла одной кнопкой? Ой, откройте для себя скажем IDEA, там это есть года так с 2005, не позже. Для Java, и других языков тоже. И да, сюрприз — если бы в жизни скорость разработки ограничивалась только этим…
Да, пожалуй я слишком мало внимания уделил основному. Самая существенная решаемая проблема — это длительная компиляция программ на текстовых языках. При блочном программировании есть возможность в реальном времени «достраивать» программу в оперативной памяти. В этом случае было бы два режима выполнения разрабатываемой программы — в режиме «редактирования» и в «финальном» режиме. В первом случае программа запускается параллельно со средой разработки и среди кодов программы содержатся вспомогательные данные, позволяющие менять и соединять изменённые объекты программы на лету, однако сама программа из-за этого выполняется медленнее. Во втором режиме всё лишнее отбрасывается и программа компилируется для выполнения с максимальной производительностью. Второй режим — это, по сути, традиционная компиляция.

Во вторых есть ещё одна хорошая возможность в том случае если программа выполняется в первом режиме. Можно построить речевой анализатор, который будет распознавать речевые команды, отдаваемые человеком и на их основе изменять устроение программы. Можно было бы отдавать команды вроде «Создай новое окно», «Когда нажимают на эту кнопку отправляй почту по адресу» или чего посложнее (в зависимости от развитости этого анализатора). И эта суб-программа вносила бы изменения в разрабатываемую программу. Во многих случаях речевое взаимодействие было бы очень полезно. Сказать что-то в микрофон зачастую гораздо быстрее чем использовать мышь с клавиатурой, к тому же это избавило бы от чтения многих страниц документации.
Самая существенная решаемая проблема — это длительная компиляция программ на текстовых языках.

… ну вот на C#, например. Длительная?


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

А что мешает сделать это с "традиционным" языком программирования?

Подскажите, сколько времени происходит компиляция программы на питоне? Или на js?
А как насчет instant run в Android с компилируемой джавой?

Весьма интересная идея, если я ее правильно понял… можно писать на нескольких языках одновременно… нужно переварить.

Похоже на Java и .Net, не правда ли?

Очень отдаленно. В комментариях ниже есть ссылка интересная на эту тему.

Да, но я возможно зря акцентировал внимание именно на этом. См. комментарий выше.
описанный способ программирования откроет огромные возможности

Действительно, как говорит sshikov, таких «конструкторов» существует очень много.
Если бы вы начинали с этих конструкторов, то осознали бы, что идея написания кода — это эволюция, логическое продолжение идеи компоновки блоков.

Когда вы попробуете написать действительно полезную, важную, большую программу на блоках, все описанные «плюсы» начнут вам только мешать.
  1. Синтаксиса не будет, но возникнет проблема выбора блоков из аналогов, их совместимости и прочего, что вы можете наблюдать в npm.
  2. В реальности, написать пару скобок, даже не смотря на клавиатуру, гораздо быстрее, чем найти нужный блок в каталоге, перетащить его мышкой и соединить с другими блоками. Учтите, что таких блоков очень много.
  3. Ну это вообще бред. Если только вы не продаете вместо готовых программ их исходники в магазине, то язык программирования влияет. Кроме того, имеет смысл передавать не код, а алгоритм.
  4. Тут вы совсем забыли про компиляторы, библиотеки, автоматическое форматирование и оптимизацию.
  5. Представление никак не зависит от кода/блоков. Составьте дерево синтаксическое/вызовов/иерархий и рисуйте его в виде комнаты. Но, важно заметить, что самый эффективный способ программирования — однонаправленный однопоточный непрерывный ввод символов с клавиатуры. Просто потому, что создаётся рефлекс, и вам уже не требуется обращать внимание на клавиатуру или набранный код.
Именно. Что есть блок? Какие у него свойства, и как их можно компоновать друг с другом? Как гарантируется, что композиция будет правильно работать? Вот это все существенные вопросы, а синтаксис — это такая десятая по счету фигня, не то чтобы не важная, но явно не решающая.

Вот тут человек уже много лет продвигает похожую идею, правда на основе бинарной совместимости блоков, и даже разработал среду разработки, которая работает по такому принципу:
http://алексейнедоря.рф

Ух… Давайте сразу ответим на главный вопрос вы тролль? Нет?! Тогда каков ваш опыт разработки, на каких языках и какие парадигмы программирования вы использовали?
Потому что современные языки программирования именно и работают с блоками. Пакеты, библиотеки, фреймворки, ООП это всё именно и есть тот набор блоков из которых мы и собираем программы. При этом можно в любой момент взять и немного изменить блок под себя не переступая через барьер.
Во-первых, мы избавимся от несущественных отличий в синтаксисе различных языков.

Языки отличаются не только синтаксисом, но и семантикой. for в С++ — не то же самое, что for в паскале, scala или питоне. Если избавиться от отличий, получится весьма невыразительный язык.


Во-вторых, увеличится скорость написания программы. Сейчас для того, чтобы написать оператор for в С-подобном языке нам нужно написать сам текст for, затем скобки, параметры, точки с запятыми, фигурные скобки, и нажать клавишу ввод.

Для программирования нет ничего быстрее при записи и нагляднее при чтении, чем текст. Графические инструменты (например, scratch, и даже мёртвый ДРАКОН ) уже существуют, но медленны и годятся только для простых программ.


А всё потому, что сложность языка программирования мала в сравнении со сложностью программы. Языки программирования просты. Для программиста не составляет труда помнить десяток синтаксисов различных языков. А вот структуру типичной программы из миллионов строк не запомнишь, и поэтому приходится часто читать код, читать много кода. И тут текст оказывается более компактным, гибким, удобочитемым, чем существующие графические представления кода.


В-пятых представлять структуру программы можно будет любым удобным для того способом. Например, в виде трехмерной комнаты, по которой можно перемещаться наподобие компьютерных игр и редактировать связи трехмерных классов и объектов.

Будет ли это удобнее? Может быть, вместо UML диаграмм пакетов/классов/объектов трёхмерная комната будет выглядеть нагляднее. Но, имхо, не вместо for'ов и while'ов внутри функции.


P.S. И как вы представите в одинаковых графических блоках C++ и Хаскель?

| Графические инструменты (например, scratch, и даже мёртвый ДРАКОН )
| уже существуют, но медленны и годятся только для простых программ.

Не совсем мёртвый, и есть определённая изюминка его использования в общем процессе разработки программ. :)

Пример темы на форуме Дракон языка Преимущества языка ДРАКОН для деревьев принятия решений (код может генерироваться на разные языки программирования)

Довольно малоприменимый подход.
Получится своего рода код, который генерируют визуальные редакторы html, с поправкой на то, что языков программирования много и они намного сложнее и богаче в синтаксисе и возможностях. О качестве генерируемого кода можно даже не говорить.


Такой подход имеет право на существование в очень ограниченном списке задач. Но зачастую это проще и лучше обойти встроенным рантаймом иного языка. Например, встраивание lua в язык более низкого уровня.

Если я верно понимаю, похожий подход декларировал еще Visual Basic почти тридцать лет назад. И казалось, что вот еще немножко, и тот унылый текст, который приходится писать для того, чтобы сделать вот эти волшебные кнопочки, перетаскиваемые мышкой на формочку, функциональными — еще немножко, и этот текст тоже превратится в блоки, таскаемые мышкой…

И еще: только не примите, пожалуйста, в свой адрес, поскольку вольная ассоциация.
В самом начале нулевых одна моя знакомая очень, очень ждала двух вещей. Хороших автопереводчиков, ибо не хотела учить английский, и хорошую распознавалку речи, ибо не хотела учиться печатать. Ей же было уже тридцать пять, жизнь практически позади и все эти технические подробности на самом деле нужны только затем, чтобы подвести некоторые итоги в этой самой уже прошедшей жизни.
Ждет до сих пор.
Извините, так кнопочки ведь работали.
Возможно, я вас не очень понял.
Кнопочки работали, но чтобы они что-то делали, это что-то нужно было прописать унылым текстом. Сама по себе кнопочка могла нажиматься, отжиматься и отлавливать это состояние. А привязать к нажатию какой-нибудь там выход из программы надо было уже вручную.
Или я что путаю?
Пожалуй проект HiAsm (конструктор программ)
оправдавшийся проект для визуального программирования.
Какая прикольная штука… Спасибо.
Э-э-э вообще то еще в 90-е был Clarion, в котором программа автогенерилась, по структуре данных. Только в специальном «помощнике» нужно было указать вид приложения и какая таблица в какой форме как должна отображаться и редактироваться.
Так что не в кнопочках дело. :-)

Вот только, как оказалось, по одной и той же структуре данных можно сгенерировать дофига разных программ...

Там в основном был генерация CRUD приложений, к которым потом нужно было нагенерить отчетов.
Особо сложной бизнес-логики не предполагалось.
Не знал про такое. Впрочем, я не то, что ненастоящий, я вообще не разработчик.
Если я вас верно понял, то собственно там, где требовалась логика посложнее и гибкости побольше, он также оказывался неприменим?
Ну почему?
Просто если нужно было совсем не стандартное, то надо было писать код, как обычно.
Например мой коллега писал программу, для приема документов в ВУЗ.
Быстро накидал прототип с отчетами. А потом ручками дописывал кучу ФЛК, чтобы операторы не могли ввести фигню.
если нужно было совсем не стандартное, то надо было писать код, как обычно.
Да, я это и имел в виду.
Самые базовые вещи автоматизируются, и могут быть представлены графически. Но шаг влево, шаг вправо, и от написания кода руками деться некуда. Собственно, это была моя основная мысль с первого коммента начиная, но похоже, я в этом треде несколько косноязычен.
Да конечно. Clarion в основном подходил для CRUD приложений.
Просто, например, когда я попробовал Delphi, который расхваливали, что программы там рисуются.
У меня было эмоции «ЧТА? Эта штука не умеет, того что давно умеет Clarion под DOS»

Для этого в первую очередь нам потребуется хранить исходные коды в другой форме.

… ну то есть хранить сериализованное синтаксическое дерево.


Например, блоки кода в С++ разделяются фигурными скобками, а Ruby обращает внимание на ключевые слова… В редакторе можно настроить и тот и другой вид программы, от этого не поменяется ничего в ней самой. Персональные настройки отображения позволят разработчику смотреть на программу так, как ему нравится. Кроме того, мы сможем использовать любые символы при именовании, включая пробелы.

Как вы думаете, почему этого до сих пор не сделано? Помимо многих других ответов, есть один очень простой: производительность. Как вы ни старайтесь, обычный текстовый редактор вы не обгоните.


Сейчас для того, чтобы написать оператор for в С-подобном языке нам нужно написать сам текст for, затем скобки, параметры, точки с запятыми, фигурные скобки, и нажать клавишу ввод. В нашем же варианте среда программирования по нажатию на определенное сочетание клавиш вставит цикл и потребует только ввести его параметры. Например, по нажатию на «f» поставит на место курсора цикл for и предложит ввести количество повторений и название счётчика через табуляцию, нажав клавишу ввод в конце

Вы про сниппеты никогда не слышали?


В-третьих, можно будет обеспечить независимость от языка программиста.[...] Насчёт имён объектов и методов посложнее, но в любом случае, если программа разрабатывается русскими разработчиками

А с многоязычными командами разработки что делать? А с тем, что порядок слов в разных языках разный?


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

Она и сейчас есть. Возьми исходники, разбери в дерево, обнови, сохрани обратно.


В-пятых представлять структуру программы можно будет любым удобным для того способом.

Она и сейчас есть. Возьми исходники, разбери в дерево, визуализируй как хочется.


В каком-то смысле, ваш подход (редактируй не то, что хранится в файле) напоминает миллион существующих визуальных редакторов (форм, веб-страниц, рабочих процессов, схем БД). И все равно люди, которым нужен контроль за происходящим, спускаются на уровень исходников, потому что визуальный редактор нужного контроля не дает. Так будет и у вас — в итоге вы сделаете еще один язык программирования, такой же, как другие, просто с визуальным редактором.

Blockly и Scratch хороши для детей/начинающих, т.к. внешняя простота достигается за счёт того, что ограничения универсальности спрятаны "под капотом". Импорт нужных модулей, например.
Делали программатор полётов для Bitcraze на Blockly.
Вот как выглядел оригинал на Питоне:


import logging
import sys
import time

import cflib.crtp
from cflib.crazyflie import Crazyflie
from cflib.crazyflie.syncCrazyflie import SyncCrazyflie
from cflib.positioning.motion_commander import MotionCommander
from cflib.utils.multiranger import Multiranger

URI = 'radio://0/80/2M'

if len(sys.argv) > 1:
    URI = sys.argv[1]

# Only output errors from the logging framework
logging.basicConfig(level=logging.ERROR)

def is_close(range):
    MIN_DISTANCE = 0.2  # m

    if range is None:
        return False
    else:
        return range < MIN_DISTANCE

if __name__ == '__main__':
    # Initialize the low-level drivers (don't list the debug drivers)
    cflib.crtp.init_drivers(enable_debug_driver=False)

    cf = Crazyflie(rw_cache='./cache')
    with SyncCrazyflie(URI, cf=cf) as scf:
        with MotionCommander(scf) as motion_commander:
            with Multiranger(scf) as multi_ranger:
                keep_flying = True

                while keep_flying:
                    VELOCITY = 0.5
                    velocity_x = 0.0
                    velocity_y = 0.0

                    if is_close(multi_ranger.front):
                        velocity_x -= VELOCITY
                    if is_close(multi_ranger.back):
                        velocity_x += VELOCITY

                    if is_close(multi_ranger.left):
                        velocity_y -= VELOCITY
                    if is_close(multi_ranger.right):
                        velocity_y += VELOCITY

                    if is_close(multi_ranger.up):
                        keep_flying = False

                    motion_commander.start_linear_motion(
                        velocity_x, velocity_y, 0)

                    time.sleep(0.1)

            print('Demo terminated!')

Вот как выглядит у нас на Блокли:


Летает одинаково, вот так:



Тем кто английского и Питона не знает — да, возможно, чуть понятнее. Но отладить что-то более сложное — это означает не просто отладить код, а ещё и устранить артефакты на этапе конвертации из Блокли в исполняемый код — ученикам это уже не под силу… Так что для начинающих изучать программирование — да, Scratch, но прикладные задачи решать — только разрабатывать такой Scratch для конкретной предметной области, целесообразность чего надо оценивать в каждом отдельном случае…

устранить артефакты на этапе конвертации из Блокли в исполняемый код

Таких просто не должно быть. А с остальным согласен.

Судя по рендерингу шрифтов и цветовой схеме автор использует Brackets. Просто переходите на любой другой редактор и отношение к текстовым языкам изменится!
Ну а вообще по теме визуала более интересным выглядит что-то в стиле JetBrains MPS
www.youtube.com/watch?v=XolJx4GfMmg
Шо, опять? Сколько уже её было, этой визуальщины, но ЧСХ, подавляющая часть ПО как писалась, так и пишется на нормальных языках.

Пожалуйста, представьте следующий алгоритм в виде блоков и потом на разных языках. Допустим, на хаскеле и си (или, если хотите, лиспе и паскале)


def filter_strings(data):
   return [
     x for x in data if isinstance(x, str)
   ]

data = '[1, 2, "hello"]'
smth = filter_strings(json.loads(data))
Надо было сразу так что бы ТС не жалеть:
smth = list(filter(lambda x: isinstance(x, str), json.loads('[1, 2, "hello"]')))

А если по сути, то в вашем примере нет ничего такого кроме вызова готового модуля, встроенной функции и генератора списков.

Ну я все таки старался написать читаемый код.
И в нем есть ещё одна важная вещь: проверка типа переменной в рантайме

Ваш код довольно-таки тривиально транслируется в цикл с if'ом. А могло быть и хуже:


немного комбинаторных парсеров
object FormulaParser extends RegexParsers with PackratParsers {

    def id: Parser[Id] = "[a-zA-Z][a-zA-Z0-9_]*".r ^^ Id

    def number: Parser[Number] = "-" ~> number ^^ (n => Number(-n.value)) |
        ("[0-9]+\\.[0-9]*".r | "[0-9]+".r) ^^ (s => Number(s.toDouble))

    def funcCall: Parser[FuncCall] = id ~ ("(" ~> expression <~ ")") ^^ {case id ~ exp => FuncCall(id, exp)}

    def value: Parser[Expression] = number | funcCall | id | ("(" ~> expression <~ ")")

    lazy val term: PackratParser[Expression] = term ~ ("*" | "/") ~ value ^^ binOperation | value

    lazy val expression: PackratParser[Expression] = expression ~ ("+" | "-") ~ term ^^ binOperation | term

    private def binOperation(p: Expression ~ String ~ Expression) = p match {
        case e1 ~ op ~ e2 => BinOperation(e1, BinOperator(op), e2)
    }

    def apply(code: String): Either[ParserError, Expression] =
        parse(expression, new PackratReader(new CharSequenceReader(code))) match {
            case Success(result, next) => Right(result)
            case NoSuccess(msg, next) => Left(ParserError(msg))
        }

    case class ParserError(msg: String)
}

sealed trait Expression

case class BinOperator(operator: String)

case class Number(value: Double) extends Expression
case class Id(name: String) extends Expression
case class BinOperation(left: Expression, op: BinOperator, right: Expression) extends Expression
case class FuncCall(funcName: Id, argument: Expression) extends Expression

object Evaluator {
    def apply(expression: Expression,
              variables: (String) => Double = Map.empty,
              functions: (String) => (Double) => Double = Map.empty): Double = {

        def eval(exp: Expression) = this (exp, variables, functions)

        expression match {
            case Number(value) => value
            case Id(name) => variables(name)
            case BinOperation(left, op, right) => operator2func(op)(eval(left), eval(right))
            case FuncCall(funcId, expr) => functions(funcId.name)(eval(expr))
        }
    }

    def operator2func(binOperator: BinOperator): (Double, Double) => Double =
        binOperator.operator match {
            case "+" => (a, b) => a + b
            case "-" => (a, b) => a - b
            case "*" => (a, b) => a * b
            case "/" => (a, b) => a / b
        }
}

object Main extends App {
    def eval(code: String,
             variables: (String) => Double = Map.empty,
             functions: (String) => (Double) => Double = Map.empty) = {
        val parsed = FormulaParser(code)
        parsed.left.foreach(error => println(s"\'$code\' parsing error: $error"))
        parsed.right.map(expr => Evaluator(expr, variables, functions)).foreach(d => println(s"\'$code\' = $d"))
    }

    eval("1")
    eval("0.1")
    eval("1.")
    eval("  1  ")
    eval("-0.1")

    eval("1+2")
    eval("2-1")
    eval("2*3")
    eval("4/2")

    val vars = Map(
        "pi" -> math.Pi,
        "e" -> math.E)

    val funcs: (String) => (Double) => Double = Map(
        "sin" -> math.sin,
        "cos" -> math.cos,
        "inc" -> { d: Double => d + 1 }
    )

    eval("pi", vars)
    eval("inc(e)", vars, funcs)

    eval("2+2*2")
    eval("1+2*(3+4*5)")
    eval("8/2/2")
    eval("8-1-2")

    eval("1. + 2.0 * sin(pi / 2)", vars, funcs)
}

(взято отсюда)


При желании, наверно, тоже можно транслировать в блоки, но если такого кода десятки тысяч строк — текст будет занимать намного меньше места.


Но с другой стороны, блоки — тоже DSL, только графический, а не текстовый.

Ваш код довольно-таки тривиально транслируется в цикл с if'ом.

Это пока в целевом языке есть возможность определить тип объекта во время выполнения.


(я тоже с первого раза не заметил, в чем дело)

Собственно нет ни какой проблемы реализовать это же в «блоках» если будет такой функционал.
Проблема в том что ТС во первых решает проблему которой нет, второе ТС не особо понимает почему же кроме различий в синтаксисе есть разные языки и почему тот или иной используется в той или иной сфере.
Собственно нет ни какой проблемы реализовать это же в «блоках» если будет такой функционал.

Ну то есть фактически понадобится написать для каждого языка свою собственную дополнительную "стандартную" библиотеку, чтобы покрыть такую функциональность.


Объем работы — это тоже проблема.

Лично меня больше волнует две вещи. Первое, графическая компоновка блоков чрезмерное усложнение — это не быстрее и не проще чем писать код в хорошей IDE. Второе слишком высокий уровень абстракции, когда от компоновки готовых блоков будет очень тяжело перейти к написанию собственных(у меня есть собственный печальный опыт).
А свою стандартную библиотеку для всех языков не обязательно писать, скорей всего придётся делать свой интерпретатор с преферансом и девушками лёгкого поведения, а если опираться на существующие языки, то можно выбирать язык в зависимости от необходимых фич, и просто обеспечить согласованность работы и передачи данных между блоками. Как ТС это собирается это сделать у него уже спрашивали.
Скорее всего писать «обертку» для каждого языка придется, ибо не все языки имеют одинаковые возможности и средства написания. Т.е. сперва привести к общему знаменателю.
Конечно можно написать и интерпретатор для каждого языка, но сути это не меняет. Производительности таким образом теоретически можно добитться большей, но сложность реализации на порядок возрастет, на мой взгляд.
Вы хотите отзыв? он есть у меня!

Причем не умозрительные рассуждения, а опыт трех лет практической работы с подобным «конструктором Лего» (конкретно — с Intershop Enfinity Suite). И я скажу так — это худшее, что со мной случалось за весь мой 25-летний стаж работы программистом.

Хорошего можно сказать только одно — это красиво выглядит. ВСЕ!

Дальше идут только одни проблемы:
— передача данных между блоками. Реализована в виде банального dictionary — то есть прощай типизация. Чтобы понять, что идет на выход из одного блока, и что идет на вход другого — изучай properties каждого блока. Отдельный привет, если данные не совпадают по формату — приходится лепить промежуточный блок для конвертации. А теперь мысленно представьте, какую площадь занимает Map<ID, List> -> List. То, что на чистой джаве укладывается в пять строчек, занимает пол-экрана.

— найти нужный блок. Медитируешь, пытаясь понять, какой из 20 блоков xxxPricexxx тебе нужен — а в итоге выясняется, что нужен какой-нибудь applyVAT

— громоздкость. Отлично, когда диаграмма простенькая, линейная и укладывается в в один экран. Но, с более-менее сложной логикой… это постоянный скроллинг в стороны, изменения масштаба и прочие удовольствия.

— ну, и least but not the last — code merge. Это просто аццкий сотона в кубе! Какие-то конфликты сложнее «добавить один блок посередине» можно решить, только очищая диаграмму, а потом ручками накатывая на нее блоки. Иначе либо отказ в автоматическом мерже (в лучшем случае), либо абсолютная каша из двух веток с совершенно рандомными переходами.

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

Повторюсь еще раз — это не теоретические мысли, это опыт практического использования такой системы во вполне реальных проектах. Упаси Боже от таких «улучшений»!
Sign up to leave a comment.

Articles