Re[46]: Есть ли вещи, которые вы прницпиально не понимаете...
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 13.01.14 07:33
Оценка:
Здравствуйте, alex_public, Вы писали:

I>>Реальность примерно такая — на динамических языках как правило надо кода гнать в разы меньше, а иногда даже на порядки. Собтсвенно смотри сам — спирит сливает даже самопалу на JS.


_>Вот это уже действительно похоже на сказки. )))


Это необходимость. Без этого динамические языки в принципе нежизнеспособны.
Re[48]: Есть ли вещи, которые вы прницпиально не понимаете...
От: akava Беларусь kavaleu.ru
Дата: 13.01.14 08:00
Оценка:
Здравствуйте, AlexRK, Вы писали:

A>>Если есть операция F(t1,t2,...)над элементами типа T (или нескольких типов T1, T2, T3, ...), то та же самая операция над optional<T> возвращает null если хоть одно из значений null и F(t1,t2,...) все значения не null.


ARK>А если мне это не нужно? Может быть, мне нужно заменить отсутствующие значения на нули. А может в одном месте надо заменить на нули, а в другом — вернуть нулл, если хоть одно нулл.

Тогда ты неверно выбрал тип. Если тебе нужны 0 вместо null, то ты так и пишешь: list.map(e=> e.val()??0) Либо пишешь новую монаду которая знает о твоем желании.

ARK>А монада берет и лихо за меня принимает решения во всех местах. Причем я даже не вижу, в каких, все ведь компилится.

Ты решаешь какую монаду выбрать. И делаешь это осознанно.

СУВ, akava
Re[48]: Есть ли вещи, которые вы прницпиально не понимаете...
От: akava Беларусь kavaleu.ru
Дата: 13.01.14 08:19
Оценка:
Здравствуйте, alex_public, Вы писали:

_>Здравствуйте, akava, Вы писали:


A>>Тогда код Синклера никак не поменяется, но использоваться будет новая функция, созданная компилятором.


_>Тот его код как раз очень даже поменяется, т.к. нам в начале надо будет выделить из него функцию (причём только с параметрами типа big_int).

Согласен, что в реальности этот код должен поменяться, но мы в философии, поэтому можем пофантазировать.
Например, предположим, что компилятор сам умеет делать из функции F функцию F* над optional<> аргументами. По принципу: все не null -> вычисляем F, что-то null -> результат null.

Тогда код Синклера:
public T DoStuff<T>(List<T> array, T limit) {where T has '*' and '+' and '>'}
{
  var sum = default(T);    // вот тут у меня косяк, так как default(optional<T>) вернет null, но, уверен, он решаем
    for(int i=0; i<array.length(); i++)
    {
        if (array[i]*array[i] > limit))
            sum+=array[i]*array[i];
    }
    
    return sum;
}

останется без изменений, потому как компилятор (см. выше) сделает нам сложение, умножение и сравнение на optional<> (сравнение по принципу если чт-то null, то false, как в sql).

Я, кнечно, могу ошибаться, но я не вижу переколбашивания. Если у T есть сложение, умножение и сравнение, то функция работает. Монада же дает эти операции "на лету" функций над базовым типом.

СУВ, akava
Re[48]: Есть ли вещи, которые вы прницпиально не понимаете...
От: akava Беларусь kavaleu.ru
Дата: 13.01.14 09:00
Оценка: +1
Здравствуйте, AlexRK, Вы писали:

ARK>А особенно хорошо, если язык поддерживает вывод типов. Изменив, например, тип результата функции, мы разом изменяем семантику кода во многих местах. А компилятор нам не подскажет — семантика изменилась, но синтаксис подходит и старый...

Да, на протяжении всей цепочки, за счет вывода типов, все значения обернутся в монады. Но в конце цепочки, где мы будем использовать результат (вызов вэбсервиса, например) компилятор ругнется и скажет, что такая-то функция не умеет работать с типом optional<T>.

Особенно хорошо это работает с монадой IO или "Async" (еще одна из вещей, которая стала у меня на месте благодаря этому топику):
Есть цепочка:
m = a+b*c
n = m/a-b;
p = m * n;

// ....

