LINUX.ORG.RU

Racket v6.0

 


5

8

Новая версия 6.0 Racket доступна уже сейчас! Racket — мультипарадигменный язык программирования общего назначения, принадлежащий семейству Lisp/Scheme.

Racket 6.0 вышел с обновлённой пакетной системой (уже доступны сотни пакетов).

Изменения в пакетной системы с момента беты (Racket 5.3.4):

  • Можно использовать github репозитарий как пакет.
  • Новый интерфейс для пакетного менеджера.
  • Сама сборка Racket была разделена на 200 пакетов (присутствует минимальная сборка 1/10 от полной).
  • Действия перед сборкой пакета — компиляция байткода, сборка документации.

Другие изменения:

  • Улучшена HTML документация.
  • Включёно в документацию официальное руководство по стилю оформления.
  • JIT-компилятор поддерживает ARM архитектуру.
  • Поддержка retina на Mac.
  • Производительность компилятора Typed Racket улучшена на 50% на некотором числе программ.
  • Новый профайлер для контрактов сообщает как долго проверяются контракты.

>>> Подробности

anonymous

Проверено: maxcom ()
Последнее исправление: maxcom (всего исправлений: 8)

А кто-то знает, что включает в себя минимальная сборка? На сайте не нашел.

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

А когда уже закапаем CL...

А, вот какая главная проблема у ракетки... Исчерпывающая характеристика ;)

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

Не ну реально, зачем люди до сих пор поддерживают старючий CL? Идея «код == данные» - упрощение, которое помогает быстро освоить макровозможности языка, но в тоже время порождает класс сложноотлавливаемых ошибок.

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

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

Это не единственная идея, которую отвергает Sсheme/Racket.

Отвергаются «специальные» (динамические) переменные. В Racket есть parameterize, но он очень медленный (defvar в SBCL уменьшает скорость вдвое, praramterize в Racket — вдесятеро). В стандартных пакетах практически не используются.

Отвергаются with-* макросы. Вместо них функции. И вообще 90% макросов CL реализованы как функции. Приходится писать чуть больше слов.

Отвергается идея разработки в образе (image-based).

Отвергается идея разработки через отладку. В CL отладчик есть всегда. Поэтому ситуации которые втречаются очень редко можно в коде не описывать. Если случится, то можно из отладчика поправить и поехать дальше. В Racket обратная идея: тестами должно быть покрыто всё, отладчик зло, есть утилиты помогающие проверить, что весь код покрыт тестами.

Отвергается идея monkey-patching. В CL если для моей программы нет нужного куска в интерфейсе библиотеки, никто не мешает мне написать (setf (fdefinition lib::api-function) ...). Да и CLOS на этой идее весь построен. В Racket даже импортированную переменную нельзя поменять, если это явно не прописано в API.

В Racket списки иммутабельные. Это не всегда удобно.

-------------

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

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

Спасибо за исчерпывающее объяснение. Если тебе не лень, можешь так-же подробно описать - что ты получил, перейдя с CL на Racket? Стоило ли оно того писать не надо - пусть каждый выводы делает сам :)

yyk ★★★★★
()

за историю успеха: в амазон во все большем количестве проектов используется некая обрезанная (и разрабатываемая с нуля) версия racket заточенная под обработку стандартного формата данных используемого в амазоне.

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

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

  1. Продолжения (continuations). А так же всё, что они дают: futures, async, green threads, generators, удобное программирование логики для GUI (нет необходимости в модальных окнах) и web-сервера — это для меня был самый важный аргумент при переходе на Racket. В CL нереализуемо (cl-cont тормозит).
  2. Модули. а) Возможность называть переменные и модули так, как удобно и понятно. В CL псевдоним для пакета является глобальным, переменную вообще переименовать нельзя. И в коде потом висит что-то типа: cl-gobject-introspection:make-tree-view-column. В Racket псевдоним определяется при импорте модуля через prefix-in, а часто используемые имена можно переименовать через rename-in. б) Так как изменение переменных происходит только через API, то не приходится в каждой функции проверять выполнение инвариантов. в) Хорошая интеграция юнит-тестов. В CL подпункт «а» реализуем через изнасилование readtable (пакет advanced-readtable)
  3. Мощные макросы. Стандартная библиотека для парсинга более-менее типичных случаев. Возможность изнутри макроса добавить функцию в top-level. Развёртка макросов не засоряет пространство имён. Нет CL-проблемы, что compile+load != load. Есть возможность импортировать модуль только для компиляции.

    В CL попытка аналогичной функциональности: xcvb.

  4. define вместо let и возможность делать вложенные функции делают код гораздо более читаемым.

    В CL реализуемо через изнасилование readtable (макрос proga, делал den73)

  5. Большая документированность. В CL принято «код=документация». В Racket документирование API (и инфраструктура для этого) гораздо лучше.
  6. FFI, интегрированный с GC, weak-pointers в GC (включая все виды хэш-таблиц).

    В CL реализуемо, но очень зависит от реализации компилятора (не в стандарте)

  7. Стандартный GUI, который работает на Linux/Windows/Mac.
  8. Много языков с общим пространством имён: может быть один модуль на Typed Racket, другой на Datalog, третий на Swindle — и они будут вместе стабильно работать.
