LINUX.ORG.RU

от @ugoday https://www.linux.org.ru/forum/development/17376931?cid=17400463

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

значит Scheme мне не подходит, мне практические задачи решать.

Elidee
() автор топика

комментарий от @den73 https://www.linux.org.ru/forum/development/17376931?cid=17400658

Не понял, что тут с тобой случилось и почему ты не можешь отвечать

почему-то большен нет ссылки «ответить на это сообщение». Пришлось создать тему здесь.

Посмотрел описание и документацию по Racket - выглядит как очень удобный учебный язык. Вряд ли подойдет.

Elidee
() автор топика

комментарий от @ugoday https://www.linux.org.ru/forum/development/17376931?cid=17400662

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

В программировании я не новичек, а вот с лиспами никогда не пересекался.

Elidee
() автор топика

комментарий от @lovesan https://www.linux.org.ru/forum/development/17376931?cid=17400766

Надо брать CL.

Технически, даже если потом работать на Clojure - учить и втыкать надо сначала все-равно в CL.

Scheme/Racket - это все учебные языки для ресерча в академии и студентоты. Накрайняк - для мелкого скриптинга (gnu guile).

Спасибо за ответ.С какой книги/тьюториала/гайда лучше начинать? Есть ли список проверенных книг?

Elidee
() автор топика

комментарий от @lovesan https://www.linux.org.ru/forum/development/17376931?cid=17400778

Вот чето есть, я не пробовал правда. Но вроде это известный лиспер писал

https://github.com/pokepay/aws-sdk-lisp/tree/master

Плюс, смотри вот тут: https://awesome-cl.com/

там есть некоторые библиотеки.

Как из схемы работать с базой данных? Тот же Postgres?

Куча ORM и всякого есть

итд

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

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

А вот для clojure есть aws-api причем похоже от компании в которой работал создатель clojure.

Что еще больше удивило, оказывается для Aws Lambda можно использовать Clojure Writing AWS Lambda Functions in Clojure

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

Посмотрел описание и документацию по Racket - выглядит как очень удобный учебный язык. Вряд ли подойдет.

Racket родился как учебный, но нынче полнофункциональный язык общего назначения. Примерно как Паскаль был изначально учебный, а потом из него сделали Delphi.

Теперь по списку:

Есть ли на схеме библиотеки для AWS?

https://github.com/greghendershott/aws

Как из схемы работать с базой данных? Тот же Postgres?

https://docs.racket-lang.org/db/using-db.html Есть переиспользование соединений: https://docs.racket-lang.org/db/using-db.html .

Как из схемы читать данные из Kafka в avro формате?

https://github.com/Bogdanp/racket-avro

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

Неожиданно :).

И в добавок похоже строки по умолчанию идут в Unicode, что могло бы избавить в случае описанном den73 от проблем с производительностью при перекодировке.

Тогда у меня вопрос - в чем отличие того же Racket (или вообще scheme) от Common Lisp? Что их делает разными языками?

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

Тогда у меня вопрос - в чем отличие того же Racket (или вообще scheme) от Common Lisp? Что их делает разными языками?

В первую очередь — идеология: Анализ пользователей Common Lisp и Racket

На Common Lisp обычно получается чуть быстрее написать. Но намного сложнее получить гарантии надёжной работы. И принято документировать между строк кода.

В Racket же есть контракты (https://docs.racket-lang.org/guide/contracts.html), типизированные модули (https://docs.racket-lang.org/ts-guide/index.html), стандарт на документацию (https://docs.racket-lang.org/scribble/index.html).

Причём всё сделано не в стиле, принятом в C и Common Lisp, когда достаточно, чтобы библиотека работала в 90% случаев, а если что не так, то будет UB в C или отладчик в CL.

Если контракты, то с возможностью указать контракт типа «данная функция возвращает функцию от числа, которая всегда возвращает число не меньше переданного первого аргумента»:

(->i ([argument real?])
      [result (argument) (-> real? (>=/c argument))])

Если типизация, то система типов уровня Haskell, а не C++.

Если документация, то с возможностью вывода в HTML и TeX и возможностью вычислять во время формирования текста.

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

Если компиляция, то воспроизводимая. В CL все модули компилируются в одной виртуальной машине и процесс компиляции модуля может влиять на компиляцию следующего. В Racket компиляция каждого модуля в изолированной среде.

В недостатке — чуть ниже производительность (аналога declare unsafe из CL нет) и хуже отладчик.

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

Scheme мне не подходит, мне практические задачи решать

Для решения практических задач, конечно, нужно углубляться непосредственно в язык, на котором ты будешь их решать. Но кроме конкретных знаний об использовании конкретного языка нужны ещё общие, абстрактные знания о программировании как таковом (как устроены вычислительные процессы и как их можно организовывать), независимо от языка. И здесь уже, понятное дело, конкретный язык особого значения не имеет. Такие знания переносятся на любой язык, который ты уже освоил или захочешь освоить когда-нибудь. ROI просто зашкаливает %)

Такого рода материала полно как раз на схеме — Little/Seasoned/Reasoned Schemer, HTDP, SICP. Так сложилось исторически (тм).

