LINUX.ORG.RU

KISS языки


1

1

Кроме ближайших родственников Оберона. С тривиальным синтаксисом, интуитивной и формально определенной семантикой. Есть такие?

PS Брейнфак не предлагать.

PPS Желающие предложить СИ отправляются учить стандарт в целом, и лютое количество таких выкрутасов в частности.

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

> Видимо, ты 20 лет программировал в другой области.

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

Ну что тут скажешь... сейчас, например, почти везде есть инструкция вроде compare-and-swap. Она тоже должна быть в языке? На многих процах есть MMU - это тоже тащим в язык?

compare-and-swap очевидно да, да емнип она и есть де-факто в жцц

Где именно? А так, в gcc много полезного есть: __typeof__, ({}).

Настоящие проблемы Си совсем в другом.

так расскажи, очень интересно

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

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

Нахрена замыкания и исключения в таком низкоуровневом языке? Как оно будет на каких-нибудь встраиваемых системах работать?

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

> А в какой области нужна арифметика над машинными словами с точно определенной семантикой переполнения?

я уклонюсь от доказательства, что оно дотягивает до «области», но пример с pi(x) я привел

основной пойнт в том, что в асме это легко, а в си это UB — и это не нормально

compare-and-swap очевидно да, да емнип она и есть де-факто в жцц

Где именно? А так, в gcc много полезного есть: __typeof__, ({}).

TYPE __sync_val_compare_and_swap (TYPE *ptr, TYPE oldval TYPE newval, ...)

__typeof__ вообще не проблема, т.к. времени компиляции, а ({}) на мой не очень просвещенный взгляд тоже никаких проблем не создает

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

это все правильно, но в стиле «этого нет»

сюда в стиле «этого нет» надо добавить: нет знаковых целых по модулю (со знанием оптимизатора о них), нет допустим оператора >> на байтах, (есть только на целых)

а вот в стиле «си делает неправильно» —

1. нафига оно продвигает незнаковые операнды до знакового инта?

2. и нафига оно вообще продвигает? (этот вопрос уже спорный, хотя случай 32 bit int && 32 bit char одновременно тоже интересен)

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

> Нахрена замыкания и исключения в таком низкоуровневом языке? Как оно будет на каких-нибудь встраиваемых системах работать?

они оба не требуют сборки мусора, а замыкания так вообще в современом си активно юзаются в виде f(bla-bla-bla, void* data)

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

>> А в какой области нужна арифметика над машинными словами с точно определенной семантикой переполнения?

я уклонюсь от доказательства, что оно дотягивает до «области», но пример с pi(x) я привел

Вычисление числа pi в целочисленной арифметике? Это не пример.

TYPE __sync_val_compare_and_swap (TYPE *ptr, TYPE oldval TYPE newval, ...)

ну блин, тогда можно сказать, что GCC и MMX поддерживает.

__typeof__ вообще не проблема

Я и не говорил, что это проблема.

а вот в стиле «си делает неправильно» --

Меня то, что есть в Си, вполне устраивает. Но в нем слишком мало есть.

нафига оно вообще продвигает?

Думаю, непродвижение потянуло бы за собой runtime-поддержку (избавление от UB с переполнениями - тоже).

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

> Нахрена замыкания и исключения в таком низкоуровневом языке?

Затем же, зачем и в Лиспе.

Как оно будет на каких-нибудь встраиваемых системах работать?

Так же, как Ада.

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

> Вычисление числа pi в целочисленной арифметике? Это не пример.

это вовсе не число 3.14, а функция; и ты опустил самое главное — в любом асме поддержка есть — и мне пофиг, если с твоей точки зрения это не область и даже не пример; эта тему по-моему уже пошла по кругу

ну блин, тогда можно сказать, что GCC и MMX поддерживает

1. а умеет ли он их оптимизировать?

2. ММХ вообще-то не лежит в пересечении асмов

Думаю, непродвижение потянуло бы за собой runtime-поддержку (избавление от UB с переполнениями - тоже).

Несколько шаблонов асма для сдвига байтов (типа как деление на 15 заменяется на умножение на 0х88888889 и сдвиг) это максимум что потребуется?

Что же касается рантайм-поддержки исключений при переполнении — то это *надо*, т.к. в любом асме флаги переполнения есть — извольте предоставить к этому интерфейс.

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

