Предлагаю читателям "Хабрахабра" перевод (возможно лучшей) статьи Kyle Kingsbury, a.k.a "Aphyr".


Давным-давно, на Шпицбергене, когда ты была юной ведьмочкой всего сорока трех лет, мама взяла в свои ладони твои еще не покрытые шрамами руки, и сказала:


Видрун, зачатая от морских ветров в верхушках елей,
Видрун, зелень моих ветвей, радость и ноша моих дней,
Видрун, всех вдохновенней и умней, да станет мудрость нашего клана твоей:
Никогда не читай Hacker News

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


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


"Прежде чем мы начнем, могу я… ответить на ваши вопросы о компании?"


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


"Так, эм… Не могли бы вы, скажем, рассказать немого о себе?"


Он не читал твое резюме. Никто этого не может.


"Зимой, над закованными льдами фьордами", начинаешь ты, "стоит скала, как пепельной шапкой покрытая призраками ледников--"


"Знаете!". Он тебя прерывает. Это прекрасная история, но может удастся рассказать ее позже. "Как насчет немножко попрограммировать вместе? Просто несколько основных упражнений, чтобы я понял как вы думаете?!"


"Милое предложение, Тим".


"Окей, отлично". Тим вроде уверен в том, что удалось вернулся в колею интервью. "Давайте откроем редактор. Эм… не хотите ли присесть?"


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


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


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


"Начнем с linked list?" — улыбаешься ты уверяюще.


"Да", говорит Тим, "но… эм… просто обычный linked list, пожалуйста. Я в курсе, что ты, ну, занимаешься функциональным программированием, но у нас тут более прагматичная контора. Создаем настоящий софт. Нам нужно что-нибудь попрактичней".


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


(ns cycle-detector.core
  (:require [clojure.java.io :as io])
  (:import (java.io DataOutputStream
                    ByteArrayOutputStream)))

"Нам, э, не нужен тут IO. Просто список в памяти".


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


; A simple mutable linked list
(deftype MutableLinkedList [value next]
  clojure.lang.Seqable
  (seq [_]
    (lazy-seq (cons value (seq @next)))))

(defn node [value]
  (MutableLinkedList. value (atom nil)))

(defn link! [node next]
  (reset! (.next node) next)
  next)

"Это… немного не то, чего я ожидал" — говорит Тим. "Нет, нет, хорошо! Прямолинейно и просто. Я просто, ну знаете, в интернете говорят что вы...". Он стихает, и извиняющеся глядит на тебя.


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


(gen-class
  :name     cycle_detector.core.ArbClassLoader
  :extends  ClassLoader
  :state    state
  :init     class-loader-init
  :constructors {[ClassLoader String bytes]
                 [ClassLoader]}
  :exposes-methods {defineClass superDefineClass
                    resolveClass superResolveClass}
  :prefix   "-"
  :main     false)

"Простите," говорит Тим из-за плеча, "я не эксперт в Clojure. Зачем это?"


"Просто бойлерплейт. Не беспокойтесь". Тим выглядит еще более обеспокоенным. "Мы все время так делаем".


(defn -class-loader-init
  [^ClassLoader class-loader ^String class-name ^bytes bytecode]
  [[class-loader] {:class-name class-name
                   :bytecode   bytecode}])

(defn -loadClass
  ([this ^String class-name]
   (-loadClass this class-name true))

  ([this ^String class-name resolve?]
   (if (= class-name (:class-name (.state this)))
     (let [bytecode (:bytecode (.state this))
           c (.superDefineClass this
                                class-name
                                bytecode
                                (int 0)
                                (int (alength bytecode)))]
       (when resolve? (.superResolveClass this c))
       c)
     (.loadClass (.getParent this) class-name))))

(defn class-loader
  [^String class-name ^bytes bytecode]
  (cycle_detector.core.ArbClassLoader. (.getClassLoader MutableLinkedList)
                                       class-name
                                       bytecode))

Цвета начинают убегать с лица Тима. Видать пришла зима и он решил полинять.


(defn run-bytecode
  [bytecode class-name method-name signature & args]
  (-> class-name
      (class-loader bytecode)
      (.loadClass class-name)
      (.getMethod method-name (into-array Class signature))
      (.invoke nil (object-array args))))