Конечно, немножко схемы выучить придётся, но после кложи/CL это будет изи катка. Проще схемы только наскальная живопись.

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

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

Вот нет. То есть, конечно, «настоящий программист может программировать на Фортране на любом языке». Но в реальности язык определяет как минимум базовую абстракцию: байтовый массив для Си/Си++, список для лиспов, массив (многомерный) для APL/J/K, бесконечноый ленивый список для Haskell.

В этом смысле Racket также почти уникален, так как на него можно переносить код практически с любого языка не меняя семантики: семантика позволяет работать как с ленивыми списками в стиле Haskell, так и с указателями в стиле Си++.

В то время, как перенести на Си++ что-то вроде

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
fib = (fibs !!)

достаточно проблематично.

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

LoL тоже на С++ замучаешься переводить, но SICP даёт такую базовую базу, что её можно будет применять где угодно.

Взаимоисключающие утверждения. Часть SICP в C++ применить невозможно (макросы, лямбды), часть возможно, но вредно (рекурсия, функции как параметры). При этом такой базовой идеи C++ как итераторы в SICP вообще нет.

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

Часть SICP в C++ применить невозможно (макросы, лямбды)

Я про С++ знаю мало, но там же есть и то, и другое, разве нет?

вредно (рекурсия, функции как параметры)

Спорно. С чего бы это вредно?

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

Я про С++ знаю мало, но там же есть и то, и другое, разве нет?

Макросы - скорее нет, чем да. Отладки макросов нет, возможности очень ограничены, использовать не рекомендуется. Аналог лисповых макросов — кодогенерация. Но это очень накладно.

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

Спорно. С чего бы это вредно?

С того, что в Си++ вызов функции — это аппаратный вызов с использованием стека. Причём оптимизация хвостовых вызовов отсутствует. Поэтому если в Си++ написать, как принято в лиспах что-то вроде

int f(int n, int k)
{
   if (n) f(n-1, g(n, k)); else return k;
}

то это будет значительно медленнее, чем через нормальный for.

monk ★★★★★
()

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

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

Еще вопрос про различия. Вот код на Racket:

> (list cons car cdr)
(#<procedure:mcons> #<procedure:mcar> #<procedure:mcdr>)

делает список из трех функций Почему в Common Lisp тот же код выдает ошибку?

> (list cons car cdr)

*** - SYSTEM::READ-EVAL-PRINT: variable CONS has no value
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of CONS.
STORE-VALUE    :R2      Input a new value for CONS.
ABORT          :R3      Abort main loop
Break 1 [4]> 

при этом эти функции точно определены:

[6]> (cons 1 2)
(1 . 2)

Ведь даже в Go можно передать функцию как параметр по имени:

v1 := router.Group("/v1")
{
	v1.POST("/login", loginEndpoint)
	v1.POST("/submit", submitEndpoint)
	v1.POST("/read", readEndpoint)
}
Elidee
() автор топика
Ответ на: комментарий от buddhist

Насколько я помню, в SICP не определяется ни одного макроса.

Да, виноват. В середине ответа начал думать про произвольный учебник на лиспе.

И язык-как-данные там есть. Там где про метаязыки.

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

Почему в Common Lisp тот же код выдает ошибку?

Потому что в Common Lisp есть отдельно «переменная cons» и «функция cons». Можно писать так

(setf cons 1)
(cons cons cons) ; вернёт пару из двух единиц

(list cons car cdr)

В CL это будет

(list #'cons #'car #'cdr)

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

Я же написал, так сделали, чтобы можно было именовать переменные и функции одинаково.

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

В результате всё равно утечка потенциально возможна. Например, если в (labels ((list ...)) (m ...)) макрос m использует функцию list, то будет использоваться локально определённая. Поэтому в CL просто просят не перекрывать стандартные функции локальными и не использовать в макросах что-то кроме стандартных функций или функций из пакетов, не предназначенных для пользователя библиотеки.

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

Вот это как раз пример того, где одна и та же задача в CL и Scheme/Racket решена кардинально по-разному. В CL сделано так, чтобы было проще отлаживать. В Scheme/Racket сделано так, чтобы вызов макроса гарантированно делал то, что предполагал разработчик макроса.

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

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

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

то это будет значительно медленнее, чем через нормальный for.

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

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

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

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

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

Лямбы же практически в любом языке есть. Или в тут они какие-то особенные?

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

Увы, но нет.

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

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

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

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

Функции - это не переменные. Ещё такое же имя может быть у класса, у типа, …

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

И отдельного понятия типа тоже нет. Есть предикат, который функция-переменная. Если вернул на объект истину, значит этот объект соответствующего типа.

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

Я исхожу из логики «если есть, но неудобно и сложно, значит нету». В пределе defmacro и #define делают одно и то же, но в одном случае макропрограммирование считается удобным подспорьем, а в другом — кошмаром, который следует избегать любой ценой. И эти практические выводы гораздо важнее теоретической применимости.

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

Я про синтаксис. В Racket

(define a (...)) ; а здесь станет классом/значением/функцией/...

В Си++

int a(...) ... // функция

int a = ... // переменная

class a { ... } // класс

typedef a // тип
monk ★★★★★
()