LINUX.ORG.RU

Vim или Emacs? А LISP в 2021?

 , ,


1

4

https://www.youtube.com/watch?v=8Q9YjXgK38I&t=42s

Парень в определённых кругах, личность известная.
посмотрел я его ролик, стал ковыряться по истории:

А ведь Crashbandicoot была годной игрой…

Что выбрать? Vim или Emacs?
Изучать в 2021 году Lisp? Если изучать, какой? Практика?
А не засмеют сотрудики?

Времени в сутках маловато, на всё не хватает.


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

Питон штатно позволяет полностью перехватывать загрузку модуля.

Ну это не совсем макрос чтения. Это разбор вручную. Ладно, написать свой технически можно. Извратившись, даже макросы можно добавить.

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

Наверное, фича настолько убойная, что от нее отказались абсолютно все языки.

Это в каких она была и от неё отказались? В Racket есть https://docs.racket-lang.org/guide/parameterize.html, даже в Rust добавили https://docs.rs/fluid-let/0.1.0/fluid_let/ .

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

Где?

Умеет питон.

Как в питоне удалить модуль из текущей программы? Или хотя бы класс.

Не умеет сохраняться да

Так это и есть основа работы в образе. Вместе с отладчиком позволяет единожды запустить программу и постепенно её дописывать уже не останавливая. Как операционную систему.

Умеет питон.

Как? У меня он при ошибке только стек выплёвывет и падает. pdb тоже ужасно куцый, даже функцию в него ввести нельзя.

внутри фреймворка

Ну так внутри фреймфорка на лиспе все DSL тоже единообразны. Но вот скрестить std::string, vtkString и QString в одном проекте то ещё удовольствие.

Не знаю про Builder

iostreams, spirit, …

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

Это можно реализовать на любом языке с eval.

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

monk ★★★★★
()
Ответ на: комментарий от no-such-file

Если ты не знаешь что-такое визитор, то ты нишиша не поймёшь когда его увидишь

Вот, только ты переставил всё верх ногами: шаблон «посетитель» гарантирует нечитаемую лапшу вместо исходного кода. visitor.accept = acceptor_function — вот тебе весь «visitor». И то при условии, что там visitor действительно нужен.

const proxied_get_response = new ProxyDecorator(get_response)
api(proxied_get_response, save_data)

(with-proxy (api get-response save-data))

Я не вижу преимущества одного метода над другим. Во втором случае тебе точно так же нужно будет разбираться на тему «что это with-proxy делает?». К тому же, почему нельзя сделать так?

api(new ProxyDecorator(get_response), save_data)
byko3y ★★★★
()
Ответ на: комментарий от monk

Сишные макросы работают также. И GObject/GTK только благодаря ним в Си более-менее нормально выглядит

Спорны тезис. Спорный потому, что можно сильно лучше, можно было вообще не упарываться объектами — сейчас уже написание интерфейсов «с нуля» не считается таким моветоном, как в 90-е. Проблема объектного подхода заключается в том, что на самом деле GUI не состоит из независимых объектов, а является цельной сущностью — интерфейсом, отображающимся на один экран, и воспринимаемым ввод из очень ограниченного числа устройств ввода. Даже если это мультитач-экран — все равно пользователь вводит жесты как единое целое, а не как независимые нажатия.

А это так и есть: всё описано в документации. Также как в ООП языках есть неявные переменные типа this, так и в макросах могут быть неявные переменные (например внутри loop есть it, в котором результат условия)

Нет, это хреновый пример. Сколько копий поломато об this в JS или в питоне.

Нет. loop и for/list

И каким образом это нельзя сделать функциями?

Так динамический линковщик до запуска программы отрабатывает

Уже ответили — dlopen/dlsym же.

byko3y ★★★★
()
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от monk

Всё отлично работает в IO. Примерно так: https://github.com/haskell/win32/blob/master/examples/hello.lhs

Отлично работает, пока это пустая программа без состояния вообще. Она ведь даже ни на какие кнопки не реагирует. Хотя, в принципе, можно в таком духе на IORef/STRef реализовать копии сишных алгоритмов, правда, получив еще более многословную программу, чем на Си. И типа зачем всё это было? От «преимуществ» хаскеля не остается и следа.

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

Это в каких она была и от неё отказались? В Racket есть https://docs.racket-lang.org/guide/parameterize.html, даже в Rust добавили https://docs.rs/fluid-let/0.1.0/fluid_let/

Кому-то нечего было писать на расте — написал «это».

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

Где?

sys._getframe(1).f_locals

Даже библиотеку подключать не нужно.

Как в питоне удалить модуль из текущей программы? Или хотя бы класс

del sys.modules["empty"]
del empty

Причем, ручное манипулирование sys.modules является штатно заложенной фичей. а не каким-то костылем — пользовательские загрузчики точно так же регистрируют модули или подменяют онные на свои объекты именно через sys.modules.

