LINUX.ORG.RU

Erlanfg VS Common Lisp VS Haskell

 , ,


1

7

Доброго утра! Хотелось бы услышать «+» и "-" данных языков. Прошу при этом сравнительные качества писать в отношении Эрланга. Основная задача при изучении - взять язык на котором будет потом легко найти работу с достойной оплатой.


Ответ на: комментарий от anonymous

Не только concurrency, но еще и динамической типизации в сочетании с pattern matching.

А функциональщина там вообще не в тему. Как и в Лиспе, кстати, там тоже вся эта функциональщина никому никуда не впилась, главная его фича это макросы.

anonymous
()
Ответ на: комментарий от anonymous

Все языки полезны. Даже unlambda и brainfuck. Но сопливым быдлокодерам этого не понять, потолок сложности задач, которые они решают - CRUD и подобное говно.

anonymous
()
Ответ на: комментарий от Alve

«Найти работу» - это у сопливой школоты такая цель в жизни, да? Смотрят на старших товарищей, строящих блистательные карьеры в макдональдсе, и завидуют? Дебилы...

anonymous
()
Ответ на: комментарий от vertexua

Ну, то, что ты вообще ничего не знаешь, давно тут ни для кого не секрет. Ты и сам своим невежеством гордишься. А Erlang в телекомах очень активно употребляют (собственно, только там ему и место).

anonymous
()
Ответ на: комментарий от anonymous

Сколько ненависти ) В интернетах кто-то не прав?

vertexua ★★★★★
()
Ответ на: комментарий от mv

Символы много где еще есть. Да и нет в них ничего такого уникального и интересного, в статически типизированных языках variants все равно лучше. А вот макросы это действительно сила, просто очень мало кто их понимает и умеет готовить.

anonymous
()
Ответ на: комментарий от quasimoto

В форму TLS.

благодарствую. Однако это означает всё же наличие присваивания, треды - это уже другая песня. А брекпойнт на опрацию put(some_atom) поставить можно? Чтобы она работала либо в выбранных тредах, либо во всех, но только для определённого атома?

records.

Спасибо, неплохо. Только дальше идет то же самое г, что и в лиспе.

Accessing Record Fields
Expr#Name.Field

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

(let-with-conc-type s string "asdf"
   s^UPCASE
   )

Не обязательно всё извлекать из оригинала.

Ну, это уже детали реализации. На самом деле реализация может и запись в исходную структуру делать, если доказуемо, что старая больше никогда не понадобится. Своего рода аналог tail recursion. Соответственно, тут вопрос,можно ли поставить брекпойнт на вызов, при котором в копию записывается иное значение поля field?

А Хаскелисты что-то совсем исчезли. Им брекпойнты «не нужны»?

den73 ★★★★★
()
Ответ на: комментарий от mv

главная его фича это макросы.

Символы его главная фича.

Мне это напоминает обсуждение волшебника Гудвина его посетителями.

den73 ★★★★★
()
Ответ на: комментарий от den73

(let ((stdin (read-stdin))) (main stdin)) ссылка stdin далее может свободно >передаваться как аргумент между функциями, зачем тут присваивание?

Это называется «мобильный ракетный комплекс».

Присваивание на самом деле тут есть, но объект живёт на стеке. А теперь задача: есть 100 функций, между которыми этот stdin всё время гуляет в качестве параметра. Какая-то функция читает из него лишний символ, а какая - непонятно. Помимо этого, есть ещё множество других потоков.

Предложите методику поиска такой ошибки в Хаскеле и Эрланге? Именно эту ошибку в лиспе можно найти, т.к. чтение - это всё же функция, а объект сохраняет идентичность. Мы можем поставить условный брекпойнт на примитив чтения (типа read-char). Хотя на самом деле, примитивов чтения несколько и брекпойнт надо ставить на них на все, но это тоже осуществимо.

Если поток притворяется функционально чистым за счёт того, что любая читающая функция возвращает новый instance потока (по-моему, это - типичный новояз функциональщиков), мы должны обернуть примитив чтения (read-char) в некую оболочку, которая отлавливает на выходе новый поток, который эта функция вернула, и меняет определение брекпойнта, чтобы он срабатывал и на новый поток. Наверено, это тоже можно автоматизировать, хотя и не так легко. При условии, что отладчик вообще позволяет обернуть функцию, но этого я не знаю в случае Erlang и Хаскель (в лиспе вроде можно).