>> Вычисление числа pi в целочисленной арифметике? Это не пример.

это вовсе не число 3.14, а функция;

Я сказал, что примером хоть какой-нибудь предметной области это не является.

2. ММХ вообще-то не лежит в пересечении асмов

Почти в любом современном асме есть свой аналог MMX.

Что же касается рантайм-поддержки исключений при переполнении — то это *надо*, т.к. в любом асме флаги переполнения есть — извольте предоставить к этому интерфейс.

Да неужели? И какой же флаг взводится при переполнении 16-бит целого на ia32?

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

> Меня то, что есть в Си, вполне устраивает. Но в нем слишком мало есть.

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

Практический аспект этой дискуссии о целой арифметике — если транслировать свой код прямо в си, то не слишком он его попортит своими продвижениями? или все-таки в llvm/gimple? (да, еще есть как-бы «нетехнический» аспект, из-за которого у си преимущества)

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

> Да неужели? И какой же флаг взводится при переполнении 16-бит целого на ia32?

я не утверждал, что надо ловить переполнения любых целых — а утверждал, что у каждого проца есть целый тип, где оно флагуется

З.Ы. а разве add ax,bx не выставит нужный флаг?

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

> а разве add ax,bx не выставит нужный флаг?

ХЗ, лень смотреть учебник. Кроме того, даже если он выставлен - что с ним делать?

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

>Затем же, зачем и в Лиспе.
Не понял.
Лисп, ну CL, низкоуровневым языком, заточенным под системное программирование, не является.

Так же, как Ада.

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

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

> Лисп, ну CL, низкоуровневым языком, заточенным под системное программирование, не является.

Эээ... и что? На Си тоже не только ядра пишут. Более того, одно время в GNU C были вполне полноценные вложенные функции (с доступом к параметрам объемлющей фугкции). Понятно, что full-blown замыканий без сборки мусора сделать нельзя, но даже ограниченные замыкания лучше, чем их отсуствие.

Ада это, все-таки, редкость.

Да не в этом дело... Ада доказала, что даже в реализации глубоко встроенных систем исключения полезны и применимы. В конце концов, можно сделать и для Си профили, как для Ады.

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

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

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

ХЗ, лень смотреть учебник.

как будто мне не лень вспоминать, кто это из OCS

оказалось О

Кроме того, даже если он выставлен - что с ним делать?

вообще не понял вопрос, юзать конечно!

например так:

#include <stdio.h> 
#include <stdlib.h> 

int main(int argc, char** argv)
{
    if(argc!=3) return 1;
    short arg1=atoi(argv[1]);
    short arg2=atoi(argv[2]);
    short sum=0, over;

    asm(
        "movw   $0,   %%bx ; "
        "movw   $1,   %%cx ; "
        "movw   %2,   %%ax ; "
        "addw   %3,   %%ax ; "
        "cmovOw %%cx, %%bx ; "
        "movw   %%ax, %0   ; "
        "movw   %%bx, %1   ; "

        : "=m"(sum), "=m"(over) : "m"(arg1), "m"(arg2) : "%ax", "%bx", "%cx"
    );

    printf("arg1=%d arg2=%d sum=%d over=%d\n", (int)arg1, (int)arg2, (int)sum, (int)over );
    return 0;
}

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

>> Кроме того, даже если он выставлен - что с ним делать?

вообще не понял вопрос, юзать конечно!

Каким образом? Сигнал послать? Неприемлемо для ядер. Исключение? Их нет в Си. Взвести глобальный флаг? Бесполезно, никто проверять не будет.

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

без исключений конечно хреново, но все равно можно было сделать конструкцию «кроссплатформенного ассемблера с оптимизатором», например

int x;
ensure {
  x=a-8+b-f(a)*g(b);
  for( i=0; i<n; i++ )
    x+=a%i;
}
on_overflow {
  /* тут можно писать свой код и даже дернуть longjmp */
}

on_overflow выполняется только если произошло переполнение в блоке ensure, при этом переполнения внутри f или g не рассматриваются — это лично дело этих функций, в частности они тоже могут дернуть longjmp

на ассемблере это просто несколько jo on_oveflow_001 после каждого сложения/вычитания

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

если f или g дернут longjmp, даже из-за overflow внутри них, то показанный в коде on_overflow верхнего уровня, очевидно, не выполнится — просто будет longjmp