ws.Call(p, n, 1);
// или
Console.Write("Result is " + p);

И вместо a мы передали IO{int} (я ее понимаю так: в a сейчас значения нет, но если понадобиться, то я его спрошу у пользователя) или Future{int}. Тогда вся цепочка завернется в монаду. Но ws.Call не умеет работать с IO или Future, поэтому компилятор ругнется и нам придется писать код, который достает значение из монады. Причем, тут может возникнуть эксепшен. Пользователь ввел не int, либо Future закончилось таймаутом. Этот момент нужно обрабатывать.

С Console.Write сложнее. У всех методов есть ToString(). Поэтому компилятор не ругнется.
У монады он, скорее всего реализован в виде кода, получающего значение из нее. А значит, мы можем получить эксепшен в месте, которое выглядит как место где НЕ МОЖЕТ быть исключения. Вот это косяк и как его фиксить я действительно ХЗ.

Еще один косяк с операциями, возвращающими bool. Например сравнение.

if (m > limit) 
    Alert();
if (!(m <= limit)) 
    Alert();

Что тут делать? Если мы определим '>' над optional<> как false, в случае когда o == null, то первая инструкция будет игнорировать null, а вторая сообщать о них.
Т.е. нужно будет об этом задумываться. А если нужно будет задумываться, то когда-нибудь, какой-нибудь разработчик Леша заменит первый вариант на второй (или наоборот) и изменит семантику кода. Вот это тоже КОСЯК.


Блин, чем больше в лес, тем дальше вылез
Товарищи опытные "монадоведы", расскажите плиз, что в таких случаях делать? Сразу хочется работать с монадоми вручную, без компилятора, но тогда монады теряют весь смысл. Единственное, что я смог придумать это принцип: если нужно значение из монады, то получай его явно (т.е. вручную).

Уфф, нифигассе я настрочил.



ARK>Ну хз, может я слишком перестраховываюсь, ведь эти рассуждения справедливы и в других случаях (Int32 заменили на Int64, например).

Я сам ХЗ. Не нужно воспринимать мои коментарии как "эгегей, монады куль, почему никто не использует монады...". Я сам только к ним присматриваюсь (как к новой для меня концепции).
Аргументы выше, это то, что я сам себе ответил, когда задался таким вопросом.


СУВ, akava
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
От: akava Беларусь kavaleu.ru
Дата: 13.01.14 09:14
Оценка:
Здравствуйте, akava, Вы писали:

A>Особенно хорошо это работает с монадой IO или "Async" (еще одна из вещей, которая стала у меня на месте благодаря этому топику):

Кстати, монаду IO я, на данный момент, воспринимаю как заморочки в угоду чистоте функций. А вот монаду Async я очень уважаю. Асинхронный код в ее случае становиться выно проще.

СУВ, akava
Re[42]: Есть ли вещи, которые вы прницпиально не понимаете...
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 13.01.14 09:23
Оценка:
Здравствуйте, alex_public, Вы писали:

I>>Не проще В моем случае надо понять только грамматику. В твоем — дополнительно к этому приседания с состоянием.

I>>Да и вообще, сколько смотрю, вот не могу понять, как твой вариант

_>Ну по крайне мере он заметно короче. ))) А остальное уже дело вкуса. )


Он короче, чем многословное решение на query comprehension. Решение на Хаскеле по краткости ты сильно вряд ли обгонишь, но я не могу его показать, Хаскелем не владею как то.

I>>Это абсолютно неважно — ты сказал что монады не нужны и сам же в конце концов предложил использовать монаду.


_>Это ты короутины что ли монадами обозвал? )


Это без пяти минут монада.

>При таком раскладе тогда надо считать и все коллекции в stl монадами, да исключения тогда тоже монада и т.п...


Про коллекции и исключения тебе уже рассказали недели три назад.

>Весь C++ тогда состоит из сплошных монад. )))


Да, монады встроены в язык.

>Но мы вроде как в этой темке обсуждали явные монады с вполне конкретными свойствами, а не некую их аналогию из императивных языков. Так вот явные монады в C++ ввести можно без проблем (как показывалось уже в этой темке), но короутины, исключения или скажем std::list — это явно не оно. )))