Класс удаляется еще проще — через один del.

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

Я боюсь, что это давно дальше книжек не работает. Ты подключился к БД, работаешь такой сбее отладчиком like a boss, а потом внезапно тебе прилетает таймаут и соединение рвется — чот образ «протёк». У меня та же беда в моей либе, где отдельные транзакции специально для отладочных целей выдают ошибку по таймауту, но при этом для отладочных целей же мне иногда нужно этот таймаут превышать. То есть, по-хорошему делать снимок нужно всей ОС и всех связанных сервисов.

Умеет питон

Как? У меня он при ошибке только стек выплёвывет и падает. pdb тоже ужасно куцый, даже функцию в него ввести нельзя

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

Ну так внутри фреймфорка на лиспе все DSL тоже единообразны. Но вот скрестить std::string, vtkString и QString в одном проекте то ещё удовольствие

Да, я давно говорю, что DSL — это зло, но иногда польза от него превышает вред. В лиспе же было принято клепать DSL налево и направо, с этим лозунгом лисп и продавали сообществу. В какой видео про «лисп крут» не ткни — там обязательно будет что-то про «смотрите, как легко я могу написать DSL». Это и убило лисп — индустрии не нужен твой DSL. А когда не нужен DSL, то выясняется, что s-выражения тоже особо не нужны, они только чрезмерно перегружают сорцы однообразным примитивным базовым синтаксисом без семантики.

Не знаю про Builder

iostreams

В каком месте это Builder?

spirit

Дичь, как и весь Boost.

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

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

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

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

Ничего особенного, просто несколько импортов.

import * as cdk from '@aws-cdk/core';
import * as events from '@aws-cdk/aws-events';
import * as awsbackup from '@aws-cdk/aws-backup';
import * as cw from '@aws-cdk/aws-cloudwatch';
import * as iam from '@aws-cdk/aws-iam';
ugoday ★★★★★
()
Ответ на: комментарий от ugoday

Ничего особенного, просто несколько импортов
import * as cdk from '@aws-cdk/core'

Мне кажется, что там еще какие-то зависимости, потому что у меня с установленным aws-cdk node_modules занимает только 150 МБ (ну и плюс 60 МБ typescript). aws-cdk такой потому, что каким-то чудом включает в себя aws-sdk два раза — если aws-sdk тоже в зависимостях, то оно будет поставлено еще и в третьем экземпляре. Почему так — это вопрос к амазону.

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

import * as cdk from ‘@aws-cdk/core’;

Тапком по рукам за такое надо бить.

B0B
() автор топика
Ответ на: комментарий от ugoday

только 150 МБ

Тем временем, установка Win98 влезала в 120Мб

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

byko3y ★★★★
()

emacs тормозит больше чем vim, nvim шустрее vim’а. вот и выбирай.

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

loop и for/list

И каким образом это нельзя сделать функциями?

А каким образом можно?

Уже ответили — dlopen/dlsym же.

Наличие функций dlopen и dlsym проверяется при запуске. А текстовые аргументы этих функций не являются функциями программы. Результат dlsym всё равно надо сохранить в какой-то указатель на функцию, который и будет функцией (и имя которого проверяется при компиляции).

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

И типа зачем всё это было? От «преимуществ» хаскеля не остается и следа.

  1. Типизация
  2. В любой более-менее сложной программе есть не только ввод-вывод.
monk ★★★★★
()
Ответ на: комментарий от byko3y

sys._getframe(1).f_locals

Это локальный контекст. Динамические переменные позволяют установить значение глобальной переменной для куска кода. Самый близкий аналог (param как динамическая переменная)

try:
  old = param
  param = a
  {здесь код}
finally:
  param = old

Но для динамической переменной эти изменения ещё и не видны в других потоках.

Нужны, чтобы иметь «текущий стандартный вывод», «текущее соединение к БД», «текущую транзакцию», …

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от byko3y

Ты подключился к БД, работаешь такой сбее отладчиком like a boss, а потом внезапно тебе прилетает таймаут и соединение рвется — чот образ «протёк»

Для восстановления соединения не обязательно перезапускать всю программу.

То есть, по-хорошему делать снимок нужно всей ОС и всех связанных сервисов.

Когда ты в ОС работаешь с Интернетом, ты делаешь снимок только своей ОС, а не всего Интернета. И если соединение с сайтом упало, не перезагружаешь всю ОС.

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

Когда же у тебя ошибка вышла по стэку до корня, то уже поздно что-то ловить и продолжать выполнение. Это чисто питонья специфика, которая требует иных методов отладки.

Так всюду кроме Common Lisp. Там, во-первых, можно делать рестарты, которые после раскрутки стека позволяют продолжить с точки ошибки (да, заранее). Во-вторых, ошибка в отладчик упадёт в точке бросания исключения и в этой точке можно исправить значения переменных, переопределить функции и запустить исправленную функцию.

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