"Clojure — динамический язык", охотно объясняешь ты, "Так что когда мы гоняем туда и сюда классы Java, обычно происходит некоторая рефлексия".


"Похоже на то, что ты… написала classloader исключительно для того, чтобы вернуть массив одиночных байт для определенного класса? Это… это нормально?"


"Да" — настаиваешь ты, грозно сверкая глазами.


"А почему просто не написать алгоритм на Clojure?"


"Производительность" — совершенно честно объясняешь ты. "Так как постоянные проверки будут проводиться в тесном внутреннем цикле, то мы совершенно не хотим писать их на столь высокоуровневом языке".


"Х-хорошо" — заикается Тим "Так ны напишешь проверку на цикличность на Java? И будешь вызывать ее из Clojure?"


"Что-то типа того".


(def racer
  (->> [0xca 0xfe 0xba 0xbe

"А это что такое?"


"Магические числа". Ты же ведьма, в конце концов. "Каждый класс начинается с девушки в кафе"
[ в оригинале — babe in a cafe, см 0xca 0xfe 0xba 0xbe ]


"Чего?!"


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


"Прости, что?"


Если честно, ты никогда не понимала замысел Sun в этой истории, или почему спецификация Java Virtual Machine, обычно столь прозаичная, низвергается в чувственную рапсодию на многие строфы в секции 4.1


        0x00 0x00                 ; Minor
        0x00 0x31                 ; Major

"Мы используем версию 49, поскольку она не требует stack maps, что упрощает задачу. Теперь нам нужно число констант"


Вспомни будущее. Это обычный трюк для заклинателей протоколов, многие из которых жили как Мерлин, записывая константы и размеры буферов еще до (после) того как они написали (не написали) сами буферы.


Вспомни, что 22 хватит. Запиши.


        0x00 0x17                 ; 22 constants

"Извиняюсь" — моргает Тим "Но ведь 0x17 это в десятичной системе 23, а не 22?"


“Og en,” — декламируешь ты напевно — “Til javanissen!”


"Прошу прощения?"


"Джаваниски. Ты наверняка о них слышал! Они мелкий волшебный народ — навроде гномов — который живет в каждой JVM. Если не выделишь им одну дополнительную константу, они пакостят сегфолтами. Но обрадуй джаваниска, и твои мьютексы будут работать как по маслу".


Это история из детства. Ты вспоминаешь маму, бубнящую офсеты и помешивающую варево в котле. “To byter for bufferen anvise / og ekstra en til javanisse.” Это радостное воспоминание, и ты в нем несколько забываешься, пока Тим не напоминает о себе покашливанием.


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


        0x01 0x00 0x10            ; 1: A UTF-8 string of 16 bytes
        (.getBytes "java/lang/Object")
        0x07 0x00 0x01            ; 2: The Object class

        0x01 0x00 0x19            ; 3: UTF-8 string of 25 bytes
        (.getBytes "cycle_detector/core/Racer")
        0x07 0x00 0x03            ; 4: Our class

Мы возьмем Iterable и вызовем .iterator(), а значит нам понадобится:


        0x01 0x00 0x12            ; 5: UTF-8 string of 18 bytes
        (.getBytes "java/lang/Iterable")
        0x07 0x00 0x05            ; 6: Iterable

        0x01 0x00 0x08            ; 7: UTF-8 string of 8 bytes
        (.getBytes "iterator")
        0x01 0x00 0x16            ; 8: UTF-8 string of 22 bytes
        (.getBytes "()Ljava/util/Iterator;")
        0x0c 0x00 0x07 0x00 0x08  ; 9: Name and type info (7, 8)
        0x0b 0x00 0x06 0x00 0x09  ; 10: Interface methodref for Iterable.iterator()

"И для этого итератора нам понадобятся hasNext и Next()...". Теперь байты пойдут быстрее. Это намного лучше, чем древнескандинавская гексография, где четные и нечетные цифры имели в составе одну и ту же руну.


        0x01 0x00 0x12            ; 11: UTF-8 string of 18 bytes
        (.getBytes "java/util/Iterator")
        0x07 0x00 0x0b            ; 12: Iterator

        0x01 0x00 0x07            ; 13: UTF-8 string of 7 bytes
        (.getBytes "hasNext")
        0x01 0x00 0x03            ; 14: UTF-8 string of 3 bytes
        (.getBytes "()Z")
        0x0c 0x00 0x0d 0x00 0x0e  ; 15: Name and type info for .hasNext()
        0x0b 0x00 0x0c 0x00 0x0f  ; 16: Interface methodref: Iterator.hasNext()

        0x01 0x00 0x04            ; 17: UTF-8 string of 4 bytes
        (.getBytes "next")
        0x01 0x00 0x14            ; 18: UTF-8 string of 20 bytes
        (.getBytes "()Ljava/lang/Object;")
        0x0c 0x00 0x11 0x00 0x12  ; 19: Name and type info for .next()
        0x0b 0x00 0x0c 0x00 0x13  ; 20: Iterator.next()

Тим онемел.


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


        0x01 0x00 0x04            ; 21: UTF-8 string of 4 bytes
        (.getBytes "Code")        ; String for code attributes

Наконец, наша сигнатура. Берем Iterable и возвращаем булево значение.


        0x01 0x00 0x17            ; 22: UTF-8 string of 23 bytes
        (.getBytes "(Ljava/lang/Iterable;)Z") ; Our arg signature

"Ну что ж". Надо хрустнуть костяшками и нанести древние печати.


        0x00 0x21                 ; Flags: public & super
        0x00 0x04                 ; Our class
        0x00 0x02                 ; Our superclass (Object)
        0x00 0x00                 ; No interfaces
        0x00 0x00                 ; No fields
        0x00 0x01                 ; One method

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


        0x00 0x09                 ; Flags: public & static
        0x00 0x15                 ; Method name (21, "Code")

"Имена методов должны начинаться в нижнем регистре", — утверждает Тим. Но голос его звучит так, как будто это был вопрос.


"Только по соглашению. Подойдет почти любая строка, а у нас уже есть одна в пуле констант".


        0x00 0x16                 ; Method signature (22)
        0x00 0x01                 ; One attribute

        ; Method attributes
        0x00 0x15                 ; Attribute name (21, "Code")
        0x00 0x00 0x00 0x48       ; + 2 2 4 bytecode-length 2 0 2 attribute-len
        0x00 0x02                 ; Maximum stack
        0x00 0x04                 ; Number of local variables

"Стой, стой, подожди" — Тим хватается за щепку во время шторма. "Всего четыре слота для переменных? Для аргументов плюс локальных?"


“Og to til javanissen!” — напоминаешь ты ему. Тим ловит ртом воздух, пока ты вспоминаешь, сколько именно инструкций написала.


        0x00 0x00 0x00 0x3c       ; Size of bytecode

Твой метод начинается с создания пары итераторов из единственного аргумента Iterable.


        0x2a ; aload_0 (take arg)

        0xb9 ; invokeinterface
        0x00
        0x0a ; .iterator()
        0x01 ; 1 arg
        0x00 ; unused

        0x4d ; astore_1  (store iterator)

        0x2a ; aload_0   (take arg)

        0xb9 ; invokeinterface
        0x00
        0x0a ; .iterator()
        0x01 ; 1 arg
        0x00 ; unused

        0x4e ; astore_0  (store iterator)

“Наверное тут нужен astore_2?” — спрашивает Тим, стараясь помочь. “Нулевая переменная содержит наш первый аргумент, ведь так?”


"Так было", соглашаешься ты, "Но больше он нам не понадобится".


"Но… они ведь даже не одного типа. Это… это нелегально".


"Если бы это было нелегальным" — терпеливо напоминаешь ты ему, "то Sun Microsystems сделала бы это невозможным".


Первая будет быстрым итератором. Ее имя будет Йорунн, ее ноги будут сильными от многих лет катания на лыжах. Она летит вперед мощными толчками.


        0x2d ; aload_1  take fast iterator

        0xb9 ; invokeinterface
        0x00
        0x10 ; hasnext
        0x01 ; 1 arg
        0x00 ; unused

        0x9a ; ifne
        0x00
        0x05 ; jump ahead 3 if we have a next element

        0x03 ; iconst_0
        0xac ; ireturn (return false)

        ; Move fast iterator forward by 1

        0x2d ; aload_1 (take fast iterator)

        0xb9 ; invokeinterface
        0x00
        0x14 ; .next()
        0x01 ; 1 arg
        0x00 ; unused

        0x57 ; discard element

        ; Ensure fast iterator has next

        0x2d ; aload_1 (take fast iterator)

        0xb9 ; invokeinterface
        0x00
        0x10 ; hasNext()
        0x01
        0x00

        0x9a ; ifne
        0x00
        0x05 ; jump forward by 3 if we have a next element

        0x03 ; iconst_0
        0xac ; ireturn

Бьёрн в нулевом регистре будет толстым и ленивым. Он не спеша топочет вперед, как его тезка. [Bjorn — медведь].


        0x2c ; aload_0 (take slow iterator)

        0xb9 ; invokeinterface
        0x00
        0x14 ; .next()
        0x01
        0x00

Йорунн, чтоб ее не догнали, делает еще рывок. Ее поступь тверда.


        0x2d ; aload_1 (take fast iterator)

        0xb9 ; invokeinterface
        0x00
        0x14 ; .next()
        0x01
        0x00

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


        0xa6 ; if_acmpne
        0xff
        0xd7 ; 0xffff + 1 - 41 instructions

        ; Return true

        0x04 ; iconst_1
        0xac ; ireturn

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


        ; End of bytecode

        0x00 0x00           ; No exceptions
        0x00 0x00           ; No attributes

        ; End of method

        0x00 0x00           ; No class attributes
        ]
       (map (fn [x]
              (if (instance? Long x)
                (unchecked-byte x)
                x)))))

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


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