Чего ты хочешь объяснить, я уже не понимаю. Большей частью у тебя сравнение query comprehension С# vs императивный код на С++.

I>>Твоя функция Analyze выдохнется через полтора дня, когда окажется, что требования поменялись и все надо будет переписать. А без короутины количество кода будет просто конским.


_>Пока что практика показывает обратное. )


Значит ты какие то не те примеры приводишь, ибо твои надо еще допиливать, что бы они обладали хотя бы половиной полезных свойств, как хотя бы то многословное на query comprehension.
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
От: Evgeny.Panasyuk Россия  
Дата: 13.01.14 09:43
Оценка: 6 (1)
Здравствуйте, akava, Вы писали:

A>Блин, чем больше в лес, тем дальше вылез

A>Товарищи опытные "монадоведы", расскажите плиз, что в таких случаях делать? Сразу хочется работать с монадоми вручную, без компилятора, но тогда монады теряют весь смысл. Единственное, что я смог придумать это принцип: если нужно значение из монады, то получай его явно (т.е. вручную).

Так его и получают вручную, даже в Haskell. Например было:
foo a b = a + b
чтобы эта функция работала с монадическими значениями "a" и "b" внутри (а не просто вытащить все значения снаружи и вызвать эту функцию), её нужно расчленить на лямбды-продолжения:
foo a b = a >>=
          (
              \x ->
                  b >>= 
                  (
                      \y ->
                          return x + y
                  )
          )
можно немного переформатировать:
foo a b = a >>= \x ->
          b >>= \y ->
          return x + y
Либо использовать синтаксический сахар do:
foo a b = do x <- a
             y <- b
             return a + b
(для этого конкретного случая достаточно аппликативного функтора вместо монады, но не суть)
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
От: AlexRK  
Дата: 13.01.14 09:45
Оценка:
Здравствуйте, akava, Вы писали:

A>Тогда ты неверно выбрал тип. Если тебе нужны 0 вместо null, то ты так и пишешь: list.map(e=> e.val()??0) Либо пишешь новую монаду которая знает о твоем желании.


Раньше никаких нуллов не было. А потом все изменилось, и надо проанализировать все места. А эти места компилятор не покажет. Ну, какие-то покажет, а какие-то нет.

A>Ты решаешь какую монаду выбрать. И делаешь это осознанно.


Осознанно я выбираю только на самом высоком уровне. А потом все происходит автоматом и компилятор далеко не везде подскажет, что что-то изменилось. Пусть я отдаю себе отчет в том, что семантика изменилась, но перелопатить гору кода мне это знание не поможет.
Re[48]: Есть ли вещи, которые вы прницпиально не понимаете...
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.01.14 09:45
Оценка: 2 (1)
Здравствуйте, alex_public, Вы писали:
_>Тот его код как раз очень даже поменяется, т.к. нам в начале надо будет выделить из него функцию (причём только с параметрами типа big_int). Т.е. например весь кусок этого кода не получится взять, а можно только то, что внутри for'a. Ну и соответственно надо будет поменять переменные на параметры функции и т.п... В общем смысловая то часть естественно не поменяется, но переколбасить код надо будет очень существенно.
По факту, в C# код Синклера никак не поменяется. Потому что ECMA 334:14.2.7 Lifted Operators
В теории, хочется того же самого для пользовательских типов, а не только для Nullable<T>, оборудованного Compiler Magic.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[50]: Есть ли вещи, которые вы прницпиально не понимаете...
От: akava Беларусь kavaleu.ru
Дата: 13.01.14 09:50
Оценка:
Здравствуйте, AlexRK, Вы писали:

A>>Ты решаешь какую монаду выбрать. И делаешь это осознанно.


ARK>Осознанно я выбираю только на самом высоком уровне. А потом все происходит автоматом и компилятор далеко не везде подскажет, что что-то изменилось. Пусть я отдаю себе отчет в том, что семантика изменилась, но перелопатить гору кода мне это знание не поможет.

Давай вот тут
Автор: akava
Дата: 13.01.14
продолжим разговор. Как раз там я нашел два места в которых компилятор не поможет.
Я не знаю как это пофиксить. Может вместе что-нить придумаем. А может кто-то подскажет.