дешево и сердито, в стиле си

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

> Понятно, что full-blown замыканий без сборки мусора сделать нельзя,

а это с чего вдруг?

да, в худшем случае куча потребуется, но куча != сборщик_мусора

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

> Замыкания требуют динамической памяти,

в худшем случае да

и, более того, GC.

обоснуй

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

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

обоснуй

Ну вот я сделаю сделаю себе вложенные списки на замыканиях:

-- foo <args> ≡ <term>
-- это сахар для
-- foo ≡ λ <args> . <term>

-- Ну а λ <args> . <term>
-- сахар для
-- λ <a1> . λ <a2> ... λ <an> . <term>

true a b  ≡ a
false a b ≡ b

pair a b  ≡ λ f . f a b
first x   ≡ x true
second x  ≡ x false

nil       ≡ pair true true
cons a b  ≡ pair false (pair a b)
head x    ≡ first (second x)
tail x    ≡ second (second x)

list x::List ≡ foldr cons nil x

tail (tail (list 1 2 3))
=> 3

И начну создавать их, вопрос - кто будет очищать мусор? Причём очищать нужно не только функциональные объекты (это просто то же что и обычный free), но и все копии окружений которые связываются с (каждым) замыканием - заставлять пользователя помимо обычной free (для вложенных структур) заниматься освобождением функциональных окружений (для них же)? - это мало кому нужно.

А если речь идёт о локальных окружениях на стеке, то они и в Си есть:

#include <stdio.h>

/* any & λ */

typedef void* any;

typedef any(*lambda1)(any);
typedef any(*lambda2)(any, any);
typedef any(*lambda1_2)(lambda2);
typedef any(*lambda4)(any, any, any, any);
typedef any(*lambda1_4)(lambda4);

/* simple local closure */

lambda2 foo (any c) {
  any true (any a, any b) { printf("in `true' c = %c\n", (int)c); return a; }
  printf("in `foo' true = %c\n", true((any)'t', (any)'f'));
  return &true;
}

/* try pairs with non-local closure */

lambda1_2 pair (any a, any b) {
  any lambda (lambda2 f) { return (f)(a, b); }
  return λ
}

any print_pair (any a, any b) {
  printf("(%i . %i)\n", (int)a, (int)b);
  return NULL;
}

/* try qua */

lambda1_4 qua (any a, any b, any c, any d) {
  any lambda (lambda4 f) { return (f)(a, b, c, d); }
  return λ
}

any print_qua (any a, any b, any c, any d) {
  printf("(%i . %i . %i . %i)\n", (int)a, (int)b, (int)c, (int)d);
  return NULL;
}

/* and then */

int main() {
  // not static scoping:
  any c = (int*)'m';
  // ok, local closures:
  lambda2 fun = foo((any)'c');
  // not dynamic scoping and is no full-closures:
  int result  = (int)(fun)((any)'t', (any)'f');
  printf("in `true' throu `main' true = %c\n", result);

  // try to make pair adt on lambdas/closures
  // but lost something in a closure:
  lambda1_2 pair_lambda = pair((any)17, (any)19);
  (pair_lambda)(&print_pair);

  // something saved because it saved on stack:
  lambda1_4 qua_lambda = qua((any)2, (any)3, (any)5, (any)7);
  (qua_lambda)(&print_qua);
  // ^ seg. fault ! but... its ok ;-)

  return 0;
}

/*
 * So, local closures works,
 * non-local closures make garbage and seg. faults.
 */

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

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

кто-то там &\lambda; поменял, так что

s/return λ/return &\lambda;/g

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

> lambda1_4 qua_lambda = qua((any)2, (any)3, (any)5, (any)7);

афтор, пеши есчо!

(void*)2 это напрямую сегфолт, и локальность-нелокальность здесь пофиг

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

сегфолт, естественно, после того, как кто-то в конце-концов попытается это разыменовать чтобы использовать

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

> И начну создавать их, вопрос - кто будет очищать мусор?

твои конс-ы пока что можно очищать деструкторами с подсчетом ссылок, т.к. циклических структур у тебя нет (хотя тут можно и привести практический контрагрумент — даже удаление через подсчет ссылок может привести к неожиданным и заранее неизвестным затратам времени на удаление; но см. ниже)