(defn write-class!
  [^DataOutputStream ds class-data]
  (doseq [x class-data]
    (condp = (class x)
      nil     nil
      Long    (.writeLong ds x)
      Integer (.writeInt ds x)
      Short   (.writeShort ds x)
      Byte    (.writeByte ds x)
      (.write ds ^bytes x))))

(defn class-bytes
  [class-data]
  (let [baos (ByteArrayOutputStream.)]
    (with-open [ds (DataOutputStream. baos)]
      (write-class! ds class-data))
    (.toByteArray baos)))

"Baos рифмуется с хаосом" — вежливо информируешь ты Тима. Тот сконфуженно спрашивает про юнит-тесты. В приготовлениях ты сплетаешь историю про тропинку в лесу, которая смыкается сама с собой.


(deftest cycle-test
  (let [nodes (mapv node (range 5))
        list  (first nodes)]
    (reduce link! nodes)
    (link! (nth nodes 3) (nth nodes 1))

Прежде чем бросить заклятье, ты обращаешься к сторонам света, обозначенным шрамами на твоем запястье: H, J, K, L. Только J дает ответы таким как ты, но быть вежливой никогда не повредит.


(deftest cycle-test
  (let [cycle? (partial run-bytecode
                        (class-bytes racer)
                        "cycle_detector.core.Racer"
                        "Code"
                        [Iterable])]
    (is (boolean (cycle? (seq list))))
    (is (not (boolean (cycle? []))))
    (is (not (boolean (cycle? [1 2 3]))))))))

