Разбиране, сложност и мисъл

Последния ми пост предизвика повече емоции, отколкото бях приготвен да поема. Очаквах да подразня доста хора, които в последствие да ме заклеймят като краен фундаменталист и да престанат да ме занимават с наивните си разсъждения по ICQ-та и GTalk-ове. Обаче не стана точно така. Като го написах, очаквах да остане неразбран. Сега ще направя допълнение, което също очаквам да остане неразбрано. Ще го отбележа пак, към края на текста.

Имаше два пасажа код в него. Нека да ги опростя мъничко, за да не ми кажете, че се заяждам за детайли.

notify User.find(:all).select { |u| u.active? }.map(&:email)

Това ще наричаме функционална парадигма. В контраст, имаме императивна такава (името не е напълно подходящо, но става, а и не искам да усложняват текста излишно):

recepients = []
for user in User.find(:all)
  recepients << user.email if user.active?
end
notify recepients

Сега, много хора се мръщят на първия код и го наричат сложен. Аз пък твърдях, че множеството не стават за програмисти, а малцинството имат още да учат. И все пак, погледнете го – далеч по-кратък (може да бъде и по-кратък, впрочем), както и да го броите – почти два пъти по-малко token-и (11 срещу 19) и почти два пъти по-малко символи (60 срещу 108). Далеч по-малко променливи, няма цикли, няма условни оператори. Първият код все едно казва какво трябва да се направи, докато вторият – как да се направи. И предвид че определено не си говорим за оптимизации, в тоя случай доста повече ни интересува какво искаме да правим (причината), отколкото как (следствието). Но все пак, първият код бил по-сложен.

Сега, какво кара хората да разсъждават така? Нека да направя една малка аналогия. Отидете в театралната академия и намерете някоя сладка девойка там. Кажете ѝ (1) „ускорението е първа производна на скоростта на движение на тяло в модела на нютоновата механика“. Освен ако не е била много старателна в часовете по физика и математика, ще ви погледне странно и ще ви каже „моля?“. Това значи ли че е глупава? Не, значи че не е запозната с вашата терминология. Значи ли, че не може да разбере какво говорите? Не. Кажете ѝ (2) „ако нещо увеличава скоростта си, то има ускорение; ако нещо има непроменливо ускорение, то постоянно увеличава скоростта си“. Най-вероятно ще ви отговори „Уау! Благодаря ти! Имаш ли още очевидни факти да споделиш с мен или мога да отивам да репетирам?“.

И в двата случая искаме да кажем (горе-долу) едно и също. Но мисля че е очевидно – формулировката, подхода и термините зависят напълно от ситуацията, в която говорите. Просто не може да говорите за движение с не-физик и да използвате термини като „производна“ и „нютонова механика“. Но може да водите такъв разговор с нея. Обърнете внимание – в първия пример сте много по-точни, конкретни и ясни. Но тя има нужда от известен контекст, за да го разбере. И понеже ѝ липсва, единствения начин да споделите тази фундаментална истина е като опростите изразните си средства до по-прост, макар по-мръсен изказ. Което все още е ОК, понеже тя не е физик и няма претенции към физиката.

Но да се върнем към кода. Очевидно трябва да знаете какво правят select и map за да разберете горния код. Трябва да знаете и какво правят корутините в ruby и каква е парадигмата зад тях. Трябва и да имате известно усещане за цялата работа, понеже не е само формализми и празна риторика на по халба бира. Ако не знаете това, то не разбирате кода. А това, че не разбирате кода не значи, че той е сложен. Вие просто сте като моя художествен образ, която не знае термините и не може да зацепи колко по-ясно сте се изразили първия път.

Но да се върнем със ситуацията при моята героиня от театралната академия (понеже започна да става цветисто, може и скоро да ѝ измисля име). Сега, защо физиците не ползват втория начин на формулировка постоянно? Защото няма да стигнат далеч с него. А и дори да го направят, осъзнават нуждата от по-компактен и по-изразител език, който да не затормозява с излишен шум техните разговори. Това се нарича терминология. Отраканите физици имат нужда от тоя език, но не го набиват в лицето на целия свят, понеже знаят, че е излишен, когато не разговаряте за физика. А нашата Монислава (актрисата, нали) пък не си говори въобще за физика и няма нужда от тази терминология. И обърнете внимание, тя не се опитва да наложи своята не-физична терминология в света на физиците. И не би казала, че физиката трябва да се разбира от всеки.

Обаче точно това чувам от страшно много „програмисти“. Програмирането трябвало да бъде е основано на две три прости неща, които са еднакви навсякъде и трябва да се ползват само те. Така ако аз съм идвал от Java, пък ти от Perl сме щели да се разбираме. Много странно разсъждение. Бих казал че е далеч по-просто всички да научим един общ език и да го ползваме на пълния му потенциал. Но по някаква причина, сума ти „програмисти“ смятат че е далеч по-важно всеки да може да разбира какво става, отколкото лекотата с която си пишеш кода.