monk ★★★★★
()
Ответ на: комментарий от monk

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

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

так что по идее должно работать в принципе шустрее CLя

На бенчмарках SBCL его обходит. Но с safety=0 и без CLOS.

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

+ Typed Racket для статического контроля типов

+ Контракты (пре- и пост-условия) для проверки данных, переданных в API библиотеки

+ возможность писать плагины на Си (хотя крайне не рекомендуется, лучше через FFI).

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

Отвергается идея разработки в образе (image-based).

Я конечно не лиспер, но сомневаюсь что image есть хорошо. Особенно если нет функции которая получает на входе имя функции и возвращает ее исходник (в смолтолке вроде такое есть).

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

FFI, интегрированный с GC

В CL принято так: (cffi:translate-to-foreign ...) — транслировали. (cffi:free-translated-object ...) — явным образом очистили. При чтении из FFI аналогично.

В Racket любой объект по-умолчанию передаётся GC. То есть, если я передаю строку в (_fun _string -> _void), то моя строка конвертируется в bytes, которые могут использоваться как указатель. Так как эти байты нигде не хранятся после вызова функции, то они благополучно собираются сборщиком мусора. Аналогично с полученными объектами: стандартные функции allocator и deallocator позволяют возложить на GC управление любыми внешними объектами. Единственная потенциальная сложность: если FFI объект ссылается на объекты Racket, эту связь надо показать явно.

Есть политика для callback'ов: скажем, можно указать, что FFI-объект ссылается на обработчики своих событий, причём эти же обработчики могут использоваться и в других объектах — http://docs.racket-lang.org/foreign/foreign_procedures.html

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

Я конечно не лиспер, но сомневаюсь что image есть хорошо. Особенно если нет функции которая получает на входе имя функции и возвращает ее исходник.

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

Такой подход позволяет писать очень быстро (если обнаружилась ошибка, то не надо после каждой попытки исправления воссоздавать причину ошибки), но поощряет неаккуратность и ввод тестов из REPL'а (не записывая их в файл). Также как и наличие в CL мощного отладчика.

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

А просто не ходить в тошниловки уже нельзя?

а где ж я буду харчеваться??

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

А просто не ходить в тошниловки уже нельзя?

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

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

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

если FFI объект ссылается на объекты Racket, эту связь надо показать явно

Пример пожалуйста. Вот есть strcut A{}; sturct B{ A* a; }; void B_setA(B* b, A* a); Как в данном случае указать GC что mark объекта B должен также маркнуть вложенный A? Или по старинке во враппере B дополнительно хранить враппер A? Если да, то как быть с циклическими связями, если мутабельность структур данных для ракета есмь нехорошо?

q0tw4 ★★★★
()

Почему-то когда спрашиваю у кого-то про ракет - отвечают что оно тормозное и глюченное :)

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

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

А где так не принято? :-O

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

Почему-то когда спрашиваю у кого-то про ракет - отвечают что оно тормозное и глюченное

Да не видно там никаких глюков. Ну иногда кажется что переупрощено местами настолько, что придется велосипедить самому, но все что есть работает отлично. Про тормоза - походу автор просто ленивый. Потенциал языка позволяет сделать из него самый быстрый скриптовый язык в мире не трогая семантику. Мне кажется что производительнее инфраструктуру скрипта придумать просто невозможно. Вот бы еще переписать по возможности либы на typed racket было б совсем C++ по скорости

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

там имелось в виду редактирование и запуск прямо в репле

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

переписывание либов все равно по скорости к C++ не приблизит.

racket - более высокоуровневый язык и рантайм в любом случае производит кучу нужных проверок.

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

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

А где так не принято? :-O

Во всех остальных языках программист запускает не функции (в существующем образе), а программу в целом.

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

Почему-то когда спрашиваю у кого-то про ракет - отвечают что оно тормозное и глюченное

Потому что встречают по одёжке IDE. Также как Java имеет имидж эпического тормоза (а Python не имеет, несмотря на гораздо меньшую скорость) только из-за того, что разработчики имели неосторожность сделать IDE на собственном независимом тулките, отрисовывающем всё самостоятельно.