СУВ, akava
Re[50]: Есть ли вещи, которые вы прницпиально не понимаете...
От: akava Беларусь kavaleu.ru
Дата: 13.01.14 09:56
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, akava, Вы писали:


A>>Блин, чем больше в лес, тем дальше вылез

A>>Товарищи опытные "монадоведы", расскажите плиз, что в таких случаях делать? Сразу хочется работать с монадоми вручную, без компилятора, но тогда монады теряют весь смысл. Единственное, что я смог придумать это принцип: если нужно значение из монады, то получай его явно (т.е. вручную).

EP>Так его и получают вручную, даже в Haskell. Например было:

EP>
EP>foo a b = a + b
EP>
чтобы эта функция работала с монадическими значениями "a" и "b" внутри (а не просто вытащить все значения снаружи и вызвать эту функцию), её нужно расчленить на лямбды-продолжения:

Ага т.е. сам код, который теоретически будет работать с монадой должен быть напсан специальным образом. Надо обдумать.

Евгений, ты не мог бы мой код из сообщения выше перевести на хаскель, чтобы он поддерживал манады IO и Future? (У Future принцип такой же как в IO только значение дает асинхронная операция).
m = a+b*c
n = m/a-b;
p = m * n;

// ....

ws.Call(p, n, 1);
// или
Console.Write("Result is " + p);

Насколько он станет сложнее. Скажем, a, b и c могут стать монадой. Было бы очень интересно.

СУВ, akava
Re[51]: Есть ли вещи, которые вы прницпиально не понимаете...
От: Evgeny.Panasyuk Россия  
Дата: 13.01.14 13:29
Оценка: 9 (1)
Здравствуйте, akava, Вы писали:

A>Ага т.е. сам код, который теоретически будет работать с монадой должен быть напсан специальным образом. Надо обдумать.


Да, именно так — он должен быть распилен на вложенные продолжения.

A>Евгений, ты не мог бы мой код из сообщения выше перевести на хаскель, чтобы он поддерживал манады IO и Future? (У Future принцип такой же как в IO только значение дает асинхронная операция).

A>
A>m = a+b*c
A>n = m/a-b;
A>p = m * n;

A>// ....

A>ws.Call(p, n, 1);
A>// или
A>Console.Write("Result is " + p);
A>

A>Насколько он станет сложнее. Скажем, a, b и c могут стать монадой. Было бы очень интересно.

Примерно так:
foo a b c = do x <- a
               y <- b
               z <- c
               -- again, applicative is enough for this case
               let m = a+b*c
               let n = m/a-b
               let p = m * n
               return print "Result is " ++ show p
               -- ^^^ do not need return for IO monad, just:
               -- print "Result is " ++ show p
Если a, b, c это IO — то "return" можно убрать.
Никакой магии — мы явно нарезаем код на continuations, do-нотация просто помогает убрать несколько буковок.
Re[35]: Есть ли вещи, которые вы прницпиально не понимаете...
От: alex_public  
Дата: 13.01.14 16:36
Оценка:
Здравствуйте, meadow_meal, Вы писали:

_>Отвечу как практик на эрланге, языке относительно бедном, поэтому ответ будет "от противного".


Эммм, это как бы снова не то, что я спрашивал. ))) Ну да ладно. )

_>Во-первых, я хочу гарантировать чистоту processEventOrFail относительно определенных типов эффектов (они должны быть аккумулированы в Effects, в то время как другие — такие как чтение разделяемых глобальных данных или логирование — допустимы) на стадии компиляции или статического анализа. В эрланге это как минимум сложно, приходится решать административно.


Я так понимаю, это пожелание иметь "чистые функции" в языке? Да, в C++ они действительно только административно вводятся. А вот например в языке D они есть (причём за этот факт ничем расплачиваться не надо), но я что-то не видел особых чудес от этого.

_>Во-вторых, я вынужден вносить Effects внутрь State, так как иначе связывание кода внутри processEvent становится очень громоздким засчет необходимости передавать, возвращать и аккумулировать Effects. Это уродливый костыль с очевидными недостатками (например невозможно явно отличить функции State -> State с эффектами от функций без эффектов), но видимо лучшее практическое решение в отсутствие монад.


