March 2nd, 2012

with Cat The Cat

Про всякие генерации кода.

Илея использования Хаскеля, как лучший C (VHDL, Javascript, и тд), чем сам C (VHDL, Javascript, и тп) состоит в использовании системы типов Хаскеля, синтаксиса Хаскеля и частично семантики Хаскеля. Вообще, это получается такая строго типизированная макросистема, возможно, оптимизирующая.

Простой пример. Для ядра Линукса сам Линус написал специальный проверяльщик, который смотрит на определения функций и ловит всякие ошибки - указатель в пользовательскую память используется напрямую, всякое такое.

Это же легко воспроизвести в системе типов Хаскеля. Фантомные параметры спешат на помощь:

data Ptr base a = Ptr (Foreign.Ptr a)

Параметр base использоваться не будет. А если мы экспортируем Ptr абстрактно, то вытащить указатель и сделать Ptr Kernel Baz из Ptr User Boo будет невозможно.

Чуть интересней создание значений по обе стороны знака присваивания. Например, разрешены x = 10, y[i] = x, *z[*p] = 1971, а вот присвоить 10 = x нельзя.

Такое тоже можно сделать, только придётся использовать GADT:
data LVal
data RVal
data Value asgn a where
    ValueDeref :: Value _a (Ptr a) -> Value LVal a
    ValueIndex :: Value _a (Ptr a) -> Value _b Int -> Value LVal a
    ValueBinOp :: BinOp x y r -> Value _a x -> Value _b y -> Value RVal r
    ValueConst :: a -> Value RVal a
И тп.

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

Далее мы можем использовать разные интересные комбинаторы. Например, если мы работаем с критичной секцией, то мы сделаем комбинатор работы с ней, и никогда не выйдем из обработки без закрытия:
bracket open close def act = do
    (opened, h) <- open
    r <- ifStat opened act (return def)
    close h
    return r
И будем использовать его не только для критической секции, но и для файлов, сокетов, контекстов OpenGL... Критическая секция и контексты OpenGL всегда открываются. Файлы и сокеты - не всегда (opened не будет тождественно равен истине).

Обычно код создаётся внутри системы в виде списков высказываний (statements по английски). Затем тривиальными преобразованиями он переводится в код на другом языке.

Что здесь полезного можно извлечь.

Во-первых, система типов Хаскеля помощней многих других. Вплоть до контекста выполнения можно задавать и отказываться (во время компиляции) что-то делать, если контекст неправилен - привилигерованная инструкция в пользовательском режиме. Про указатели я уже сказал.

Во-вторых, проще комбинировать код - "макросистема на стероидах". В C код не объект первого класса, его так просто не передашь и не вставишь. А здесь - сколько угодно.

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

И всё это - на расстоянии REPL. То есть, загрузили, посмотрели на код, проверили то-другое-третье, исправили исходник и продолжили. Расстояние между внесением ошибки и её исправлением уменьшается до минимального.