Причём очищать нужно не только функциональные объекты (это просто то же что и обычный free), но и все копии окружений которые связываются с (каждым) замыканием - заставлять пользователя помимо обычной free (для вложенных структур) заниматься освобождением функциональных окружений (для них же)? - это мало кому нужно.

type-aware free вполне справится с этим, проблемы не вижу

_________________________________________

Я понял, что ты хотел сказать. Но практически, если нам нужны Алг.ТД, то *их* и надо использовать, а не криво эмулировать структурами, построенными на замыканиях.

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

ООП через замыкания, АлгТД через замыкания — это все извращения.

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

сегфолт, естественно, после того, как кто-то в конце-концов попытается это разыменовать чтобы использовать

А никто и не пытается их разыменовывать, даже если и начнут пытаться - будут предупреждения от компилятора (у меня в примере их нет и сегфолт не имеет отношения к void указателям).

с подсчетом ссылок

Ну так reference counting это и есть разновидность GC, причём неэффективная в отношении слежения за замыканиями.

Но практически, если нам нужны Алг.ТД, то *их* и надо использовать, а не криво эмулировать структурами, построенными на замыканиях.

Речь не об этом, а о том что как только у нас есть «полные» замыкания мы можем эмулировать с их помощью как cons-ячейки так и объекты с состоянием. Просто такое у них свойство у замыканий - и с этим надо считаться. Вот я и спрашиваю - как без GC следить за окружениями что создают замыкания?

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

> Ну так reference counting это и есть разновидность GC

тогда, извините, получается что в с++ есть GC

причём неэффективная в отношении слежения за замыканиями.

пока что это не продемонстрировано

Речь не об этом, а о том что как только у нас есть «полные» замыкания мы можем эмулировать с их помощью как cons-ячейки так и объекты с состоянием. Просто такое у них свойство у замыканий - и с этим надо считаться.

с баранами, возомнившими себя черчем и пытающимися натуральные числа кодировать замыканиями считаться не надо :-)

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

в «скриптописательском» профайле GC можно и разрешить (хотя я пока не вижу от тебя примера его необходимости)

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

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

> Вот я и спрашиваю - как без GC следить за окружениями что создают замыкания?

1. копирование значений и счетчик ссылок (для строк, например, он идеален, т.к. строки сами не на что не ссылаются)

2. сложно устроенные счетчики ссылок (типа счетчика на замкнутый список в целом или на строку, откуда берутся подстроки)

3. недопущение связывания сложных объектов замыканиями — это вполне оправдано, т.к. сложный объект может неожиданным образом поменяться, а это вовсе не в духе использования замыканий

4. неглубокое копирование объектов, до момента, пока «листья» окажутся простыми объектами, например строками

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

например, идея «имеем замыкание, которое возвращает пару из нового значения и замыкание, которое тоже возвращает пару из нового значения и замыкание, которое...» похоже извращенная

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

вообще, нормальное использование замыканий — это что-то типа фильтра на коллекцию; фильтр например может захватить пару строк (одна как минимальная, другая как максимальная), скопировать к себе целое число (пропускать каждый 5-ый), еще чего-то... но все это должно быть константным

а на константных захваченных значениях думаю можно обойтись без полноценного GC

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

> 3. недопущение связывания сложных объектов замыканиями — это вполне оправдано, т.к. сложный объект может неожиданным образом поменяться, а это вовсе не в духе использования замыканий

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

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

тогда, извините, получается что в с++ есть GC

(библиотечно есть, а не в самом языке) если get the facts то:

0) Конечно это вид примитивного GC (http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29, но я думаю ты там был).

1) В GObject есть GC в виде подсчёта ссылок, но это не значит что в самом си есть GC.

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

3) Вообще реализация подсчёта ссылок в виде библиотеки возможна в любом языке и может использоваться для автоматического управления в foreign memory (если для внутренней памяти уже есть свой GC).

4) Другое дело языки претендующие на какую-то низкоуровневый и при этом имеющее внутренний (а не библиотечный) GC - пример Objective-C в нём есть как замыкания (в виде блоков) так и сборка мусора (опционально, как я понял).

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

Хм, спроси у тех кто пишет на языках с замыканиями (python, elisp, scheme, CL, haskell, etc.) - применения замыканий крайне разнообразны ;) И я не знаю где бы они были сделаны без сборки мусора.