Ну начнём с того, что мы изначально здесь рассматриваем довольно узкий случай, когда "ValidateEvent" нельзя отделить от "ProcessEvent". Т.к. в противном случае мы для этой задачки просто берём какую-нибудь библиотечку конечных автоматов и записываем код в виде таблички вида:
state1+event[guard]/action==state2

Это вот прямо код на C++, где state1, event, state2 — это классы, а guard и action — функции (ну естественно могут быть функторы, лямбды и т.п.). При такой записи мы автоматом получаем нужное нам разделение и даже гораздо больше этого.

_>А в хаскелле и то и другое не проблема.


Пока что не увидел нигде реальных примеров этого.
Re[47]: Есть ли вещи, которые вы прницпиально не понимаете...
От: alex_public  
Дата: 13.01.14 16:45
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>В том-то и дело, что в языках без поддержки монад приходится вот так вот извращаться. Как только мы выйдем за пределы начальной школы, где формулы ограничены двумя аргументами, у нас объём кода, привнесённого apply, начнёт превышать объём оригинального кода.

S>А в языке, где монады — первоклассная сущность, весь heavy lifting должен делать компилятор.

Эээ, что что? ) Насколько я понимаю, "главный" язык для работы с монадами у нас Хаскель. Так вот в нём компилятор не делает ничего подобного, а надо записывать всё руками.

S>Да, в С++ конкретно это место будет работать — потому что неинстанциированные шаблоны не компилируются, поэтому отсутствующие для T операторы будут отсутствовать и для optional<T>. К сожалению, это не коснётся пользовательских функций, поэтому прикладному программисту придётся выписывать либо Apply руками в каждое место применения, либо руками лифтить все нужные функции (см. System.Math для маленького примера. Реальная мат.библиотека будет содержать на порядок больше)


Ну для начала, если мы говорим о C++, то скажем все наши функции реализованные в виде шаблонов заработают сами автоматом. )))

Ну а нешаблонные действительно надо оборачивать в Apply. Так же как и в Хаскеле (только там в bind). И кстати в Хаскеле это единственный вариант, в отличие от C++.

S>Это всё паллиатив. Хардкорность монад — именно в том, что они имеют специальную семантику, и предоставляют определённые гарантии. Благодаря этому компилятор может втыкать Apply там, где нужно, не дожидаясь ручной подсказки от программиста.


Ээээ что что? ) Это в каком языке компилятор так умеет? )
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
От: alex_public  
Дата: 13.01.14 16:47
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вот я как раз хочу, чтобы не надо было переколбашивать. И мне кажется, что как раз монады — путь к успеху.


Единственный вариант, чтобы совсем не переколбашивать, это сочетание перегрузки операторов и полноценных шаблонов.
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
От: alex_public  
Дата: 13.01.14 18:07
Оценка:
Здравствуйте, akava, Вы писали:

A>Тогда код Синклера:

A>
A>public T DoStuff<T>(List<T> array, T limit) {where T has '*' and '+' and '>'}
A>{
A>  var sum = default(T);    // вот тут у меня косяк, так как default(optional<T>) вернет null, но, уверен, он решаем
A>    for(int i=0; i<array.length(); i++)
A>    {
A>        if (array[i]*array[i] > limit))
A>            sum+=array[i]*array[i];
A>    }
    
A>    return sum;
A>}
A>

A>останется без изменений, потому как компилятор (см. выше) сделает нам сложение, умножение и сравнение на optional<> (сравнение по принципу если чт-то null, то false, как в sql).

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

Поясняю разница в подходах:
1. Монады. Определяется ровно одна функция и всё. Дальше, с помощью неё можно разрулить абсолютно все ситуации (в этом и фокус как бы). Но вставлять эту функцию по коду надо руками.
2. Перегрузка операторов. Надо определить кучу операторов для каждого вида монады. И плюс возможно перегрузить несколько функций (те, которые определены не как шаблонные и соответственно не могут сами подхватить новый тип). Но зато после этого, никаких изменений в коде не потребуется вообще. Ни в одной букве.