И ето тук опираме до нещо съществено. Много биха казали, че е по-лесно да ползваш ако, за всеки и списъци, отколкото да учиш нещо повече. И съответо е „по-лесно“ да ползваш „тея три прости неща“ вместо да пишеш map и select. Абсурд. Далеч по-лесно е да кажеш (1), отколкото (2) ако знаеш за какво говориш. Трябва силно да се напънеш за да изразиш нещо, което ти идва толкова просто на език, който не е естествен за него. Освен ако не си като Монислава, разбира се. Тогава ще ти е по-лесно да избягаш от терминие, понеже не си физик. Което е ОК, ако не си физик. Или е ако говориш на не-физицици. Обаче това е рядко – далеч по-често искаш да изследваш физиката. Или да правиш софтуер, а не да се обясняваш на непрограмисти.

Но абсурда в мисленето не свършва тук. Стига ме и до момента, в който инженер ми казва „програмирането трябва да се направи толкова лесно, че да го разбират всички“. Чували ли се оная история, където в някой американски щат искали да направят π да бъде равно на 3? Схващате ли защо това толкова много ми напомня на горната мисъл? Искате ли да продължим с това опростяване на света? Да направим математиката разбираема за всички – да махнем тригонометрията, да закръглим π надолу и да разкараме цялата тая сложност с корени и степени? Да приложим същото и във физиката, махайки целия математически апарат и да опростим законите на природата? И да основем музиката на три-четири тона – така не просто всички ще я разбират, но и всички ще могат да я пеят?

Опитвате се да набутате LAN кабела в USB порта. Е не става. Мъчете се колкото си щете. И няма сила на тоя свят, която ще ми помогне, да ви убедя че грешите. Ако не спрете за момент и не се запитате дали не правите феноменална глупост, цялата логика, риторика и дори здрав разум, които мога да събера няма да са ви достатъчни.

Малко отклонение. Колкото до въпросния инженер – силно ме боли, че мой близък приятел може да демонстрира толкова голямо ниво на не-мислене. Силно ме боли, че магистър в естествени науки може да говори такива глупости и да има такова инфантилно възприятие за света. Отказвам да си говоря с теб на каквито и да е програмистки теми, докато не разбереш че си просто една театрална студентка, която се опитва да говори да уличен български с физици за физика и да ги кара да си пренапишат теориите без термини или всякакви други „сложнотии“, които ти и колежките ти могат да възприемат. Това, че знаеш ефекта на ускорението в колата ти не те прави физик. По същите причини не си и програмист. Така че, моля те, забрави за тоя блог докато не излезеш от този (повтарям) инфантилен модел на разъждение, понеже предизвикваш у мен единствено тъга и яд. А не вярвам някой от нас да има нужда от това.

Но да се върна към темата и към по-спокойния тон. Ето наблюденията, които направих, обобщение.

  • Сложната материя печели от терминология. В един момент на физиците им е писнало да повтарят „промяната на скоростта с времето“ и са го нарекли „ускорение“. В един момент на програмистите им е писнало да повтарят „направи нов списък, итерирай този и добави всеки елемент в първия след тази трансформация“ и за го нарекли map. Тази терминология обогатява занаята, понеже опростява разговорите и ни позволява да си говорим по сложни неща.
  • Ако боравиш със сложна материя, печелиш от това да познаваш терминологията ѝ. Не можем да си говорим за физика без термини. Не може да си говорим за алгоритми, без формалния апарат за изчисление на сложност (голямо О и т.н.). Като програмираме, също трябва да знаем редица термини – if, for, списък и масив. Обаче нещата не свършват до там, по същия начин по който математиката не свършва с простите числа.
  • Ако не си физик, не се изказваш за физика. А ако не си програмист, не говориш за програмиране. Приемливо е да не разбираш нещо и да го избягваш по тези причини. Но ти не го разбираш, отказваш да го разбереш и на всичкото отгоре се обаждаш, че всички други трябва да не го ползват. Без никаква друга причина, освен че ти и въображаемите ти събратя по неразбиране им е много трудно, понеже ви мързи да мислите. Което е крайно нахално.

И най-вече – разширявайки терминологията си, ставаме по-добри в областта. Сигурно можеш да стигнеш доста далеч във физиката с „промяна на скорост с времето“. Но можеш да стигнеш доста по-далеч като си говориш за „ускорение“. Така е и в програмирането – можеш да стигнеш доста по-далеч, ако осъзнаваш функционалната парадигма. Както и if/for/списък ти дадоха много по-добри изразни средства от branch, jump и индексна адресация в assembler. Не, не ти е нужно. Нужни са ти само въздуха, храната и подслона. Другото е опционално.

