The Expression Problem and its solutions (2016)
Проблема выражений и её решения
Проблема выражений: нужно добавлять новые типы данных и новые операции без изменения старого кода.
В ООП-языках легко добавлять типы (наследование), но сложно — операции (менять интерфейс).
В функциональных языках наоборот: легко добавлять функции, сложно — варианты данных.
Пример на C++
Базовый класс Expr с двумя методами: Eval() и ToString().
Новый тип — просто новый класс-наследник.
Новая операция — правим базовый класс и все наследников, нарушая OCP.
Функциональный подход (Haskell)
Типы данных и функции разведены:
data Expr = Constant Double | BinaryPlus Expr Expr
eval (Constant x) = x
eval (BinaryPlus a b) = eval a + eval b
Добавить операцию легко: пишем новую функцию.
Добавить вариант Expr — правим сам тип и все функции, pattern-match’и которых его затрагивают.
Как быть
- Визитор (ООП) — двойная диспетчеризация, но код всё равно растёт.
- Мультиметоды (CLOS, Clojure) — выбор по типу всех аргументов, код не трогается.
- Type-class / протоколы (Haskell, Clojure) — «открытые» функции, реализуемые вне исходного модуля.
- Tagless-final / finally-tagless — выразить язык как набор операций, интерпретаторы добавляются без изменения AST.
Итог: ни один стиль не побеждает; выбираем язык и технику, которая даёт нужную сторону расширяемости.
Комментарии (69)
- Суть проблемы: нужно добавлять новые типы данных и новые операции без переписывания старого кода и без потери статической безопасности.
- Классический подход: O·T матрица — каждая операция реализуется для каждого типа; при росте O или T взрывается boilerplate.
- Языковые решения:
– трейты/impl в Rust, typeclass в Haskell, протоколы в Swift позволяют добавлять операции «сбоку»;
– enum (суммарные типы) упрощают добавление операций, но затрудняют новые варианты данных. - Продвинутые техники: tagless-final, object algebras, data types à la carte, multiple dispatch, visitor-pattern.
- Ограничения: «сиротское правило», отсутствие виртуальных extension-методов, необходимость заранее знать все комбинации при раздельной компиляции.
- Вывод: идеального языка нет; выбор зависит от того, что важнее — расширять типы или операции — и насколько нужна статическая типизация.
Forking Chrome to render in a terminal (2023)
-
Рисование
Терминал умеет только моноширинные символы и escape-последовательности. Используем нижний полублок▄, задавая цвет фона (верхний пиксель) и символа (нижний).fn print_pixels_pair(top, bottom, (x, y)) { println!("\x1b[{};{}H\x1b[48;2;{t}m\x1b[38;2;{b}m▄", y+1, x+1, t=top, b=bottom); } -
Текст
СоздаёмTextCaptureDeviceв Skia: перехватываемonDrawGlyphRunList, преобразуем glyph → Unicode, вызываем Rust-функциюdraw_text.
Добавляем очистку текста при заливке прямоугольников:if (paint.getStyle() == kFill_Style && paint.getAlphaf() == 1.0) clear_text(rect); -
Ввод
Читаем stdin, парсим escape-коды клавиш/мыши, передаём их в Chromium через DOM-события. -
Pipe-режим
carbonyl --pipeрисует в stdout, позволяя встраивать браузер в скрипты. -
Mojo
Заменяем GPU-процесс на заглушку, отключая лишние сервисы. -
Layout
Подгоняемdevice_scale_factorиviewportпод размер терминала, чтобы 1 px = ½ клетки. -
LoDPI
На 1×-экранах включаем сглаживание, чтобы символы не «дребезжали». -
Цвет
Палитра 6×6×6 или 24-бит truecolor; приводим цвета к ближайшему доступному. -
Заголовок
ESC-операторы меняют заголовок окна и вкладки tmux. -
Итог
Carbonyl запускает весь веб в терминале без X11/Wayland:cargo install carbonyl.
Комментарии (17)
- Carbonyl — терминальный браузер на движке Chrome, удивительно шустрый и юзабельный, особенно с --zoom=300 --bitmap.
- Пользователи просят добавить Kitty Graphics Protocol, sixel/chafa для нормального вывода картинок без ASCII-арта.
- Проект вдохновлён browsh, но работает быстрее; автору даже помог получить работу.
- Запускается в podman, показывает YouTube «кубиками» и почти справляется с капчей (могут помочь мультимодальные LLM).
- Под капотом — Skia и Mojo из Chromium, что позволяет рендерить всё, включая PDF.
A review of Nim 2: The good and bad with example code
Плюсы Nim 2
- Память: по умолчанию ORC/ARC (RAII, деструкторы, move/copy), а не трассирующий GC. Можно
--mm:noneили--mm:atomicArc. - Компилируется в C/C++/Obj-C/JS; выбираем компилятор (
gcc,nvcc, …). - Лёгкая интеграция:
{.importc.},{.importcpp.},{.importjs.},{.compile.}для сторонних файлов. - Метапрограммирование: макросы,
staticисполнение кода на этапе компиляции, генерация CUDA. - Краткость: мало шаблонного кода (чат на 70 строк).
- Производительность ≈ C/C++/Rust; поддержка SIMD, CUDA.
Минусы и подводные камни
- Генерируемый C/C++ код нечитаем — не цель проекта.
- Нет атомарных счётчиков в ORC по умолчанию (требуется флаг).
- Некоторые старые статьи/комментарии описывают Nim 1.x (GC по умолчанию).
- Синтаксис чувствителен к отступам и регистру (но это субъективно).
Мини-пример: простой key/value формат
import std/[tables, strutils]
type Config = object
host*: string
port*: int
proc loadConfig(path: string, T: typedesc): T =
let data = readFile(path).splitLines()
var kv = initTable[string, string]()
for line in data:
let parts = line.split('=')
if parts.len == 2:
kv[parts[0].strip()] = parts[1].strip()
result.host = kv.getOrDefault("host", "localhost")
result.port = parseInt(kv.getOrDefault("port", "8080"))
let cfg = loadConfig("app.conf", Config)
echo cfg.host, ":", cfg.port
Файл app.conf:
host = 0.0.0.0
port = 9000
Всё компилируется в один бинарник без GC-задержек.
Комментарии (51)
- Пользователи отмечают редкую, но полезную возможность Nim — определять собственные операторы.
- Хвалят макросы, интегрированные в систему типов и перегрузку, сравнивая язык с «статически типизированным Lisp».
- Критика: сложности с Windows (Nimble-зависимости тянут GCC, Defender блокирует бинарники) и неоднозначная «фантастическая» интеграция с C++.
- Обсуждают объём JS-артефактов: для мелких примеров — десятки килобайт, но без оптимизации могут быть мегабайты.
- Для WASM рекомендуют компилировать в C и прогонять через Emscripten, но стандартные JS-биндинги не работают.
- Вопросы IDE: есть nimsuggest и быстрая настройка для Neovim, но плагинов для JetBrains почти нет.
Mistral Integration Improved in Llama.cpp
- Добавлена поддержка моделей Mistral-Small-3.1-24B-Instruct-2503 и Mistral-Small-24B-Instruct-2501
- Улучшена работа с Mamba-2 и Sliding Window Attention
- Новые правила конвертации:
convert-hf-to-gguf.pyтеперь корректно обрабатываетsliding_window,mamba2,attention_bias,tie_word_embeddings - Обновлён
llama_model_loaderиllama_model: добавлены поляmamba2иsliding_window, упрощена логика KV-cache - Поддержка
mamba2вllama_contextиllama_decode - Удалены устаревшие
llama_modelиllama_vocab - Добавлены тесты
test-mistral.pyиtest-mistral-vision.py
Комментарии (11)
- Mistral предлагает mistral-common как официальный токенизатор, но пока только через Python-библиотеку и временный REST-обвязанный FastAPI.
- Сообщество жалуется: «cpp-бинарь, зависящий от Python-сервера — временное и грустное решение», ждут нативный C++ порт.
- Пользователи расстроены, что Mistral, выпуская веса, не сразу поддерживает llama.cpp, на котором держится большинство «домашних» запусков.
- Некоторые замечают, что llama.cpp и так тянет Python для шаблонов, но это не отменяет желания увидеть полноценную C++ реализацию.
- Сторонники Mistral отвечают: компания маленькая, пока не ясно, какие именно инференс-фреймворки поддерживать, зато открыли собственный mistral-inference.
Комментарии (112)
Whenever I start to feel like a real programmer making games and webapps and AI-enhanced ETL pipelines, I inevitably come across the blog post of a C++ expert and reminded that I am basically playing with legos and play-doh. I had to do a UML thing for the first time in years for