Если вдруг над потоком возможны ещё какие-то, по смыслу, деструктивные операции, например, изменение какого-то фильтра, которые тоже лицемерно представлены копированием, то все эти функции нужно тоже обернуть в нашу обёртку. А как мы их все выявим? Напоминает борбу Геркулеса с гидрой, но не факт, что победит Геркулес.

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

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

А программист на C просто поставит брекпойнт на доступ к памяти...

Интересны ваши решения.

З.Ы.кстати, даже в лиспе возможно нарушение идентичности потока, т.к. есть synonym-stream и concatenated-stream. Т.е., оборачивание и изменение брекпойнта понадобятся даже в лиспе, хотя он всё же язык императивный.

den73 ★★★★★
()
Ответ на: комментарий от den73

А брекпойнт на опрацию put(some_atom) поставить можно?

Можно (int, debugger), в том числе на удалённых нодах. Точнее, можно поставить на любую «выполняемую линию», функцию (просто на первую «выполняемую линию» функции), возможно, с условием, в любом интерпретируемом модуле.

Чтобы она работала либо в выбранных тредах

Если получится в функции int:test_at_break реализовать логику выбора pid-ов. Это, видимо, предполагает общение со специальным сервером который должен запускать процессы (чтобы он знал нужные pid-ы), ставить условные break-и ассоциативно с pid-ами и отвечать тестирующим функциям подходит ли переданный self.

либо во всех, но только для определённого атома?

put_atom(Value) -> put(atom, Value). и ставить break на put_atom. Либо ставить на весь put (или аналог) int:test_at_break в сочетании с проверкой нужного атома с помощью int:get_binding.

Только дальше идет то же самое г, что и в лиспе.

В лиспе с CLOS есть :accessor, так что проговаривать имя класса при обращение к слоту не нужно - аккессоры могут быть полиморфными.

А Хаскелисты что-то совсем исчезли. Им брекпойнты «не нужны»?

Там такой же интерпретатор с дебагером как и в Эрланге, с возможностью делать :b переменная/мутатор/любая другая функция. Только он не работает :) По крайней мере, для наблюдения, отлаживания или горячего обновления сколько-нибудь сложной программы (с IO/STM и «переменными», да) он не предназначен. Скорее как некий shell на хаскеле - модули туда загружать, тесты запускать, функции пробовать.

quasimoto ★★★★
()

На мой взгляд, вакансий на Haskell больше чем на CL. Но с другой стороны, очень странный выбор языков для данной задачи...

nCdy
()
Ответ на: комментарий от den73

Присваивание на самом деле тут есть, но объект живёт на стеке.

Семантически это несколько отличная от присваивания вещь - let binding или инициализация. Например:

const struct Global {
    FILE *stdin;
    // ...
} global = {
    stdin
    // ...
};

    // global.stdin = stdout;
    // ^ assignment of member «Global::stdin» in read-only object

тут это ещё и инициализация _глобального_ объекта, но не присваивание.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Либо ставить на весь put (или аналог) int:test_at_break в сочетании с проверкой нужного атома с помощью int:get_binding.

Вот это кажется приемлемым, а замена кода, с практической точки зрения - не самый лучший путь. Но что делать в случае, если у нас не поток, а произвольная структура и нам нужно отследить только некоторые экземпляры? Ставить брекпойнт на копирование структуры, проверять идентичность структуры (с учётом того, что при копировании она может ускользнуть), искать имя... Нда, не слишком практично, мягко говоря.

И вообще я хотел простую вещь: брекпойнт на let и setf для special variable в лиспе, а не эти ужасы.

В лиспе с CLOS есть :accessor, так что проговаривать имя класса при обращение к слоту не нужно - аккессоры могут быть полиморфными.

