Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет Abu Idris ([info]zhd)
@ 2017-01-10 21:11:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Настроение: geeky
Музыка:Tomo Akikawabaya -- Mars
Entry tags:generic programming, haskell, music making

Против аналогового секвенсора не попрешь, part 1: QuasiQuotation
На первоначальных этапах нам предстоит иметь дело с генерацией и преобразованиями С-кода, поэтому стоит поговорить о механизме QuasiQuotation.

Пусть у вас есть два языка программирования L и M (это может быть один и тот же язык). Вы хотите генерировать или преобразовывать программы на языке M функциями языка L. Пусть SM -- тип, описывающий синтаксис языка M в языке L. На практике тип SM это обычно запутанный набор взаимно-рекурсивных типов. Чтобы что-то с ним сделать вам придется изучить как он устроен и писать много довольной уродливых выражений. Гораздо удобнее иметь механизм, позволяющий вам писать куски кода на M, которые автоматически преобразовываются в выражения типа SM. Более того, вы можете иметь ``переменные'' внутри фрагментов M-кода, с соответствующей операцией подстановки. Это и есть механизм QuasiQuotation. Когда возможности подстановки нет, это называется просто Quotation.

Поясним на примере.

Пусть L=Haskell, M=C. Пользуемся пакетом language-c-quote. Очень, надо сказать, неудачное там определение синтаксиса C. Но для примера это даже хорошо.

Напишем функцию, которая для любых двух C-типов дает определение их прямого произведения, т.е. структуры с двумя полями заданных типов. Без QQ это выглядит так:

  droot :: Decl
  droot = DeclRoot noLoc

  ident :: String -> Id
  ident i = Id i noLoc

  ty :: TypeSpec -> Type
  ty t = Type (DeclSpec [] [] t noLoc) droot noLoc

  struct :: Id -> [FieldGroup] -> TypeSpec
  struct i [] = Tstruct (Just i) Nothing [] noLoc
  struct i (x : xs) = Tstruct (Just i) (Just (x : xs)) [] noLoc

  fieldG :: TypeSpec -> Id -> FieldGroup
  fieldG t x = FieldGroup (DeclSpec [] [] t noLoc) [Field (Just x) (Just droot) Nothing noLoc] noLoc

  product :: String -> TypeSpec -> TypeSpec -> Type
  product s t0 t1 =
   let
    name = ident s
    proj0 = ident (s ++ "_proj0")
    proj1 = ident (s ++ "_proj1")
   in
  ty . struct name $ [fieldG t0 proj0, fieldG t1 proj1]

Ад каннибалов, не правда ли? C QQ это выглядит так:

  product' :: String -> TypeSpec -> TypeSpec -> Type
  product' s t0 t1 =
   let
    name = s
    proj0 = s ++ "_proj0"
    proj1 = s ++ "_proj1"
    ty0 = ty t0
    ty1 = ty t1
   in
  [cty| struct $id:name {$ty:ty0 $id:proj0 ; $ty:ty1 $id:proj1 ;} |]

В QQ-варианте из вспомогательных функций нам понадобилась только ty, все остальное делают идиоматические скобочки [cty| .. |]

Если вызвать product "product" int double, то обе функции дают нужный результат:

  struct product {
   int product_proj0;
   double product_proj1;
}


Такие дела. На фото я.