ООП через замыкания, АлгТД через замыкания — это все извращения.

Добавь тогда ИМХО, и ещё к этой фразе:

с баранами, возомнившими себя черчем и пытающимися натуральные числа кодировать замыканиями считаться не надо :-)

Т.е. понятно что извращения при практическом программировании, но не с точки зрения изучения (откуда ты знаешь - может через некоторое время появятся методы верификации и оптимизации программ которые применимы в формализме какого-нибудь lambda calculus, но не применимы с точки зрения классического вычисления на state machine).

причём неэффективная в отношении слежения за замыканиями.

пока что это не продемонстрировано

Кем? Это я должен доказывать? А как же мировой опыт и Generational GC ;)

вообще, нормальное использование замыканий ...

Опять таки - как же мнения всех тех людей что используют замыкания в своих лиспах-хаскелях-и-питонах?

Короче, я не знаю ни одного языка в котором бы замыкания (с возможностью их возвращения из функции) были бы сделаны без GC, если ты знаешь - назови, я с удовольствием посмотрю. Ну или сам напиши proof-of-concept - можно для начала посмотреть как там в gcc реализованы локальные замыкания.

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

использование http://en.wikipedia.org/wiki/Church_encoding вместо целых чисел, предоставляемых железом тоже «ничем не противоречит определению»; вот только я посмотрю, на сколько десятичных порядков программа начнет после этого тормозить (не меньше чем на 3, думаю)

«сложный» должен тут означать не «много полей и методов», а что-то другое — например, немутабельные объекты проще мутабельных, строки проще объектов, держащих внутря себя ссылки с подсчетом; но и это не вся история

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

>>> причём неэффективная в отношении слежения за замыканиями.

пока что это не продемонстрировано

Кем? Это я должен доказывать? А как же мировой опыт и Generational GC ;)

да, ты, так как ты сказал

Ну так reference counting это и есть разновидность GC, причём неэффективная в отношении слежения за замыканиями.

«мировой опыт» чего? мировой опыт практического языкостроения вообще — не имеет к этому вопросу отношения

лично о Гослинге — если бы умные люди типа Одерски и остальных не заставили его ввести в яву дженереки, ява бы говном и умерла

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

> использование http://en.wikipedia.org/wiki/Church_encoding вместо целых чисел, предоставляемых железом тоже «ничем не противоречит определению»; вот только я посмотрю, на сколько десятичных порядков программа начнет после этого тормозить (не меньше чем на 3, думаю)

но при чём тут «сложные» объекты

«сложный» должен тут означать не «много полей и методов», а что-то другое — например, немутабельные объекты проще мутабельных, строки проще объектов, держащих внутря себя ссылки с подсчетом; но и это не вся история

это я понял, но почему их нельзя (по-твоему) использовать в замыканиях?

я, например, люблю использовать хеш-таблицы в замыканиях. хеш-таблица — «сложный» объект?

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

> Конечно это вид примитивного GC

посчет ссылок *можно* использвать как вид примитивного и неполноценного GC, но это не означает, что они *есть* вид примитивного GC

д-во:

1. GC заставляет периодически выгонять из из свопа в оперативку неиспользуемые страницы памяти, подсчет ссылок — нет

2. подсчет ссылок на строках обеспечивает гарантированное время удаления строки, GC на строках — скорее всего нет (хотя может и можно сделать)

в результате имеем GC — это такой вид примитивного посчета ссылок

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

> я, например, люблю использовать хеш-таблицы в замыканиях. хеш-таблица — «сложный» объект?

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

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

Ну хотя бы обычный RC не работает с циклическими структурами (circular data structures), во-вторых смешивание инструкций gc с реальными инструкциями кода (инкрементальный/конкурентный сборщик, работающий в отдельном потоке даёт большой выигрыш при параллелизации).

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

Естественно речь о использовании счётчиков в алгоритмах автоматического освобождения а не о самих счётчиках (сами счётчики как они есть вообще не нужны, разве что сказать «вот тут у нас выделилось уже 1 MB данных и 1000 объектов, ну и ладно пусть весят» ;))

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

гм хеш-таблица, допустим объектов некоего класса (или разных классов), доступ к которым осуществляется по символу-идентификатору, типа так:

(let ((instances (make-hash-table :test #'eq)))
  (defmethod make ((id symbol) (class-name symbol) &rest initargs)
    (make id (apply #'make-instance class-name initargs)))
  (defmethod make ((id symbol) instance &rest initargs)
    (setf (gethash id instances) instance))
  (defun find-instance (id)
    (gethash id instances)))
korvin_ ★★★★★
()
Ответ на: комментарий от quasimoto

> Этот пост я уже совсем не понял ;) RC это один из вариантов GC - к чему умствования?

RC это НЕ один из вариантов GC, так же как мотоцикл это не один из вариантов автомобиля

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

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

не, ну это понятно, я думал ему более конкретный пример нужен =)

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

Я чуть ниже написал - тут мы рассматриваем RC в применении к автоматическому освобжению памяти, собственно GC, - это чтобы чистить окружения после создания замыканий.

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

все равно не понял :-)

вообще с моей точки зрения замыкание — это облегченная синтаксическая форма для создания объекта;

думать решать «а надо ли собирать мусор для замыканий» надо так же, как и решать «а надо ли собирать мусор для объектов»

*отдельно* рассматривать замыкания надо (вроде) только в том аспекте, что было бы полезно, если бы вместо любой функции можно было бы пихнуть замыкание

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

что именно ты не понял? функции make и find-instance замыкаются на символ instances, который связывается с хеш-таблицей. make заносит в неё объект, find-instance возвращает по идентификатору, при этом сама хеш-таблица видна только этим функциям. инкапсуляция.

без замыканий это аналогично такому псевдокоду с модулями:

(module foo
  (provide make find-instance)
  
  (define instances (make-hash))

  (define (make ...) ...)

  (define (find-instance ...) ...))

(require foo)

просто замыкание в таком ключе — вещь «низкоуровневая», «базовая» =)

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

> вообще с моей точки зрения замыкание — это облегченная синтаксическая форма для создания объекта;

не совсем. строить объекты (в ООПшном смысле, например как в С++) с помощью замыканий немного некрасиво и громоздко, нужен синтаксический сахар (макры/шаблоны)

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

«замыкания — объекты для бедных, объекты — замыкания для бедных» // не-помню-чья

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

> *отдельно* рассматривать замыкания надо (вроде) только в том аспекте, что было бы полезно, если бы вместо любой функции можно было бы пихнуть замыкание

ну в «функционально-ориентированных» языках, любая функция фактически является замыканием, только у некоторых пустое окружение.

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

> «замыкания — объекты для бедных, объекты — замыкания для бедных» // не-помню-чья

ну вот и расскажи, чем в *твоем* случае замыкание лучше CLOS, возможно в сочетании с модулями (только словами, без простынь кода)

пока что я только понял, что у тебя типичный объект

З.Ы. эмуляция объектов замыканиями смотрится особенно странно в лиспе, т.к. есть возможность сделать почти любой необходимый синтаксис вокруг объекта

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

> ну в «функционально-ориентированных» языках, любая функция фактически является замыканием, только у некоторых пустое окружение.

ну так и тема «нам нужны замыкания» там бессмысленна; здесь же речь идет о си-подобных языках

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

> не совсем. строить объекты (в ООПшном смысле, например как в С++) с помощью замыканий немного некрасиво и громоздко, нужен синтаксический сахар (макры/шаблоны)

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

в лиспе никогда не было проблем с дефицитом макр (и круглых скобок) — так что причины эти надуманны

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

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

* как ты заставишь лисп, чтобы он использовал замыкания, сделанные на основе объектов CLOS, точно так же, как и родные функции?

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

>> Конечно это вид примитивного GC

посчет ссылок *можно* использвать как вид примитивного и неполноценного GC, но это не означает, что они *есть* вид примитивного GC

д-во:

1. GC заставляет периодически выгонять из из свопа в оперативку неиспользуемые страницы памяти, подсчет ссылок — нет

2. подсчет ссылок на строках обеспечивает гарантированное время удаления строки, GC на строках — скорее всего нет (хотя может и можно сделать)

в результате имеем GC — это такой вид примитивного посчета ссылок

ыыыы... чуваку, отсыпь, а?

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

>> ыыыы... чуваку, отсыпь, а?

в переводе на русский «по фактам и логике возразить не могу, но очень хочется», так?

По факту то, что ты написал там доказательство, выдает в тебе больного ФГМ.

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

> * как ты заставишь лисп, чтобы он использовал замыкания, сделанные на основе объектов CLOS, точно так же, как и родные функции?

1) зачем мне делать замыкания на основе объектов CLOS, если в CL есть нормальные замыкания и так?

2) обобщённые методы (которые являются объектами CLOS) заставили же =)