Ну, на практике это далеко не так. Если, допустим, заходим сделать в классе поля first и second, нас ждут неприятности. Или если в одном классе нам нужна функция foo (x), а во втором - слот foo. Кроме того, полиморфизм тянется туда, где он не нужен, а это имеет два вида последствий: тормоза и усложнение интроспекции. Я стараюсь пользоваться только структурами с «крышечками», хотя это и в ущерб динамизму и иногда приходится делать лишнюю пересборку из-за того, что defstruct не обновляет экземпляры. Может, надо написать «defsturct over defclass» для отладки?

Только он не работает :)

Ха-ха-ха :)

den73 ★★★★★
()
Ответ на: комментарий от quasimoto

тут это ещё и инициализация _глобального_ объекта, но не присваивание.

Да в общем-то, идеи чистого ФП мне знакомы и понятны, и польза от них тоже ясна, можно не объяснять. В данном случае, нам повезло в том, что у нас есть только один stdin, а могло и не повезти.

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

den73 ★★★★★
()
Ответ на: комментарий от den73

Но что делать в случае, если у нас не поток, а произвольная структура и нам нужно отследить только некоторые экземпляры?

Как я понимаю, мы тут два вопроса обсуждаем - присваивание и дебагер.

put/set это довольно примитивная форма присваивания. С тем же успехом можно завести хэш-таблицу и делать insert/lookup, просто put/set удобнее. А вот произвольные структуры в Эрланге не могут быть мутабельны, ссылок/указателей тоже нет, так что и говорить тут не о чем.

В GHC же есть структуры и полноценные ссылки с семантикой чтения/записи:

struct foo {
    int c, *m;
};

    int z = 0;
    struct foo x = { 0, &z }, *x_ref = &x;
    *x_ref->m = 1;
    struct foo y = { 1, &z };
    x_ref = &y;
    *x_ref->m = 2;

аналогично:

data Foo = Foo {
  c :: Int,
  m :: IORef Int
}

  z :: IORef Int <- newIORef 0
  let x = Foo 0 z
  x_ref <- newIORef x
  x_value <- readIORef x_ref
  writeIORef (m x_value) 1
  let y = Foo 1 z
  writeIORef x_ref y
  x_value <- readIORef x_ref
  writeIORef (m x_value) 2

writeIORef x_ref и writeIORef (m x_value) вполне могут происходить in-place так как в конечном итоге превращаются в примитивы RTS (а IORefы реализует и управляет он).

Теперь дебагер - нужна возможность ставить breakи на линии и имена функций, возможность добавлять произвольные условия, обозревать состояние при остановке и итерировать/продолжать выполнение. Data breakpoints при этом сводятся к breakам на примитивы вроде put/set или writeIORef/readIORef.

В Эрланге есть адекватный дебагер, но нет ссылок и мутабельность ограничивается примитивными put/set. В GHC есть ссылки, но дебагер ведёт себя довольно странно в контексте IO.

Ха-ха-ха :)

Но к языковым особенностям хаскеля это вообще не имеет отношения - вопрос качества реализации, сам интерфейс у дебагера GHCi достаточный, просто он [дебагер] какой-то игрушечный.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

но дебагер ведёт себя довольно странно в контексте IO.

Даже больше - :break на fn x = x + 1 и gn = (+ 1) ведёт себя по-разному (второй «не работает»).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Ну, в общем, вроде разобрались. Спасибо за консультацию.

den73 ★★★★★
()

Чем интересен этот Erlang? ФП, таки нет, тут больше ЛП. Быстрый рантайм - тоже странный критерий. Модель акторов - так она есть во многих библиотеках, взять хотя бы Akka.

anonymous
()
Ответ на: комментарий от anonymous

Хер с ней, с моделью, там дешевые и удобные зеленые нити, такого (из распространенных сред) больше нигде нет. Если б еще функциональщину выпилили и типизацию добавили - идеальное решение было бы.

anonymous
()
Ответ на: комментарий от den73

Подводя итоги по отладке, я так понял, в плане отлова доступа к переменной в лиспе

Что за дебильная привычка называть CL лиспом? Да, в CL все плохо, но есть лиспы, сделанные правильно, которые не тянут за собой весь кретинизм CL. Так что в лиспе в этом плане все как раз замечательно.

anonymous
()
17 июня 2014 г.

борщ vs борщ vs борщ

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.