All streams
Search
Write a publication
Pull to refresh
8
0
Send message

в статье и про ФП ничего не написано. Написаны выдумки автора, использующие те же слова, но с каким-то безумным (и бездумно переусложненным) смыслом.

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

Опять же, нет. В ФП достаточно много перегруженных терминов.


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


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


List — это монада. Цепочки вычислений над функтором List.

После прочтения этой статьи я уже нихера не понимаю.

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


Так тип категория или нет?

Нет. То, что вы написали до того, верно.


Мне это (либо очень очевидное, либо очень неправильное) понимание

Это очевидное и правильное понимание. Вы правильно поняли смысл функторов. Вы лифтите функцию int -> string, чтобы она работала с Maybe int -> Maybe string. Вы можете записать fmap как fmap (a -> b) -> (f a -> f b).


Аппликативный функтор. А вот тут непонятно.

Обычный функтор берет чистую функцию и преобразовывает ее так, чтобы она принимала обернутые значения.


Аппликативный функтор берет обернутую функцию и преобразовывает ее так, чтобы она принимала обернутые значения. Т.е. аппликативный функтор лифтит функции, не разворачивая их.


А вот почему монада — контейнер?

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


Но с таким же успехом и функтор — контейнер, нет?

Если рассматривать неверные аналогии, то функтор куда ближе к контейнеру, чем монада.

В статье столько ошибок, что даже и не знаешь, с чего начать.


Категории — это не типы. Это, если уж пытаться натянуть сову на глобус, системы типов. Или не типов. В категории Set объектами (базовыми элементами категорий) являются множества разных элементов, которые в каком-то смысле могут быть типами (множество всех целых чисел, множество всех строк и т.д.). В категории Hask (которая не совсем категория, но это вопрос практического свойства) базовыми элементами являются типы хаскеля. Конечно, можно создать категорию Double, где объектами будут литералы, а морфизмы будут задавать преобразования между этими литералами, но что это даст?


Эндоморфизмы — это не "тип в себя", это морфизм в пределах одной категории. Преобразование из string в int вполне себе эндоморфизм. Преобразование из Maybe в Maybe тоже эндоморфизм.

Монады — это не контейнеры. Как пример, есть монады IO и Reader не являются "контейнерами", в которые можно что-то положить. Монады — это абстракция вычислений в контексте. Если уж хотите использовать аналогии, то ближайшее к монаде, что можно придумать из "обычного" мира — это Promise. Вы связываете промисы в цепочки вычислений, и каждое вычисление находится в контексте того, что оно случится когда-то. И, как забавное следствие, вы не можете просто взять и избавиться от Promise, если уж вы начали его использовать.


На самом деле Promise не монада, но вполне могла бы ей быть.


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


  • берет Promise<int>,
  • неявно извлекает int,
  • применяет к нему intToStr(x: int): string (чистая функция)
  • оборачивает в Promise
  • возвращает Promise<string>.

Если бы это была монада, то это выглядело бы так:


  • берет Promise<int>,
  • неявно извлекает int,
  • применяет к нему intToStr(x: int): Promise<string> (монадическая функция, может делать внутри async/await)
  • возвращает Promise<string>

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


Т.е., опять же, используя промисы как пример, у нас есть Promise<(x: int) -> string> и Promise<int>. Аппликативный функтор дает возможность применить Promise<(x: int) -> string> к Promise<int> и получить Promise<string>.


Что касается использования List как примера функторов и монад, и вытекающие отсюда неправильные представления о том, что это контейнеры, а монады и функторы работают с контейнерами. Нет. List не является массивом, List является абстракцией недетерминированных вычислений. То, что List можно вычислить, представив как массив, это лишь "ложный друг переводчика". Значение List[1,2,2,3] на самом деле описывает недетерминированное значение, которое с вероятностью 25% равно 1, с вероятностью 25% равно 3 и с вероятностью 50% равно 2.


Суммируя: вы ничего не поняли, но уже пошли объяснять остальным.

Если вдруг решите оптимизировать по-настоящему, то посмотрите на эту статью — https://habr.com/ru/post/180135/

Да, конечно. Разница между ними, как вы заметили, в преднамеренности и, как следствие, в наличии секретной компоненты для противодействия преднамеренности.


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


Для обнаружения таких случайных модификаций достаточно самых простых мер — раньше, например, для очень небольших пакетов (7-10 бит) использовались биты четности (суммирование по полиному x+1). Для данных большего размера хорошо подходят CRC32 и CRC64, которые тоже суммирование по полиному, просто большего порядка.


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


Почему CRC и коды Рида-Соломона нельзя использовать для контроля целостности? Активный атакующий, цель которого — выдать свои данные за данные изначального отправителя, уже не вписывается в модель случайного шума, меняющего биты, т.к. его изменения зависят от положения в контенте и от значений конкретных бит соседей (например, атакующий ищет "authorized": false, и хочет поменять это на "authorized": true ,). Почему CRC и RS не подходят для защиты от таких изменений? Очевидно, что так как эти алгоритмы не содержат в себе секретной компоненты, атакующий может просто посчитать CRC и RS заново. Даже если используется нестандартный полином, не представляет проблемы его отреверсить при достаточно большом количестве наблюдаемого открытого текста — ни CRC, ни RS не являются криптографически безопасными примитивами, и не обеспечивают защиту от преднамеренной модификации.


Для контроля же целостности можно использовать HMAC — семейство функций с секретным симметричным ключом, основанные на криптографических хэш-функциях. Пересчитать HMAC повторно, даже зная низлежащую функцию (SHA3-256, например), не зная ключа, невозможно, а HMAC безопасен ровно настолько, насколько безопасна низлежащая функция. Поэтому если низлежащая функция (SHA1) уязвима к второй preimage атаке (для оригинального текста x найти такой x', который sha1(x) == sha1(x') при x != x'), то и HMAC-подпись небезопасна. Для CRC же поиск такого поддельного текста тривиален.


Как вариант, может использоваться асимметричная подпись хэша. В таком случае атакующий может пересчитать хэш (его алгоритм известен), но не сможет подделать для него правильную подпись, не восстановив секретную часть ключа.

А что сделают недовольные, напишут гневный твит? Нормально все будет, вот увидите, когда границы таки закроют :)

Ну как раз тут речь и идет о том, что для SHA1 правило "один хэш — один контент" больше не выполняется? Какая разница, преднамеренно или нет.


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

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


Если же вам нужна хэш функция для балансинга, или, например, для построения хэшмапа, то подойдет и SHA1, и RC4-Hash.


Если же вам вообще нужна контрольная сумма для противодействия непреднамеренному искажению (не для контроля целостности) — то и CRC32 хороший вариант.


Все зависит от того, зачем это вам нужно и какие свойства вы ожидаете получить.

Ну, тогда используйте identity function :) Правда, для файла в 10 гб идентификатор будет все те же 10 гб, потому что это и будет сам файл, но что стоят такие маленькие неудобства?

Например, BLAKE3. Быстрая, длинная и надежная.

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

А каким образом это влияет на написанное? Глупость написал не автор deno, а автор этого поста. И то, что у автора ноды получилось в первый раз сделать что-то хорошее, не дает гарантий того, что у него это получится во второй раз. Вот, deno тому ярким примером — хуже только php.

Information

Rating
Does not participate
Registered
Activity

Specialization

Software Architect