Решил тут заняться преждевременными оптимизациями. Очень часто во всяких численных методах используется конструкция вида:
f(x,y)=\sum_{i,j}{a_{x+i,y+j} p_{i,j}}, где a - сетка значений какой-то величины, p - константная матрица. И эта сумма обычно находится в самых узких местах программы.
Поскольку p известна заранее (на стадии компиляции), я решил заоптимизировать вычисление таким образом, что сумма не будет проходить по нулевым элементам и не делать умножение на 1 и -1.
Получение нужной функции:
{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies #-}
module Module1 where
import Language.Haskell.TH
import Data.VectorSpace
stencil2D :: (Ord b, Num b, VectorSpace a, b ~ Scalar a) => [[b]] -> ((Int,Int)->a) -> (Int, Int) -> a
stencil2D pat get =
    let
        lx = length $ head pat
        ly = length pat
        -- Zip indices and values:
        i = [
                 ((x', y'), a)
                |(xx, y) <- zip pat [0..]
                ,(a, x) <- zip xx [0..]
                ,a /= 0
                ,let x' = x-(lx`div`2)
                ,let y' = y-(lx`div`2)
            ]
        f r0 (r, a)
                | a ==  1 = (^+^)(get $ r^+^r0)
                | a == -1 = (^-^)(get $ r^+^r0)
                | True    = (^+^)(a*^(get $ r^+^r0))
        
        fun r = foldr (\b a->a.(f r b)) id i
    in
        \r -> fun r zeroV
Проверил бегло, вроде работает. Применение её в compile time и проверка:
-- Dummy get function:
fakeGet :: (Int, Int) -> Float
fakeGet _ = 1
foo = $([| stencil2D [[0,1,0],[1,-4,1],[0,1,0]] fakeGet |])
main = print $ foo (1,1)
Вопрос. (Моё знание и понимание template haskell и quasi-quotation на данный момент близко к нулю.) Действительно ли foo раскрутилась в функцию, с 5 операциями + и одним умножением? (zeroV^+^... я потом уберу).
Ну и прошу опытных людей заодно конструктивно поругать мой код в целом.