Также и Racket. В состав входит IDE DrRacket, который достаточно долго загружается (около 20 секунд). Кроме того, в настройке по-умолчанию он делает постоянный синтаксический контроль вводимого текста и ругается на недовведённые конструкции. Новичок запускает DrRacket, долго ждёт, вводит что-то вроде (ведь говорят, что Racket — это Scheme)

(define x 1)
(define x (+ x 1))
(display x)

получает

Module Language: only a module expression is allowed, either
    #lang <language-name>
 or
    (module <name> <language> ...)

дочитывает что надо вначале написать «#lang racket»

#lang racket
(define x 1)
(define x (+ x 1))
(display x)

Получает

module: duplicate definition for identifier in: x

так как Racket — язык похожий на scheme, но более строгий (в Racket вместо второго define обязательно писать set!). После этого новичок закрывает DrRacket и пишет на форуме «глючная фигня этот Racket, даже примитивные программы не запускаются».

К слову, именно из-за такого сценария Racket переименовался из PLT Scheme в Racket. Но «осадок остался».

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

racket - более высокоуровневый язык и рантайм в любом случае производит кучу нужных проверок

Проверки можно не делать (если статически доказано в Typed Racket или вручную программистом через unsafe-функции). Неустранимые тормоза — GC и boxing. Поэтому по скорости можно соревноваться с C# и Java, но не с C/C++. Разве что считать, что http://planet.racket-lang.org/package-source/jaymccarthy/superc.plt/2/0/examp... — тоже программа на Racket :-)

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

Что, правда, в racket постоянно перезапускают интерпретатор для опробования изменений?

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

Я говорю про проверки в самом рантайме и в генерации кода.

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

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

Или по старинке во враппере B дополнительно хранить враппер A?

Фактически да. Представление B будет (vector b (B-a b)), где b — указатель на структуру B.

Если да, то как быть с циклическими связями, если мутабельность структур данных для ракета есмь нехорошо?

Не «мутабельность нехорошо», а «неявная мутадельность по-умолчанию нехорошо». У векторов, строк и полей структур мутабельность определяется явно программистом.

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

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

У sbcl в крайних случаях отключается контроль количества аргументов, их длины, типа... В Racket никто не мешает это тоже отключить, но нет смысла. В рабочей программе всё-равно никто не станет так извращаться (если скорость настолько критична, то проще кусок на SuperC переписать).

Ещё один нюанс: JIT. Из-за него чем дольше программа работает, тем лучше Racket по отношению к SBCL, но в бенчмарках JIT медленнее.

В остальном: вложенные процедуры, запрет изменения констант позволяют оптимизировать Racket лучше. Inlining, constant propagation на усмотрение компилятора. Но, опять же, для того чтобы это стало заметно, проект должен быть достаточно большим. А здесь сравнивать 1-к-1 становится невозможно.

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

constraint

И это тоже. Но и constant тоже. http://en.wikipedia.org/wiki/Constant_folding#Constant_propagation

Я в том смысле, что в CL всегда в

(defun foo (x)
   ...)

(defun bar (y)
   (foo x))

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

В Racket почти всегда foo будет или инлайниться (если нигде больше не вызывается) или вызываться по прямому указателю. Единственный случай, при котором придётся вызывать как в CL: наличие в том же модуле, где определён foo какой-нибудь функции, где есть (set! foo ...).

Таким образом, получаем удвоение скорости запуска функций (аналог в C++ обычных методов класса против виртуальных).

Аналогично, с другими значениями. Если я пишу

lib.rkt
------
#lang racket
(provide foo)
(define x 10)
(define (foo) (+ x 20))

test.rkt
-------
#lang racket
(require "lib.rkt")

(define test-foo (foo))

то оптимизатор свернёт определение в (define test-foo 30).

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

Во всех остальных языках программист запускает не функции (в существующем образе), а программу в целом.

Да ну. Я когда пишу на clojure, elisp, ruby, python, ocaml - всегда по возможности юзаю репл.

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

Я когда пишу на clojure, elisp, ruby, python, ocaml - всегда по возможности юзаю репл.

Да ну? Опиши процесс на примере Питона.

Написал в редакторе (или IDE)

def foo:
  x + 1

Куда нажал, чтобы применить в REPL и проверить? Потом понял, что пропустил return. Исправил в редакторе

def foo:
  return x + 1

Снова куда нажал?

Потом понял, что есть ошибка в импортированной библиотеке: открыл её, исправил — как в REPL обновил функцию из импортированной библиотеки?

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

