Вариант метапрограммирования шаблонов на Haskell

Я новичок в Хаскеле. Учитывая, что вся предпосылка Haskell заключается в том, что функция всегда будет возвращать одно и то же значение, я ожидаю, что будет какой-то способ, например. вычисление значений Фибоначчи констант во время компиляции, как я могу сделать в C++ с метапрограммированием шаблона, но я не понимаю, как это сделать. Есть ли способ?


person lobsterism    schedule 29.12.2012    source источник
comment
Шаблон Haskell, возможно? Это даже немного менее ужасно, чем метапрограммирование шаблонов в C++, хотя это мало о чем говорит. Ни то, ни другое не очень весело. :П   -  person C. A. McCann    schedule 29.12.2012
comment
@C.A.McCann Это работает для вымыслов? Насколько я понял, TH больше похож на макросы C, но это может быть совершенно неправильно. (Тем не менее, шаблон C++ - это своего рода просто прославленные макросы, и хм, я думаю, что можно было бы определить fibs в C во время компиляции только с помощью макросов, не уверен ...) В любом случае, конкретный пример поможет.   -  person lobsterism    schedule 29.12.2012
comment
TH — это произвольный код на Haskell, выполняемый во время компиляции, который может генерировать синтаксические деревья для вставки определений, выражений или чего-то еще в код. Макросы C даже не начинают сравнивать. Он далеко не так ограничен, как мусор шаблона C++, его просто неудобно использовать для чего-либо нетривиального, потому что работа с типами данных AST неуклюжа.   -  person C. A. McCann    schedule 29.12.2012
comment
Я обнаружил, что это очень похоже на то, как выглядели бы макросы Lisp, если бы у Lisp был реальный синтаксис.   -  person Daniel Gratzer    schedule 29.12.2012


Ответы (2)


редактировать: Даниэль Фишер указывает, что вы можете перенести обычное выражение в Template Haskell и оценить результат во время компиляции, с учетом определенных ограничений на тип вывода, используя обычную функцию fib, а затем соединив

$(let x = fib 1000 in [|x|])

Оригинальный ответ следует.

Как указано в комментариях, для этого подходит Template Haskell. Для индуктивных функций, таких как Фибоначчи, это довольно просто. Вы пишете код, аналогичный стандартному определению, но возвращающий значение ExpQ. Из-за ограничений по соединению вам нужно будет использовать 2 модуля.

{-# LANGUAGE TemplateHaskell #-} 
module TH where

import Language.Haskell.TH

fibTH :: Int -> ExpQ
fibTH 0 = [| 0 |]
fibTH 1 = [| 1 |]
fibTH n = [| $(fibTH (n-1)) + $(fibTH (n-2)) |]

а также

{-# LANGUAGE TemplateHaskell #-}
module Main where

import TH

y :: Int
y = $(fibTH 10)

main = print y

Чтобы убедиться, что работа выполняется во время компиляции, мы можем скомпилировать с помощью -ddump-simpl, чтобы увидеть ядро, что подтверждает это.

Main.y :: GHC.Types.Int
[GblId,
 Caf=NoCafRefs,
 Str=DmdType m,
 Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=True,
         ConLike=True, WorkFree=False, Expandable=True,
         Guidance=IF_ARGS [] 10 20}]
Main.y = GHC.Types.I# 55
person John L    schedule 29.12.2012
comment
Не лучше ли иметь обычную приличную функцию fib в другом модуле, а затем соединить y = $(let x = fib 1000 in [|x|])? - person Daniel Fischer; 29.12.2012
comment
@DanielFischer Я не думал пробовать, так лучше. - person John L; 30.12.2012

Есть отличная статья Доном Стюартом, где он показывает, что использование бэкенда LLVM с правильным выбором флагов предварительно вычислит определенные функции во время компиляции и заменит их константами.

person Gabriel Gonzalez    schedule 29.12.2012
comment
Ну вроде как, но это скорее метод проб и ошибок (хотя и организованно-так), а не прямо детерминированный. Шаблоны C++ более поздние; WYSIWYG, и я надеялся на большее количество подобных решений. - person lobsterism; 29.12.2012
comment
@lobsterism Тогда вам нужен шаблон Haskell. Я упомянул это решение только потому, что вы спрашивали, почему мы не можем воспользоваться чистотой функций для их предварительной компиляции. Однако Template Haskell вообще не использует преимущества чистоты и даже может иметь побочные эффекты. - person Gabriel Gonzalez; 29.12.2012