В лиспе же было принято клепать DSL налево и направо, с этим лозунгом лисп и продавали сообществу.

Много DSL под лиспом знаешь? cl-sql, loop, … Они все единообразны и полностью интероперабельны с произвольным кодом на лиспе (то есть DSL можно воткнуть вместо любого выражения и выражениями внутри DSL может быть любое выражение лиспа).

Это и убило лисп — индустрии не нужен твой DSL.

Убило не это, а AI Winter. И UNIX.

iostreams

В каком месте это Builder

А что такое cout << setprecision(4) << value;, если не Builder? Разве что вместо явного вызова метода за каким-то чёртом используют арифметический сдвиг влево.

Дичь, как и весь Boost.

И огромное количество DSL в нём почему-то не убило Си++. Так что это контрпример к твоей теории.

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

На лиспе это пишется проще только пока входной код является s-выражениями.

В моём примере никаких s-выражений нет. Смотри: https://queue.acm.org/downloads/2011/racket/5-lang/world.rkt

Вся реализация DSL - два файла: https://queue.acm.org/downloads/2011/racket/5-lang/txtadv-reader.rkt (148 строк) и https://queue.acm.org/downloads/2011/racket/5-lang/txtadv.rkt (343 строки).

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

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

Поздравляю, ты накостылил кривой костыль на приватной интроспекции,

CPython implementation detail: This function should be used for internal and specialized purposes only. It is not guaranteed to exist in all implementations of Python.

который не только кривой, но и не работает:

>>> import sys
>>> var1 = 1
>>> var2 = 2
>>> def foo(*args):
        for name in args:
            print(name, '=', sys._getframe(1).f_locals[name])
 
>>> def oof(x):
        foo('var1', 'x')
 