Неустранимые тормоза — GC и boxing.

Да ну. Разве typed racket не анбоксит примитивные типы? Вроде как читал что анбоксит. Плюс ко всему можно некоторые локальные GC объекты заменить на стековые переменные.

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

Разве typed racket не анбоксит примитивные типы?

Те, что может — да (малые целые). Но, например float/double уже никак.

Плюс ко всему можно некоторые локальные GC объекты заменить на стековые переменные.

Это понятно. Само наличие GC даёт оверхед.

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

GC в другом потоке надо пускать наверно. Если ядер больше, чем потоков алгоритма - все норм. Выпил GC вообще нельзя ставить целью. Существуют случаи когда даже на С++ приходится узать GC или refcount с weak reference, что в принципе не факт что шустрее. Просто если научится распознавать все возможности замены GC на примитивные модели памяти с единственным овнером, то и выйдет в результате самый шустрый скрипт в мире. И станет мир раем, в котором писать не на ракете никто не станет (ну кроме плюсовиков)...

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

И станет мир раем, в котором писать не на ракете никто не станет (ну кроме плюсовиков)

... и все ходить будут строем :-D

Если бы это было так, то не было бы программ на питоне, руби, перле, ..., баше. Есть ведь ещё такая штука как нравится/не нравится синтаксис. Кроме того, Racket не очень лояльно относится к eval (по сравнению с tcl например).

Я могу на bash написать

a='echo $i'
for i in 1 2 3; do eval $a; done

А на Racket нельзя из eval обратиться к локальной (лексической) переменной того места, где eval запущен.

Ещё есть Haskell с принципиально другой семантикой: вычисления ленивые и у функции всегда один аргумент.

Идеологические противоречия с CL я уже описал. Они неоднозначны. Если бы не call/cc, я скорее всего просто дописал бы всё чего мне не хватает к CL (и не лишался бы дебаггера, например).

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

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

GC в другом потоке надо пускать наверно. Если ядер больше, чем потоков алгоритма - все норм.

Не норм. Когда GC перемещает объект, к нему нет доступа. Когда GC проверяет время изменения объекта, нет доступа на запись. Блокировки.

Кроме того, количество аллокаций памяти в средней программе на любом лиспе обычно выше, чем на C/C++. Так как есть на Си тебе надо 4 числа, ты напишешь int a[4] = {1, 10, 15, 20}; и получишь статическое размешение на стеке. В лиспе, скорее всего, напишешь (list 1 10 15 20) и получишь 4 malloc'а (+ 4 free при выходе из области действия).

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

Довожу код до рабочего состояние в REPL и копипастю в редактор

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

Кроме того, в CL помимо «переопределить функцию в REPL» есть ещё команда «сохранить образ» (save-lisp-and-die). Если долго отлаживаешь и надо перезагрузиться — помогает.

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

Самый эпичный вопрос про образ: если пописал, пописал в образе и вдруг неожиданно все получилось и захотелось сохранить то, что накалякал в файл для потомков, есть ли такая функция? Или по истории ввода команд вспоминать?

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

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

В CL есть. http://sbcl.sourceforge.net/manual/#Function-sb_002dext_003asave_002dlisp_002... — его даже exe-шником сделать можно (тогда запускать можно будет даже там, где не установлен лисп).

В Racket есть сохранение «истории ввода команд».

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

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

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

дак сохранять надо в текст.

Образ в текст??? Это как?

Вот пример:

REPL:
> (defvar *s*
    (let ((seq (make-array 1024 :element-type 'character)))
       (with-open-file (stream "/dev/urandom")
          (read-sequence seq stream))))

> (cffi:defcvar "errno" :int :read-only t)
> (defvar *ptr* (get-var-pointer '*errno*))
> (save-image)

Какой текст должен записаться в образ? Хотя бы приблизительно.

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

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

Само собой сохранять надо выборочно (местами вручную). Но не набирая же все заново. Лучше всего имея (get-fun-source f) для просмотра набранного давным давно.

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

(defvar *s*

(let ((seq (make-array 1024 :element-type 'character))) (with-open-file (stream «/dev/urandom») (read-sequence seq stream))))

(cffi:defcvar «errno» :int :read-only t)

(defvar *ptr* (get-var-pointer '*errno*))

Наглядная демонстрация того, что образ неудобен. Потому что хрен сочинишь общий метод сохранения такого. Только хистори и ручным выпилом лишних команд типо (defvar test ...) (или всетаки неручным)

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

Когда GC перемещает объект, к нему нет доступа.

А он вообще обязан такое делать? Вроде как есть алго и без этого.

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