Глубинный котер
94 subscribers
60 photos
7 videos
4 files
70 links
Download Telegram
Разбирая реализации MCP-серверов в разных экосистемах, я был впечатлён разницей в объёме кода при схожих характеристиках (статическая типизация, асинхронность, GC). Go-версия занимает 400+ строк, тогда как Kotlin-решение укладывается в 100 строк— и всё благодаря выразительности языка и богатой стандартной библиотеке Kotlin.

В Go-реализации все механизмы синхронизации прописаны явно:


type stdioSession struct {
pendingRequests map[int64]chan *samplingResponse // Хэш-таблица запросов
pendingMu sync.RWMutex // Мьютекс для доступа
requestID atomic.Int64 // Атомарный счётчик
}


Каждое изменение shared-состояния требует явной блокировки:


s.pendingMu.Lock()
s.pendingRequests[id] = responseChan
s.pendingMu.Unlock()


В Kotlin аналогичная функциональность достигается проще:


private val pendingRequests = ConcurrentHashMap<Long, Channel<SamplingResponse>>()
private val requestId = AtomicLong(0)


Доступ к ConcurrentHashMap автоматически потокобезопасен:


pendingRequests[id] = responseChannel // Не нужны явные блокировки


Запуск асинхронной задачи в Go с коммуникацией через канал:


go func() {
response := s.server.HandleMessage(ctx, rawMessage)
if err := s.writeResponse(response, writer); err != nil {
s.errLogger.Printf("Error: %v", err)
}
}()


Эквивалентная логика в Kotlin более лаконична:


scope.launch {
val response = server.handleMessage(rawMessage)
writeChannel.send(response) // Потокобезопасная отправка
}


Исходники:

- на Kotlin
- на Go
4🤔2
После coq продолжаю знакомиться с чудесами Computer Science. На этот раз познакомился со статьей Checking Polynomial Time Complexity with Types за авторством Patrick Baillot, которая посвящена верификации полиномиальной временной сложности программ с помощью систем типов.

В статье используется модифицированная система типов на основе Light Affine Logic, где модальности ограничивают дублирование данных и глубину рекурсии. Это гарантирует, что типизированные программы выполняются за полиномиальное время.

Примеры модельностей:

- ! (экспоненциал) контролирует, сколько раз значение может быть использовано.
- § (мягкий экспоненциал) ограничивает глубину рекурсивных вызовов.

Например, рекурсивные функции проверяются на соответствие линейным и стратифицированным правилам, исключая неконтролируемый рост вычислений. Тип N → §N для функции означает, что каждый рекурсивный вызов уменьшает «глубину» страта, обеспечивая полиномиальную остановку.

В коде такой подход можно проиллюстрировать так:


-- Тип с модальностью §, ограничивающей рекурсию
data § a = § a

-- Функция сложения с контролем глубины
add :: §Int -> §Int -> §Int
add (§x) (§y) = §(x + y)

-- Рекурсивная функция с ограничением через типы
factorial :: Int -> §Int
factorial 0 = §1
factorial n = §(n * un§ (factorial (n-1))) -- un§ "снимает" модальность


Здесь модальность § гарантирует, что глубина рекурсии factorial ограничена, а время выполнения остается полиномиальным.
2
Болото унаследованных интеграций


Смешно до боли 💀
Please open Telegram to view this post
VIEW IN TELEGRAM
😁51
Представьте ситуацию: вы пишете на языке, где нельзя объявлять функции с именами (чистое лямбда-исчисление), или работаете с ограничениями вроде тех, что есть в Haskell. Как сделать рекурсивную функцию?

Обычный факториал выглядит просто:


fun factorial(n: Int): Int = if (n <= 1) 1 else n * factorial(n - 1)


Но что если мы не можем ссылаться на factorial внутри его же определения? Вот тут-то и нужен Y-комбинатор, который позволяет функции вызывать саму себя, не имея имени:

1. Мы передаём функцию в саму себя как аргумент
2. Y-комбинатор организует "бесконечную" подачу функции в саму себя
3. Рекурсия работает, хотя явного вызова по имени нет

Вот как я реализовал это в Kotlin (люблю его за выразительность):


typealias Func<A> = (A) -> A

fun <A> yCombinator(f: (Func<A>) -> Func<A>): Func<A> {
return { x -> f(yCombinator(f))(x) } // Магия!
}

val factorial: Func<Int> = yCombinator { recurse -> {
n -> if (n <= 1) 1 else n * recurse(n - 1)
} // Лямбда внутри лямбды
}