>>> oof(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in oof
  File "<stdin>", line 3, in foo
KeyError: 'var1'
Puzan ★★★★★
()
Ответ на: комментарий от monk

Всё отлично работает в IO. Примерно так: https://github.com/haskell/win32/blob/master/examples/hello.lhs

Hello, World на 123 строки. Это нормально? Это отлично? Да вы со свим хаскелем совсем рехнулись. Даже в ассемблере если дёргать WinAPI’шные функции результат будет на продяк меньше, проще и мене трудозатратный и с меньшим количеством строк.

хаскель это помойка

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

Я не вижу преимущества одного метода над другим

Я и не говорю что оно есть, это спорный вопрос, что лучше. Однако сам метод существует и макросы нужны для этого.

почему нельзя сделать так?

Потому что это был простой демонстрационный пример для объяснения на пальцах. В реальности там абстрактные фабрики фабрик поверх фасадов адаптеров билдеров и код размазан по 100500 файлов. И чтобы рулить всей этой лапшой изобрели «программирование в XML». Так вот ЛИСП это такое нативное «XML программирование».

no-such-file ★★★★★
()
Ответ на: комментарий от vM

классический майкрософтовский Hello-Windows

Какой-то дзенский Hello-World без надписи вообще. В Haskell хоть надпись есть и автоматически центрируется.

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

А ты, кстати, так и не сказал, что будет, если сделать (foo (+ a 1) b something) — а в этом и вся причина, почему уже 50 лет так не делают.

Спасибо, что обратил внимание. Вот исправленный вариант, который на твоё предложение скажет Bad syntax: (foo (+ a 1)):

(define-syntax foo
  (syntax-rules ()
    [(_) `()]
    [(_ (name value) xs ...) (cons `(name ,value) (foo xs ...))]
    [(_ (a ...) xs ...) (error (format "Bad syntax: ~a" '(foo (a ...))))]
    [(_ var xs ...) (cons `(var ,var) (foo xs ...))]))

Он, кроме того, понимает конструкции вида (foo a b (c 10) (d (+ a b))), и возвращает ((a 1) (b 2) (c 10) (d 3)).

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

Дзенский

const main=0303;

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

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

Поздравляю, ты накостылил кривой костыль на приватной интроспекции

Такой ты зануда:

Python 2.7.17
>>> import sys
>>> var1 = 1
>>> var2 = 2 
>>> def foo(*args):
        for name in args:	
            print(name, '=', sys._getframe(1).f_locals.get(name, sys._getframe(1).f_globals.get(name)))
>>> def oof(x):
        foo('var1', 'x')      
>>> oof(10)
('var1', '=', 1)
('x', '=', 10)

Эта «приватная» интроспекция старше и стабильнее, чем синтаксис третьего питона.

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

Наличие функций dlopen и dlsym проверяется при запуске. А текстовые аргументы этих функций не являются функциями программы. Результат dlsym всё равно надо сохранить в какой-то указатель на функцию, который и будет функцией (и имя которого проверяется при компиляции)

Тем не менее, это позволяет дергать функцию, которой потенциально нет на целевой платформе, без каких-либо макросов.

И каким образом это нельзя сделать функциями?

А каким образом можно?

forEach в JS сделали же.

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

Типизация

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

Как ты видишь, вся хаскелева программа hello world для win32 написана в монаде IO. Так-то можно в случае чего хаскелевый код дернуть из более практичного ЯП.

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

Тем не менее, это позволяет дергать функцию, которой потенциально нет на целевой платформе, без каких-либо макросов.

Так же, как и eval, например.

forEach в JS сделали же.

Он убог. Нельзя идти по нескольким коллекциям, нельзя добавить условие на элементы. Это не loop, а map.

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

Как ты видишь, вся хаскелева программа hello world для win32 написана в монаде IO.

Потому что она ничего не делает (кроме центрирования надписи, которое как раз не в монаде IO).

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

Так-то можно в случае чего хаскелевый код дернуть из более практичного ЯП.

Теоретически, да. А практически на порядок проще дёргать из IO.

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

Я уже написал, что все вопросы к амазону.

— Почему ваш жабоскрипт такой жырный?
— Я не виноват! Оно само!

Вот и поговорили.

Бинарь ноды влезает в 15 Мб

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

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

Нельзя идти по нескольким коллекциям, нельзя добавить условие на элементы

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

auto for_(auto && range, returns_bool auto && foo) {
    for (auto && r: range) {
        if (!foo(r)) {
            return;
        }
    }
}
auto for_(auto && range, auto && foo) {
    for (auto && r: range) {
        foo(r);
    }
}

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

Такой ты зануда:

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

Эта «приватная» интроспекция старше и стабильнее, чем синтаксис третьего питона.

Она приватная без кавычек, независимо от версии питона.

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

Ничего не мешает написать вариант, в котором все это можно будет делать.

Покажешь, как выглядит использование? Например, аналог

(loop for i from 1 to 10
      for j from 100
      when (oddp i)
      collect (+ i j))
monk ★★★★★
()
Ответ на: комментарий от monk

Ждал этого вопроса.

Выглядеть это будет


#include <iostream>
#include <vector>
#include <optional>
#include <tuple>

auto into_tuple(auto ... opts) {
    std::optional<std::tuple<std::remove_cvref_t<decltype(*opts)>...>> r;
    if ((opts && ...)) {
        r.emplace(*opts...);
    }
    return r;
}

int main() {
    auto for_range_ = [](auto a, auto b) {
        return [a, b, i=a]() mutable {
            std::optional<decltype(i)> r;
            if (i < b) {
                r.emplace(i);
                ++i;
            }
            return r;
        };
    };
    auto for_0 = [=](auto b) { return for_range_(0, b); };

    auto fors_ = [](auto && ... fors) {
        // декартово произведение из fors... делать лень
        return [=]() mutable {
            return into_tuple(fors()...);
        };
    };

    auto loop_ = [](auto && generator, auto && filter, auto && foo) {
        auto next = generator();
        while (next) {
             if (filter(*next)) {
                 foo(*next);
             }
             next = generator();
        }
    };

    std::vector<int> result;
    loop_(
        fors_(for_range_(1, 10), for_0(100)),
        [](auto && ij) {
            auto && [i, j] = ij;
            return i % 2;
        },
        [&](auto && ij) {
            auto && [i, j] = ij;
            result.push_back(i + j);
        }
    );
    for (auto && r: result) {
        std::cout << r << '\n';
    }
}

компилировать так

g++ -std=c++2a loop.cxx

Вложенные циклы не сделал, но если это принципиально важно, то сделаю на выходных. Вывод:

$ ./a.out 
1
5
9
13
17
Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Вот ты реально считаешь, что

    std::vector<int> result;
    loop_(
        fors_(for_range_(1, 10), for_0(100)),
        [](auto && ij) {
            auto && [i, j] = ij;
            return i % 2;
        },
        [&](auto && ij) {
            auto && [i, j] = ij;
            result.push_back(i + j);
        }
    );

читается не сложнее, чем

(loop for i from 1 to 10
      for j from 100
      when (oddp i)
      collect (+ i j))

?

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

На лиспе без макросов лучшее, что можно сделать — что-то вроде

(loop (for 1 10)
      (for 100)
      (when (lambda (i j) (oddp i)))
      (collect (lambda (i j) (+ i j)))

Получаем кучу мусорных синтаксических элементов. Не такой ужас как на Си++, но всё равно читается гораздо хуже, чем версия с макросом.

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

Кого-то еще пугают скобки в лиспе?

Конечно. В лиспе они одинаковые круглые. Это пугает. В «семантически богатом» (byko3y) языке все скобки должны быть разные.

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

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

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