LINUX.ORG.RU

Синдром Эллочки-людоедки и lisp

 


3

4

В целом, мне нравится lisp - импонирует сама концепция lisp-a, я без особых проблем читаю s-выражения, нравиться его поддержка в emacs. И я использую emacs lisp как язык для всякой мелочевки.
С другой стороны простота концепции, когда первый аргумент s-выражения - функция, а остальные єлементы - параметры, имеет свой неприятный побочный эффект: огромное, неструктурированное пространство имен. Примерно за это я не люблю python - надо помнить кучу тонких особенностей и фич языка. А в lisp надо помнить кучу нужных функций. В книжке Грема их приблизительно 1000. В противопложность java - минимум ключевых слов, а вся функциональность вынесена в методы, которые выясняются по автодополнению и доктипу.
Второй нюанс, ХЗ, может зависит от конкретной реализации. Все функции из заргуженных пакетов валятся в одно пространство имен. Т.е. если Васян по глупости или злому умыслу перепишет стандартный car можно поиметь проблем, особенно если такой car подгружаю в составе какой-то библиотеки. Хотелось бы импорта a-la python import my-package as mp с последующим доступом типа (mp.foo).
Собственно, вопрос. Как борются с этими проблемами местные лисперы. Особенно с первой. Лично я запомнил около полусотни функций, примерно из списка снипеттов, к части прибавляю p и автоматом получаю знание новых. Может есть компактный список must know функций как перечень самых популярных коменд для emacs?
Вторая проблема больше для собственного кругозора, я сомневаюсь, что буду в большой команде использовать lisp, та и не годиться он для этого.

С пакетами вопрос решился. А с насышенным и неструктурированным пространством имен или с кратким справочником на манер такого - нет.

★★★★

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

Лол :-) Ну давай ещё разок, теперь на пальцах :-)

(defpackage my-package
  (:use cl)
  (:export person machine name))

(in-package :my-package)

(defclass person ()
  ((name :accessor name)))

(defclass machine ()
  ((name :accessor name)))

Что ты там использовал? :-) slime-fuzzy-complete-symbol ? :-) Ок:

Completion:        Flags:   Score:
------------------ -------- --------
my-package:        -------p 134.19
my-package:machine ---ct--- 1.34
my-package:name    -fg----- 1.34
my-package:person  ---ct--- 1.34

Ну и как тут в этом кипише понять что у machine и person есть аксессор name? :-) Расскажи :-)

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

Ну и как тут в этом кипише понять что у machine и person есть аксессор name? :-) Расскажи :-)

Нажми Alt-. на my-package:name и увидишь:

/home/monk/Программирование/Lisp/test.lisp
  (DEFMETHOD MY-PACKAGE:NAME MY-PACKAGE:MACHINE)
  (DEFMETHOD MY-PACKAGE:NAME MY-PACKAGE:PERSON)
monk ★★★★★
()
Ответ на: комментарий от monk

Угу. А если потом появился наследник baz'а foo, то его новые методы будут foo-*, а унаследованные — baz-*.

О, теоретики пожаловали :-) Велкам :-) Ну и где здесь проблема? Есть 2 класса - foo и baz - их методы имеют префиксы foo- и baz- соответственно. Зачем мне кодировать в именах методов отношения наследования? Лол.

Не говоря уже, что писать (sequence-build (sequence-first x) (sequence-rest x)) после двадцатого повторения sequence начинает утомлять

И поэтому всеми любимые Hunchentoot и прочие CL-PPCRE, которыми с удовольствием пользуются 100% лисперов, написаны в стиле foo-method-name, и их, почему-то, не задалбывает. Лол.

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

Нажми Alt-. на my-package:name и увидишь:

О да, очень удобно и продуктивно. Хочешь узнать какие перегрузки у обобщённых функций - ходи и нажимай Alt-. на каждом. И так каждый раз. Сразу видно теоретика.

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

Есть 2 класса - foo и baz - их методы имеют префиксы foo- и baz- соответственно.