И не, не мога да те убедя. Хората не сме логични същества. А и дори да бяхме, не виждам как мога да ти наложа заключенията до които аз съм стигнал. Можеш да седнеш мъничко, да помислиш, да експериментираш с идеите ми, да проследиш пътя до който съм стигнал до тези заключения, да видиш дали ти можеш да стигнеш до други и дали те няма да си противоречат с началните ти допускания. Можеш да стигнеш до други изводи, можеш да стигнеш и до същите. Но не мога нито да те убедя, нито да те принудя да мислиш.

И сега като един хубав strange loop към началото на статията. Обзалагам се, че ще остане напълно неразбрана. Точно поради горната причина – не можеш да убеждаваш хора, можеш само да им покажеш пътя, по който си стигнал до заключенията и да ги оставиш да го извървят и те самите. Но не можеш да ги накараш да мислят. А и аз не си давам зор да ги предразполагам в тоя блог. Но за да разбереш нещо, трябва да искаш да го разбереш. Обаче хората рядко искат да разбират света – искат единствено да не виждат противоречия в него, да не са гладни и да няма заплахи.

16 thoughts on “Разбиране, сложност и мисъл

  1. Съм с два въпроса не толкоз абстрактни още от предния пост: 1. Има ли причина да не е User.find(:all, :conditions => { :active => true }).map(&:email) ами се прави select { |u| u.active? } от всички юзъри? 2. Как така map(&:email) е същото като map{|u| u.email}..досега се рових в Programing Ruby и gotapi.com ама нищо не открих по въпроса?

  2. Добре де, трябва ли да се изпишеш чак толкова в защита на някавата (вероятно не съвсем пълна) реализация на функционално програмиране в Ruby? Който има идея какво е и с какво ще е полезно, ще си го ползва, който няма, точно с това си обяснение нищо няма да му кажеш и като цяло целия постинг смисъла му се губи.

    Ако трябва, кажи колко хубаво нещо е функционалното програмиране за определен клас задачи (и за оптимизиране при работа в/у няколко процесора например)…

    Колкото до това колко е добра идеята да се набият в един език няколко парадигми, на мен ми се вижда лоша идея, защото предизвиква програмистите да пишат глупости, ама това е тема за много по-подробно обяснение с примери (това не зависи и от езика, виждал съм какво ли не на SQL, C, C++, perl, php и shell, трябва да помисля кво още съм ровил)

  3. @qrasio:

    1. Сигурно има, но не е много смислена. Просто исках да илюстрирам селекта някъде. Очевидно е по-добре с conditions, щото отива в базата данни.
    2. Symbol.to_proc

    @maniax:

    Не защитавам функционалното програмиране, нито в Ruby, нито по принцип, нито говоря за смесване на парадигми (което намирам за добра идея, понеже положението с тях не е черно-бяло). Използвам думата „парадигма“ съвсем странично. И цялостната идея на ФП-то въобще не е във фокуса на поста ми.

    Не говоря за парадигми. Говоря за хора и тяхното отношение към програмирането.

  4. Ахам, мерси……станa ми ясно що не го намерих в Programing Ruby 🙂

  5. Aquaris. Същината на идеята ти е, че хората които не са готови да научат нещо различно, по-добре да работят като водопроводчици, защото там няма да съществува такава тягостна необходимост, да?

    Поне аз останах с това впечатление след 1-2 съсредоточени прочита и гласувам с две ръце в твоя полза за това.

    Иначе останалата част, това разделяне на КАКВО и КАК ми се струва много изкуствена. Езиците постоянно се развиват и затова всяко „КАК“ постепенно се превръща в „КАКВО“ и след време пак започва да изглежда като „КАК“.

    Сега… Ясно е, че човек няма да се върне 20 години назад, към наистина примитивното „КАК“, на което говори асемблер да речем. Обаче да се търси най-абстрактното налично в момента „КАКВО“ също не винаги е добър подход. Понякога са достатъчни (и даже препоръчителни) по-конвенционални средства.

    Поради тази причина аналогията ти между терминологията във физиката и езиците за програмиране не е много точна.

    Нека я допълня така – в един момент терминологията може да се превърне в нещо паразитно/самоцелно, ако не се използва внимателно и на място.

    Поне аз така мисля и затова мисля частично се съгласявам с теб :).

  6. Йес, брутално як пост 🙂 До Sennin – това, което казваш за развитието на езиците е вярно, но функционалното програмиране съвсем не е нов начин да казваш как (освен ако 1950та година не ти се струва скоро.) Аз вдигам и седемнадесетте си ръце и крака в подкрепа на казаното. Какво е много по-важно от как в 95% от случаите.

  7. Обаче да се търси най-абстрактното налично в момента „КАКВО“ също не винаги е добър подход. Понякога са достатъчни (и даже препоръчителни) по-конвенционални средства.

    Това е безумно вярно и напълно подкрепя тезата ми. Просто въпроса е къде чертаеш линията на тези „по-конвенционални“ средства. Аз включвам

    map
    и
    select
    в тях. А ти?

  8. Бтв, тъкмо вчера като се просвещавах, четох за Питонската им версия. В C/C++ няма такова нещо, но може да се постигне същия ефект с използване на указатели към функции. Та в тоя ред на мисли, не виждам защо да не ги пишем към конвенционалните средства :D. Просто са едно удобство, което е вградено в езика и е хубаво човек да се възползва.

  9. Не мисля че „какво“ е толкова по-важно от „как“ освен ако компютрите не са станали толкова мощни че да не ни интересува дали търсим оптимален път в граф чрез алгоритъм на Дейкстра или чрез генериране на всички пътища и пресмятане на стойността им. Функционалното програмиране е готина парадигма ама понякога кара програмистите да забравят какво точно предизвиква кода им „под капака“. Мнението както сигурно се досещате е естествено на C програмист:)

  10. Твърдата позиция — Аз, света и сметачите

  11. В случая сблъсъкът е между хората, които не умеят да ползват стандартните методи на обектите и тези, които умеят. Прочетох статиите по темата и стигнах до извода, че всичко се свежда до:

    „Ползвайте select и map, по дяволите, и sort, никой на днешно време не пише алгоритми за сортиране на масив, нали?“

    Правилно ли разбрах?

  12. Първият код не е сложен, просто е нечетим …

    Нечетимият код се подържа по-трудно и предполага по-голяма вероятност за грешки. Грешките водят най-често до загуба … на време(и много вероятно пари)

  13. @Obi_Wan:

    Не мисля че “какво” е толкова по-важно от “как”…

    А пък аз казах, че ако някой смята така, или има още да учи (в твоя случай) или трябва въобще да си сменя професията (в случаите на много останали хора). Изписах сума ти неща и частично тоя пост, специално да ти го покажа от още една гледна точка.

    Ще направя последен опит да го обясня по различен начин. След това отказвам да говорим с теб на тая тема по коментари и продължаваме на бира 😉 . Не си ползвал map и select, не можеш да се изказваш колко добре работят. Преди четири години и аз не ги бях ползвал, бях заклет C++-аджия и въобще не признавах нещо, което има garbage collector или не прави .exe. После обаче видях Java, след което Lisp и Ruby. Пробвах тяхните подходи и те ми помогнаха да видя, че мога да върша нещата много по-бързо и по-качествено със средствата, които дотогава бях отричал.

    Така че, разбери какво е map и select и пиши известно време с тях. Примерно четири-пет дена. Докато ги зацепиш и свикнеш да мислиш с тях естествено. След това се върни към средите без select/map и дай да си говорим тогава. Защото, казах ти го вече – извървял съм тоя път и няма никаква сила, която ще ми позволи да те убедя, че грешиш. Можеш само да пробваш сам и да видим докъде ще стигнеш. Може да стигнеш до други изводи, а може да стигнеш и до същите.

    И само обърни какво елегантно противоречие направи. Статията на raganwald, която цитира в предния пост, казва точно това – „какво е по-важно от как“ и че разликата от „как“ до „какво“ се нарича accidental complexity.

    @dzver:

    Не, не, никак даже. Свежда се до това какво намираме за сложно, какво намираме за разбираемо и какво намираме добра идея. И от какво се ръководим като вземаме тези решения. И по-скоро, първия пост опираше до „какво е по-важно от как и ако не го схващате – лошо“, а втория – „като не разбирате нещо, това не го прави сложно или нечетимо. и не може да правите такива заключения“.

    Иначе, да, map-а и select-а са добра идея, където имаш предвид map и select. И да, хубаво е да ползваш sort, вместо да си пишеш собствен, освен в ситуациите, в които не ти е много важно бързодействието.

    @Стойчо:

    Силно ти благодаря за този коментар. Блестящ е! По три причини. Първо, страхотно илюстрира тезата на този материал. Второ, страхотно илюстрира тезата на предния пост. Трето, понеже ясно си личи че не си прочел за какво иде реч, официално се квалифицираш като първия трол на моя малък, скромен блог. Честито!

  14. Това User.find.select прави ли автоматично генерирано query към база?

Вашият коментар

Вашият имейл адрес няма да бъде публикуван.