ну и вообще проблемы не вижу: reader-макру для символа #\( можно и переопределить... =)

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

reader-макру для символа #\( можно и переопределить... =)

хотя даже этого не понадобится, примерно так:

(defclass closure ()
  ; slot definition
  )

(defmacro closure (context args &body body)
  (let ((clsr (gensym "CLOSURE-")))
    `(let ((,clsr (make-instance 'closure
                                 ,context
                                 #'(lambda ,args ,@body))))
       #'(lambda (&rest args)
           (apply (closure-func ,clsr) args)))))
korvin_ ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

ммм... и ты хочешь сказать, что (eval '(korvin_closure 3 2 1 17)) сработает так же, как и (eval '(some-lisp-function 3 2 1 17)) ?

да

(defpackage #:closure
  (:use #:cl)
  (:export
   #:closure
   #:defclosure))


(in-package #:closure)


(defclass closure ()
  ((closure-environment
    :initform (vector)
    :initarg  :env
    :accessor closure-env
    :type     vector)
   (closure-function
    :initform #'(lambda () nil)
    :initarg  :func
    :accessor closure-func
    :type     function)))

(defmacro closure (args &body body)
  (let* ((clsr (make-instance 'closure))
         (ctxt (make-ctxt clsr args)))
    `(progn
       (setf (closure-func ,clsr)
             #'(lambda ()
                 (symbol-macrolet ,ctxt
                   ,@body)))
       #'(lambda ,args
           ,(bind-env args clsr)
           (funcall (closure-func ,clsr))))))

(defun make-ctxt (clsr args)
  (loop :for x :in args
        :for i :upfrom 0
        :collect `(,x (elt (closure-env ,clsr) ,i))
        :into ctxt
        :finally (progn
                   (setf (closure-env clsr)
                         (make-array (list (1+ i))))
                   (return ctxt))))

(defun bind-env (args clsr)
  `(progn
     ,@(loop :for x :in args
             :for i :upfrom 0
             :collect `(setf (elt (closure-env ,clsr) ,i) ,x))))

(defmacro defclosure (name args &body body)
  (let* ((clsr (make-instance 'closure))
         (ctxt (make-ctxt clsr args)))
    `(progn
       (setf (slot-value ,clsr 'closure-function)
             #'(lambda ()
                 (symbol-macrolet ,ctxt
                   ,@body)))
       (defun ,name ,args
         ,(bind-env args clsr)
         (funcall (closure-func ,clsr))))))
CL-USER 31 > (defpackage #:closure-user
               (:use #:cl #:closure))
#<The CLOSURE-USER package, 0/16 internal, 0/16 external>

CL-USER 32 > (in-package #:closure-user)
#<The CLOSURE-USER package, 0/16 internal, 0/16 external>

CLOSURE-USER 33 > (defclosure foo (x)
                    (closure (y)
                      (closure (z)
                        (+ x y z))))
FOO

CLOSURE-USER 34 > (foo 1)
#<anonymous interpreted function 200F9A6A>

CLOSURE-USER 35 > (funcall (funcall (foo 1) 2) 3)
6

CLOSURE-USER 36 > (defclosure bar (x)
                    (closure (y)
                      (closure (x)
                        (+ x y))))
BAR

CLOSURE-USER 37 > (funcall (funcall (bar 1) 2) 3)
5

CLOSURE-USER 38 > 
CL-USER> (load "/home/korvin/p/cl/1/closure.lisp")
T

CL-USER> (use-package '#:closure)
T

CL-USER> (defclosure korvin-closure (a b c)
	   (+ a b c))
KORVIN-CLOSURE

CL-USER> (defun some-lisp-function (a b c)
	   (+ a b c))
SOME-LISP-FUNCTION

CL-USER> (eval '(korvin-closure 1 2 3))
6

CL-USER> (eval '(some-lisp-function 1 2 3))
6

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