А метод baz-bar, который foo унаследовал от baz? И вообще, методы CLOS они не по одному объекту диспетчеризуются, а по нескольким. Вот, например https://common-lisp.net/project/cffi/manual/cffi-manual.html#Defining-Foreign... . Предлагаешь free-translated-object переименовать в t-free-translated-object ?

всеми любимые Hunchentoot и прочие CL-PPCRE ... написаны в стиле foo-method-name, и их, почему-то, не задалбывает.

???

hunchentoot: методы start, stop, start-listening, accept-connections, ...

cl-ppcre: все методы create-scanner, scan, scan-to-strings.

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

Хочешь узнать какие перегрузки у обобщённых функций - ходи и нажимай Alt-. на каждом. И так каждый раз. Сразу видно теоретика.

А это нужно часто? Обычно знаешь протокол (то есть набор обобщённых функций) того, с чем работаешь. Щёлкать приходится разве что, чтобы проверить, необходимо ли, например, преобразовать символ в строку или и так сойдёт. Причём протокол метода дублируется в строке статуса: если написать "(name ", внизу будет "(name object)".

Сразу видно теоретика.

Мои проекты на github'е. А где твои?

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

Автокомплишен с хэлпом позволит выбрать и написать что-то подходящее по смыслу

Плохо позволяет. Кроме прямых методов объекта есть огромное количество функций (или методов других классов), где этот объект выступает аргументом. Например, в Java есть метод toString, но нет toStringBuffer. В результате, если вместо чтения документация пользоваться автодополнением, то про StringBuffer просто не узнаешь.

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

А метод baz-bar, который foo унаследовал от baz?

А метод baz-bar как относился к baz, так и будет относиться к нему, потому что всякий foo - это baz.

И вообще, методы CLOS они не по одному объекту диспетчеризуются, а по нескольким.

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

hunchentoot: методы start, stop, start-listening, accept-connections, ...

acceptor-address acceptor => address 
acceptor-port acceptor => port 
acceptor-read-timeout acceptor => read-timeout 
acceptor-ssl-certificate-file ssl-acceptor => ssl-certificate-file 
acceptor-ssl-privatekey-file ssl-acceptor => ssl-privatekey-file 
acceptor-ssl-privatekey-password ssl-acceptor => ssl-privatekey-password 
acceptor-write-timeout acceptor => write-timeout 

cl-ppcre: все методы create-scanner, scan, scan-to-strings.

Ну тут да, я спутал с другим софтом.

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

Обычно знаешь протокол (то есть набор обобщённых функций) того, с чем работаешь.

Давай про «обычно». Обычно программа читается в 10 раз чаще, чем пишется. И когда код нагромождён зашифрованными выражениями типа (let ((nm (name obj))) ... ), то становится плохо. Префиксы делают код для читателя более явным, - сравни - (let ((nm (person-name obj))) ... ), поэтому их использование предпочтительно.

Мои проекты на github'е. А где твои?

И мои там же. Но я больше практик.

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

Лучше так:

let ((nm (person-name person))) ... )
ados ★★★★★
()
Ответ на: комментарий от azelipupenko

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

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

Твоя анонимность на этом форуме подсказывает, что ты тут на всех смотришь свысока как на всяких теоретиков

Не свысока, а с практической точки зрения. Префиксы удобны на практике. Да, без них можно обойтись. Да, они избыточны. Да, их дольше набирать. Зато: а) информативность со стороны IDE выше; б) код понятнее и читается легче. А так то, теоретически, префиксы не нужны, они лишние.

а сам лисп нихрена не знаешь.

Лол. Чего там можно не знать?

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

Лол. Чего там можно не знать?

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

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

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

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

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

Автокомплишен с хэлпом позволит выбрать и написать что-то подходящее по смыслу

Плохо позволяет. Кроме прямых методов объекта есть огромное количество функций (или методов других классов), где этот объект выступает аргументом. Например, в Java есть метод toString, но нет toStringBuffer. В результате, если вместо чтения документация пользоваться автодополнением, то про StringBuffer просто не узнаешь.

Строго говоря, я очень условную схему набросал.

про StringBuffer просто не узнаешь