A>Я, кнечно, могу ошибаться, но я не вижу переколбашивания. Если у T есть сложение, умножение и сравнение, то функция работает. Монада же дает эти операции "на лету" функций над базовым типом.


Монада ничего подобного не даёт. Ну точнее она нам даёт автоматическую реализацию этих функций, но расставить их вызовы в коде надо руками.
Re[50]: Есть ли вещи, которые вы прницпиально не понимаете...
От: alex_public  
Дата: 13.01.14 18:18
Оценка: 3 (1)
Здравствуйте, alex_public, Вы писали:

_>Поясняю разница в подходах:

_>1. Монады. Определяется ровно одна функция и всё. Дальше, с помощью неё можно разрулить абсолютно все ситуации (в этом и фокус как бы). Но вставлять эту функцию по коду надо руками.
_>2. Перегрузка операторов. Надо определить кучу операторов для каждого вида монады. И плюс возможно перегрузить несколько функций (те, которые определены не как шаблонные и соответственно не могут сами подхватить новый тип). Но зато после этого, никаких изменений в коде не потребуется вообще. Ни в одной букве.

Да, и выходит что самый оптимальный вариант получается в языке D, где мы можем переопределить все операторы для данного типа в одной функции. Если при этом ещё и все используемые функции являются шаблонными, то мы получаем полностью автоматическую замену T на optional<T> путём написания ровно одной функции и без модификации кода вообще.
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
От: AlexRK  
Дата: 13.01.14 18:25
Оценка:
Здравствуйте, akava, Вы писали:

A>Да, на протяжении всей цепочки, за счет вывода типов, все значения обернутся в монады. Но в конце цепочки, где мы будем использовать результат (вызов вэбсервиса, например) компилятор ругнется и скажет, что такая-то функция не умеет работать с типом optional<T>.


Где-то может и уметь — совершенно случайно.

A>Блин, чем больше в лес, тем дальше вылез


Да, я про это как раз и говорю. Но, судя по дальнейшему обсуждению, монады таки не предполагают волшебного изменения кода...
Re[36]: Есть ли вещи, которые вы прницпиально не понимаете...
От: AlexRK  
Дата: 13.01.14 18:30
Оценка:
Здравствуйте, alex_public, Вы писали:

_>Я так понимаю, это пожелание иметь "чистые функции" в языке? Да, в C++ они действительно только административно вводятся. А вот например в языке D они есть (причём за этот факт ничем расплачиваться не надо), но я что-то не видел особых чудес от этого.


Я так понял, что речь идет не просто о чистых функциях, а о "выборочно чистых", к примеру чистых от работы с диском, но не от сетевых взаимодействий. В принципе, интересная идея, но в чем реальный бенефит выборочной чистоты и не будет ли это выглядеть адски страшно на любом ЯП — мне пока не ясно.
Re[69]: Есть ли вещи, которые вы прницпиально не понимаете...
От: alex_public  
Дата: 13.01.14 18:41
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Безотносительно конкретного вопроса, хотелось бы предостеречь против

S>1. Использования голосований (в т.ч. в виде статистики использования) для решения технических вопросов. Это очевидно нерелевантная аргументация — иначе мы были бы вынуждены признать PHP наиболее технически совершенным средством для веб-разработки.
S>2. Отсылки к промышленным применениям. Природа встраиваемых систем такова, что в них всегда предпочитают надёжное новому. Даже если новое решение в 100 раз лучше существующего, ему потребуется значительное время на внедрение.

Ну да, есть такой нюанс. Но разница между пунктом 1 и тем, что мы тут обсуждали, принципиальная. В веб'е грубо говоря все знают, что есть "плохие" технологии и "хорошие". И "плохие" частенько используются только потому, что это выгоднее (или в начале кажется, что это выгоднее). Но все понимают расклад и держат в уме набор "хороших" технологий, даже если не могут ими пользоваться в текущем проекте. А в робототехнике (практической, а не в чем-то типа ИИ), автоматике и т.п. областях просто нет в принципе каких-то известных "хороших" технологий. Чувствуется разница? )

Соответственно возникает вопрос: они там ещё просто не появились или же может быть оно просто не надо? )
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.