println(factorial(5)) // 120


Ключевые идеи реализации:

1. yCombinator принимает функцию, которая хочет быть рекурсивной
2. Он подсовывает ей саму себя в качестве аргумента recurse
3. Когда функция вызывает recurse — она неявно вызывает саму себя

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

Это как обнаружить, что ваш любимый конструктор Lego на самом деле состоит из ещё более фундаментальных деталей 😳
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔62👀1
На недавнем DDD Europe выступил с докладом Gregor Hohpe — автор книги Enterprise Integration Patterns (да, той самой!). Доклад посвящен инженерии платформ, и красной нитью в нём проходит мысль simple ≠ easy. Ниже приведены основные идеи доклада.

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

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

Ошибка Cadillac Cimarron: Компания попыталась выпустить новый автомобиль на базе существующего, лишь дополнив его несколькими функциями. Однако игнорировала необходимость построения полноценной платформы. Результат оказался провальным, так как машина не обладала уникальными характеристиками, выделявшими её среди конкурентов.

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

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

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

Абстрагирование может приводить к ошибочному восприятию реальной проблемы. Основная цель абстракций — выражение сложности, а не упрощение. Ориентация на специфику предметной области делает систему интуитивно понятной. Разработчики должны предлагать уникальные сервисы, основываясь на своей экспертизе в предметной области.
4
В то время как в компаниях уже на проде используют Kotlin MP, Apple недавно выложили Swift SDK под Android — можно писать нативное приложение сразу под две платформы.

Похоже, что идея KMP стрельнула и для улучшения конкурентоспособности экосистемы Swift слепили SDK под Android. Из интересного, что используется интероп Swift-Java (третий по счёту в Swift?)

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

С другой стороны, на Kotlin много бэкенда, да и KMP на проде уже крутится.

Наблюдаем за этой гонкой 🍿
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Because request parameters and responses are handled inside route lambdas, the plugin cannot infer detailed request/response schemas automatically. To generate a complete and useful specification, you can use annotations.


Cтыдно, очень стыдно
🌚4
Антропик посягнул на святое 🤱

Буквально сегодня выложили ролик с демонстрацией того, как можно использовать Клод для модернизации старого доброго кода на Коболе. В качестве примера они взяли репозиторий AWS Mainframe Modernization (!!!) — там есть система управления кредитными картами.

А я уже хотел купить курс по Коболу и попасть в золотую КОБОЛу 🙈
Please open Telegram to view this post
VIEW IN TELEGRAM
4🤣1
Только ленивый не позлорадствовал над вчерашним падением Cloudflare, вызванным небезопасным вызовом unwrap у монады Result. Но этот инцидент, как и многие до него, — ещё одно подтверждение тому, что первичны процессы, а технологии как таковые вторичны 🥲
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Диалоги Платона ATDD
5
На дворе почти 26 год, а агент от ЖБ отказывается работать с одним из языков ООН
😁4
Знакомая ситуация? Моделируем пользователя, добавляем поле email и флаг isEmailConfirmed. Кажется логичным, но на практике рождает хрупкий код:


class Email(val value: String)

data class User(
val email: Email,
val isEmailConfirmed: Boolean
)


У такого подхода есть ряд проблем:

- Тип Email ничего не знает о своём статусе
- Флаг живёт отдельно, можно случайно отправить письмо на неподтверждённый адрес
- Валидация размазывается по коду

Решение. Вместо флага — два разных типа, выражающие жизненный цикл:


sealed class Email

class Confirmed(val value: String) : Email()
class Unconfirmed(val value: String) : Email()

data class User(val email: Email) // email может быть в двух состояниях


Компилятор теперь заставляет обрабатывать все случаи:


fun sendNewsletter(email: Email): Result = when(email) {
is Confirmed -> sendTo(email)
is Unconfirmed -> tryConfirm(email)
// Компилятор проверит, что все ветки покрыты
}


Система типов становится нашим надёжным союзником: она позволяет явно описать бизнес-правила предметной области непосредственно в структуре данных.

Например, при добавлении нового статуса (скажем, Banned) вы не столкнётесь с ошибками в процессе выполнения. Вместо этого компилятор укажет все места в коде, которые нужно обновить, — и позаботится о согласованности изменений.

Это ещё один приемчик для снижения когнитивной нагрузки на разработчика, что, безусловно, повышает эффективность нашей работы 👽
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥63👍2👎1