Ran 1 tests containing 3 assertions.
0 failures, 0 errors.

"Триста пятьдесят шесть байт" — заявляешь ты, и с удовольствием потягиваешься. "У Javac получилось бы примерно пятьсот восемьдесят. Мы конечно порядком сэкономили, пропустив stackmap и line mapping, но дополнительно мы сократили число переменных, и еще выбросили четыре лишних операции astore/aload. И естественно, поскольку класс никогда не инстанциируется, нам не надо генерировать метод <init>, или вызывать суперкласс".


Тим пялится на тебя с молчаливым интересом. Его кофта блестит от быстро тающего инея. Скорей всего ты нанята.


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

Поделиться с друзьями
-->

Комментарии (27)


  1. k12th
    18.04.2017 13:57
    +5

    Стильно.


    Но… писать на clojure, чтобы потом писать в JVM-байткодах?


  1. mrsantak
    18.04.2017 14:15
    +5

    Тот случай, когда смотришь на код и не понимаешь то ли он цикл в связном списке ищет, то ли Ктулху призывает :)


  1. AstarothAst
    18.04.2017 15:31
    +14

    Скорей всего ты нанята.

    На месте Тима я бы трижды подумал — такой вот гений сбежит с проекта, а тем, кто останется его «магию» придется поддерживать…


    1. questor
      18.04.2017 18:33
      -1

      Ну тут одно из двух: либо потом придёт кто-то и со словами "чё тут за хрень непонятная, ща я всё это перепишу" перепиливает на код с таким запахом… либо Тим сам возьмёт и своими руками наберёт толпу вчерашних троечников из пэтэу, а этому вот гению откажет. Вы возьмётесь решать, что из этих альтернатив лучше?


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


      1. Gugic
        18.04.2017 19:46
        +3

        Крупным организациям тяжело с талантами. С крепкими середняками-ремесленниками комфортнее и спокойнее. И другим работягам и менеджменту и бухгалтерии.


      1. tsvetkovpa
        18.04.2017 21:57
        +13

        Гению? Подозреваю, что этот «гений» ничего длиннее лабораторной не писал.

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

        В проектах в «крупных организациях» размер кода измеряется десятками и сотнями тысяч строк.

        Представьте сколько будет стоить простейший рефакторинг такого кода, пускай даже силами команды «гениев»


      1. sebres
        18.04.2017 22:56
        +3

        Гению откажет… Искать и удерживать.

        Ну вот не откажет, а через год-два "гения" собьет машиной, или он с Эвереста скувыркнётся, тем самым не "удержится". Как вам такая альтернатива?.. Оцените риски?


        Тут как-бы стоит не про гениальность соискателя поговорить… Дело вот в чем: написать jvm-ный байт-код (а то и в с десяток других, включая питоний или нативный, ака asm например) смогут возможно многие, и из моего окружения в том числе, но при этом:


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

        А так, да… как красивая история для бложика — возможно (т.е. написано-то и правда красиво, + понты, эго… все дела)…
        Но как "реальная" история из жизни, — упаси боже от таких гениев-коллег.


        1. avost
          19.04.2017 00:10
          -1

          Забыли ещё пункт — вздохнут, наступят на горло собственной песне и пойдут херачить тонны унылого абсолютно "поддерживаемого" (дебилы не понимают как работает тернарный оператор, поэтому пишем 6 строк кода вместо одной и пофиг, что метод перестал помещаться на один экран) говнокода…


          1. AlexTheLost
            19.04.2017 14:52

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


            1. avost
              19.04.2017 15:12
              -1

              Да, я про это и написал. Для дебилов не осиливших тернарный оператор его растягивают на 6 строк и теперь любой дебил сможет его "поддерживать". Потому что вменяемых от этого бойлерплейта, простите, тошнит-с.
              Нормальная минимальная еденица, позволяющая системе быть гибкой — метод (или его аналог). А что там внутри творится — если некто, называющий себя программистом не в состоянии разобраться, что делает компактный метод, то зря этот некто так себя называет.


    1. mrsantak
      18.04.2017 23:12
      +1

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


  1. olegchir
    18.04.2017 16:40
    -5

    Вот это да


  1. aml
    18.04.2017 21:37
    +6

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


  1. solver
    19.04.2017 00:06

    Мда…
    Люди, вы чего? Какие гении, продакшен код, преждевременная оптимизация и вот это вот все?
    Ну блин никто же не пишет такой продакшен код, это же очевидно рассказ про другое…


    1. AstarothAst
      20.04.2017 09:05
      +1

      Вы остановились на самом интересном месте — про какое же «другое» этот рассказ?


  1. madkite
    19.04.2017 03:11
    +11

    (is (not (boolean (cycle? []))))

    А зачем тут приведение к boolean? Оно лишнее и без него будет работать точно так же.
    А вот вторым параметром передать строку с описанием не помешало бы, особенно на интервью и особенно если интерьер поставил практичность на первое место.
    И вообще с тестом там какая-то лажа — два теста с одинаковым именем, первый не дописан, второй использует необъявленные переменные из локального скопа первого теста. В итоге это не компилябильно.


    :main false

    Оно по умолчанию false, можно убрать это из "бойлерплейт", чтобы не пугать интервьюера.


    (int (alength bytecode)))

    alength возвращает и так int, т.е. это приведение лишнее.


    Short (.writeShort ds x)

    Это явный копи-паст со stack overflow или подобного. Там только либо байты, либо массивы байтов. Т.о. этот супер-универсальный метод совсем ни к чему.


    Так что когда мы гоняем туда и сюда классы Java, обычно происходит некоторая рефлексия

    Получается, что девочка, зная на память все коды инструкций джавовского байткода, ни разу не открыла скопилированный компилятором clojure класс и не глянула, как оно работает. Java-методы там вызываются без всякого reflection-а. Т.е. можно было просто написать на своём любимом байт-коде класс и просто его вызвать из clojure. Без всего изврата с ClassLoader-ами.


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


    (let [cycle? (partial run-bytecode
    (class-bytes racer)
    "cycle_detector.core.Racer"
    "Code"
    [Iterable])]
    … )

    partial тут просто фиксирует аргументы для функции run-bytecode. Т.о. при каждом вызове функции будет создаваться экземпляр ClassLoader-а, создаваться класс на основе массивчика байтов… ну вы поняли.


  1. sotnikdv
    19.04.2017 11:03
    +1

    Это мнение от лица тимлида и архитектора
    Ну и нахрена мне в команде это чудо, если на выходе абсолютно неподдерживаемый и нечитаемый код.

    Даже если этот код ну просто офигительно оптимальный, правда в том месте, где мне лишние такты не мешают, а вот ошибки в логике могут стоить весьма дорого?

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

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

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

    Еще раз, я плачу деньги не за ЧСВ, не за знание языка, не за знание скандинавского языка и т.д. Я плачу деньги за решение моих проблем и достижение моих целей максимально эффективным и уместным (тактические и стратегические цели) способом.

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

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

    Как то так.


    1. k12th
      19.04.2017 11:05

      Скандинавского языка не существует:)


      1. sotnikdv
        19.04.2017 11:18
        +1

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

        Т.е. вместо «это какой то язык из группы скандинавских» говорят «это какой-то скандинавский язык» и т.д.


        1. k12th
          19.04.2017 11:20
          -1

          Еще говорят «в калидоре» и «ло?жить», да.


    1. sotnikdv
      19.04.2017 11:15
      +1

      Печально все это. Особенно утомило начинать каждое общение с командой, как лиду, начинать вступительной лекций «Почему мы делаем, то, что мы делаем».

      Хотя мозги прочищает и народ начинает спрашивать не «сколько комментариев расставлять», а какие цели заказчика и сколько код планируется поддерживать и развивать. И тогда уже сам принимает решения и по технологиям и по тестам и по качеству и по количеству док и комментов.

      Почему этого не дают вместе с чисто техническими вещами — для меня загадка. И сам учился и сам преподавал. И сижу на двух стульях, принципал и менеджер, а все равно загадка.


    1. Maccimo
      19.04.2017 20:17
      +1

      Я плачу деньги за решение моих проблем

      Тот, кто решает проблемы с отсутствием у вас чувства юмора, не справляется со своей задачей.
      Примите меры.


      1. AstarothAst
        20.04.2017 09:04

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


  1. AlexTheLost
    19.04.2017 14:55

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


    1. sebres
      19.04.2017 16:50
      +1

      А это художественное произведение и есть...


  1. alexeiz
    19.04.2017 19:48
    +1

    Чувак просто написал нужный код на Java, скомпилировал в bytecode, потом тупо скопировал bytecode в кложу, а всем сказал "смотрите, какой я крутой, шестнадцатиричный bytecode могу по памяти выдавать!"


  1. Maccimo
    19.04.2017 20:24
    +1

    Самое удивительное в этой статье — это комментаторы, делающие вид, что не осознают шуточность данной статьи.