Если этот метод не используется в проекте, то он и не нужен. Иначе как я про него не узнаю?

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

Префиксы делают код для читателя более явным, - сравни - (let ((nm (person-name obj))) ... ), поэтому их использование предпочтительно.

То есть хочешь сказать, что в коде на Java/C++ тебе тоже obj.name() — зашифрованное выражение, а нужно obj.personName()? Ведь информации ровно столько же. Или читателю кода принципиально важно знать, что в (let* ((obj (make-instance 'employee)) (nm (person-name obj)) имя объекта относится не к классу employee, а к его предку person?

Префиксы были нужны до появления CLOS. Потому что для defstruct другого метода доступа просто не было.

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

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

Есть ещё такая вещь как унаследованный код. Если что-то было структурой, а потом стало классом CLOS, то для совместимости API методы останутся в стиле доступа к полям структуры. А то я тоже могу поцитировать K&R и на этом основании утверждать, что в современном Си надо писать именно так.

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

Если этот метод не используется в проекте, то он и не нужен. Иначе как я про него не узнаю?

Так тогда зачем дополнение через точку? Если ты знаешь метод, то по первым буквам слов автодополнение тебе развернёт полное имя метода. А если ты его не знаешь, то он и не нужен.

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

Смотрю, программирование вслепую очень популярно.

Доку читать нужно. Но помнить все методы и сигнатуры, а еще таблицы, поля и прочие сущности СУБД - зачем? Автодополнение и доктип как раз помогают не забыть и точно выбрать что нужно. Ну и меньше набирать, да.

ЗЫ. Я навигатором тоже пользуюсь, но ПДД руководствуюсь в первую очередь.

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

То есть хочешь сказать, что в коде на Java/C++ тебе тоже obj.name() — зашифрованное выражение, а нужно obj.personName()?

Лол. В Java/C++ синтаксис не основан на S-выражениях. И это радикально отличает эти языки от Лиспа. Там нет вложенных S-выражений различных уровней вложенности. Поэтому код там выглядит более просто и упорядочено. И то, что означает obj.name(), скорее всего, будет выглядеть очевидным.

Или читателю кода принципиально важно знать, что в (let* ((obj (make-instance 'employee)) (nm (person-name obj)) имя объекта относится не к классу employee, а к его предку person?

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

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

метод baz-bar как относился к baz, так и будет относиться к нему, потому что всякий foo - это baz

А вот тут не соглашусь. Я работаю с foo, который, хотя и baz, но, cемантически, это уже несколько иная сущность. Тем более, что foo может переопределять baz-bar. Т.е. baz-bar уже совсем не baz-bar. Потекла абстракция.

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

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

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

Используй apropos и поиск по документации по ключевым словам.

я сомневаюсь, что буду в большой команде использовать lisp, та и не годиться он для это

лисп, с точки зрения заключённых в нём идей абсолютно ничем не хуже (а точнее, намного лучше) какого-нибудь питона. К вопросам размеров команды это ортогонально. Для примера, подумай, какого размера «команда» написала EMACS.

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

В CL есть предупреждение при таком действии, если читать предупреждения, то беды не случится. Зато ты можешь вносить горячие патчи в существующие библиотеки, не форкая их (т.к. в лиспе всё public и всё заменяется на горячую). Это даёт просто немеряную мощь на этапе интеграции и поддержки.

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

Это не связанные вещи. В CL можно функции распихать по пр-вам имён и их станет меньше, в EMACS - раздать префиксы.

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

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

Автодополнение

В лиспе более важен апропос, чем автодополнение. Т.к. это «дополнение из середины слова». При наличии дисциплины префиксов/пространств имён, автодополнение тоже работает.

Но да, есть такая вещь, что в статически типизированных языках однажды написанный тип переменной сразу резко сужает количество применимых методов. На мой взгляд, здесь лисп теряет, причём, он больше теряет за счёт многословности (одно и то же название типа может повторяться 2-3 раза на строке, а в С++ оно было бы упомянуто один раз). Притом, когда появляется auto и вывод типов, статически типизированные языки теряют это преимущество обратно :)

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

Я работаю с foo, который, хотя и baz, но, cемантически, это уже несколько иная сущность.

Есть принцип Лисковой, по которой говорит, что foo - это обязательно в точности baz. И есть реальная жизнь языков «с ООП», где он не обязательно в точности baz. Так что да, абстракция потекла, во всех ООП языках. Может быть, в Расте и Голенге её заклеили, хотя я ХЗ.

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

А вот тут не соглашусь. Я работаю с foo, который, хотя и baz, но, cемантически, это уже несколько иная сущность.

Лол. Если foo *семантически" иная сущность - это нарушение принципа Лисков, на котором стоит всё ООП. Так что своим несогласием Вы уже не придерживаетесь ООП.

Тем более, что foo может переопределять baz-bar.

Он может его переопределять, но это ни в коем случае не должно изменять семантику! Иначе - потекла абстракция.

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

Используй apropos

здесь подробнее

более адекватна система с тегами

Она есть?

К вопросам размеров команды это ортогонально.

Есть опыт работы в команде с текучкой с использованием python и java. Статика практичней. Такое убеждение, не обсуждается.

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

Ты сам и ответил на свое возражение. Достаточно лишь помнить приблизительно где и что искать. Замечу, что там еще сильно упрощается кодогенерация. Пишешь реально меньше кода.

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

Если foo *семантически" иная сущность - это нарушение принципа Лисков

Ну, нарушение. И шо? Мне проще думать, что foo - это такая вот сущность. И опускаться до уровня baz только если понадобиться. Потому, когда в foo всплывает baz-bar, причем он переопределен, это как минимум выглядит некошерно.

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

Ну, нарушение. И шо? Мне проще думать, что foo - это такая вот сущность.

Тогда нет смысла использовать наследование.

И опускаться до уровня baz только если понадобиться.

Не опускаться, а подниматься. baz - родитель foo.

Потому, когда в foo всплывает baz-bar, причем он переопределен, это как минимум выглядит некошерно.

В этом состоит суть полиморфизма. Если это «некошерно», то нечего вообще говорить про ООП, наследование и т.п.

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

Тогда нет смысла использовать наследование.

Есть. Например, SpecialBill, конечно, Object. Но только Object там хорошо если прадедушка и вообще ни о чем. А меня интересует, например, SpecialBill и его переопределенный toString(). А ты мне предлагаешь использовать что-то типа objectToString(). Cистема префиксов перестает быть информативной.

Не опускаться, а подниматься. baz - родитель foo.

Смотря откуда смотреть.

А по функциям да, я согласен, префиксы очень даже хороши.

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

А меня интересует, например, SpecialBill и его переопределенный toString(). А ты мне предлагаешь использовать что-то типа objectToString().

Прекрасно. Но, во-первых, тут не меняется семантика метода. Во-вторых, это и есть Object::toString(). Просто в Java это не так актуально, как в Лиспе. В Лиспе я предлагаю object-to-string, да.

Cистема префиксов перестает быть информативной.

С чего это вдруг код (let ((str (object-to-string object))) ... ) перестал быть информативным?

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

здесь подробнее

Ну, в емаксе это что-то вроде C-h f, а можно ещё попробовать M-x apropos, а в CL есть просто функция apropos. Я емаксом почти не пользуюсь, поэтому так не помню слёту, а под рукой нет. Короче, apropos apropos :)

cистема с тегами

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

Статика практичней.

Статика к размеру команды, может быть, и не ортогональна. Да и вообще, статика проще и позволяет искать ошибки в программе, не запуская её. Поэтому она и лучше там, где её можно применить. Но я говорил о пр-вах имён, утверждая, что ось статичность/динамичность ортогональна к оси одно-большое-пр-во-имён/много-маленьких. Если из C выбросить void * и преобразование указателя в число, то будет статика, но пр-в имён от этого не появится.

Ты сам и ответил на свое возражение.

Я не возражал, а просто рассматривал с разных сторон. Если бы лисп был достаточно хорош, то вообще не было бы ни Явы, ни Питона.

это нарушение принципа Лисков, на котором стоит всё ООП.

Кто-то сказал, что оно якобы должно на нём стоять, но де-факто оно на нём не стоит.

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

Кто-то сказал, что оно якобы должно на нём стоять, но де-факто оно на нём не стоит.

У кого как. У меня, по факту, стоит :-)

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

Но, во-первых, тут не меняется семантика метода

Я могу не вывести то, что считалось важным в родительском методе. Вот тебе уже и не та семантика, несмотря на общуюю сигнатуру.

С чего это вдруг код (let ((str (object-to-string object))) ... ) перестал быть информативным?

Учитывая, что object тут указывает на класс, то зачем мне это наследие забытых предков? Ну хорошо, пускай abstract-sales-document у нас дедушка special-bill, тогда (let ((str (abstract-sales-document-to-string object))) ... ) применительно к special-bill как минимум выглядит коряво.

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

Я могу не вывести то, что считалось важным в родительском методе. Вот тебе уже и не та семантика, несмотря на общуюю сигнатуру.

Ничего не понял. Семантика метода toString() - сгенерировать и вернуть строковое представление объекта. Если у кого-то toString() вместо этого запустит космический спутник в космос, то он нарушит принцип Лисков и изменит семантику.

Учитывая, что object тут указывает на класс, то зачем мне это наследие забытых предков?

Какое ещё наследие предков? object - это класс. У него есть метод object-to-string.

Ну хорошо, пускай abstract-sales-document у нас дедушка special-bill, тогда (let ((str (abstract-sales-document-to-string object))) ... ) применительно к special-bill как минимум выглядит коряво.

Корявость тут в абсолютно бессмысленной иерархии и в абсолютно бессмысленных именах классов. Если же рассмотреть что-то более логичное, то будет всё хорошо, например: (let ((name (person-name employee))) ...). Всякий рабочий - это человек. Тут кристально ясно, что код получает человеческое имя рабочего.

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

вернуть строковое представление объекта.

Оно может очень разное быть. Например, Instance of Object@123456 будет отличаться от SpecialBill: Contr-1234; ContrDate-21/08/2018; Partner-azelipupenko.
А в результате что-то где-то там может поламаться.

Какое ещё наследие предков? object - это класс

object - и есть базовый класс, предок. По отношению к бизнес логике - вообще ни о чем.

Корявость тут в абсолютно бессмысленной иерархии и в абсолютно бессмысленных именах классов.

И такого - полно. Люди не идеальны: полно дураков, лодырей и прочих фошиздов. А, может, и оправдано - ты же не знаешь конкретных бизнес-процессов и эволюции проекта. Короче, так исторически сложилось.
И, в приведенном мной примере, префикс в виде имени класса не оправдан.

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

А в результате что-то где-то там может поламаться.

Ничего там ломаться не должно. Строковое представление объекта более конкретного класса всегда должно включать данные объекта более абстрактного класса плюс идентификатор типа. Т.е. из строкового представления можно всегда создать объект конкретного класса, принадлежащего к определённой иерархии. Если это не так, то toString() реализован неправильно и абсолютно бесполезен.

object - и есть базовый класс, предок. По отношению к бизнес логике - вообще ни о чем.

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

И такого - полно. Люди не идеальны: полно дураков, лодырей и прочих фошиздов.

Ну так помоги им, оформи свой код так, чтобы было понятно. Зачем эти бахвальства владением премудростями и без того никому не понятного языка?

И, в приведенном мной примере, префикс в виде имени класса не оправдан.

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

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

Строковое представление объекта более конкретного класса всегда должно включать данные

Всегда найдется тот или те, кто сделает неправильно. И префиксы от этого не защитят. И очевидность такого кода - сомнительная.
Впрочем, ладно. Просто следует держать в голове, что поведение baz-bar может изменяться не там, где ты ожидаешь, основываясь на префиксе. А для себя лучше опереировать пространствами имен.

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

Всегда найдется тот или те, кто сделает неправильно. И префиксы от этого не защитят.

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

Впрочем, ладно. Просто следует держать в голове, что поведение baz-bar может изменяться не там, где ты ожидаешь, основываясь на префиксе.

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

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

Префиксы решат твою первую проблему - упорядочат имена в пакете.

Тут да. Если оформлять код, то так. Хотя, встроенные функции такого не придерживаются. Тут den73 прав про теги и т.д. Короче, жаль, что в былые времена такого не придерживались и гайдлайнов как бы тоже не выработали.

Тогда и не придётся держать в голове возможность неопределённого поведения

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

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

Там нет вложенных S-выражений различных уровней вложенности.

Зато там есть {}-выражения различных уровней вложенности. В чём-то принципиальное различие?

Поэтому код там выглядит более просто и упорядочено.

Гм... вопрос вкуса.

И то, что означает obj.name(), скорее всего, будет выглядеть очевидным.

Информация в obj.name() и (name obj) абсолютно совпадает.

В Лиспе при использовании убогих (или коротких) имён этот контекст держать в голове сложно.

Чем контекст в Лиспе отличается от контекста в Джаве?

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

Ты думаешь, программисты не могут запомнить тип переменной в коде и им надо его напоминать при вызове каждого метода? А переменные тоже именуешь с именем типа?

Как раз проще прочитать что-то вроде (+ (* (x s) (x s)) (* (y s) (y s))), чем (+ (* (coordinates-x s) (coordinates-x s)) (* (coordinates-y s) (coordinates-y s)))

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

Зато там есть {}-выражения различных уровней вложенности. В чём-то принципиальное различие?

В наглядном синтаксисе. Ненаглядный синтаксис Лиспа - главная причина его фейла.

Информация в obj.name() и (name obj) абсолютно совпадает.

В Java/C++ видно тип объекта в месте определения переменной и, как правило, это место находится рядом. В Лиспе ничего не видно - привет динамическая типизация. Поэтому глупо писать программу на Лиспе по аналогии с Java (в стиле (name obj)) - результат будет совершенно не читаемым для других программистов.

Чем контекст в Лиспе отличается от контекста в Джаве?

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

Ты думаешь, программисты не могут запомнить тип переменной в коде и им надо его напоминать при вызове каждого метода? А переменные тоже именуешь с именем типа?

Я когда пишу код, то думаю о других программистах. В цепепе, например, я предпочту такой код:

void f()
{
  const auto a = foo();
  const auto b = bar();
  const auto c = baz(a, b);
  // ...
}

вот такому:

void f()
{
  const auto c = baz(foo(), bar());
  // ...
}

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

Как раз проще прочитать что-то вроде (+ (* (x s) (x s)) (* (y s) (y s))), чем (+ (* (coordinates-x s) (coordinates-x s)) (* (coordinates-y s) (coordinates-y s)))

Второй вариант предпочтительней.

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

потому что последний проще в сопровождении и отладке.

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

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

В Java/C++ видно тип объекта в месте определения переменной и, как правило, это место находится рядом.

Чем

template <typename T>
void printname(T obj) { print(obj.name()); } 

отличается от лиспового

(defun printname (obj) (print (name obj)))
?

А наличие auto, как правило, и в месте объявления не даёт узнать тип переменной. В лиспе наличие declare также часто позволяет узнать тип переменной.

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

Так делают все опытные лисперы (привет Эдмунду Вайтсу)

Смотрю его книгу: https://github.com/clojurians-org/lisp-ebook/blob/master/Common Lisp Recipes(...

На страницах 53-54 описана структура node с методами left-child и right-child, на странице 57 класс queue с методами enqueue и dequeue и нигде у него не сказано, что надо добавлять префикс класса к методу.

Так что не надо своё мнение приписывать известным людям.

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

Чем
отличается от лиспового

Хрен редьки не слаще. В обоих случаях какой-то сферический код в вакууме.

А наличие auto, как правило, и в месте объявления не даёт узнать тип переменной.

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

void foo()
{
  auto p = make_unique<Person>();
}

В лиспе наличие declare также часто позволяет узнать тип переменной.

Этим мало кто пользуется.

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