2016-12-30 18 views
5

Mam kilka powiązań, które chcę zachować jako prywatne dla funkcji (tak, nie zadeklarowane w przestrzeni nazw modułu najwyższego poziomu), ale które są kosztowne do obliczenia, więc wolałbym, aby były CAF. Czy istnieje pragma lub jakaś inna sztuczka, którą mógłbym użyć, aby wymusić na GHC CAF dla tych wiązań?W jaki sposób można wymusić powiązania w klauzuli where jako CAF?

przykładowy kod:

header :: HeaderProps -> Element 
header = div_ [style_ headerStyle] [str_ "Here be headerz"] 
    where 
    -- Creating the 'headerStyle' is expensive! Do it only once 
    -- regardless of how many times the 'header' function is used. 
    headerStyle = mkStyle $ do 
     display flex 
     flexDirection column 
     padding (px 20) (px 40) 

Jeśli nie jest to możliwe, widzę kilka opcji, ale każdy ma swoje wady:

  1. Przejście każdej funkcji do własnego modułu więc mogę przenieść drogie powiązania z przestrzenią nazw modułu najwyższego poziomu bez obawy o inne funkcje uzyskujące dostęp do nich. Ponieważ spodziewam się, że będzie wiele takich funkcji, liczba modułów eksploduje (setki w dużym projekcie). I nie jestem fanem tylko jednej funkcji na moduł religii, tak jak jest on głoszony w społeczności node.js.
  2. Przenieś wiązania do przestrzeni nazw modułu najwyższego poziomu i nadaj każdej z nich wystarczająco unikatową nazwę, aby uniknąć konfliktów i wyraźnie wyjaśnić, że są prywatne (np. headerStyle ->header__headerStyle).
  3. Użyj TemplateHaskell, aby odciążyć kosztowne obliczenia do kompilacji.
+5

Dlaczego po prostu nie wykonywać funkcji na najwyższym poziomie, ale nie eksportować jej z modułu? – user2297560

+0

@ user2297560 Wyjaśniłem, dlaczego chcę tego uniknąć. – wereHamster

+0

To nie dzieje się automatycznie? –

Odpowiedz

4

Jeśli mam rozumieć swoją intencją poprawne, to jest rzeczywiście proste: wystarczy przesunąć żadnych argumentów funkcji w lambda, więc cała funkcja (w tym blok where) jest CAF:

foo :: Int -> Int 
foo = \x -> x * nFermat 
where nFermat = length [() | a<-[1..m], b<-[1..m], c<-[1..m], a^3+b^3==c^3] 
     m = 200 

main = interact $ show . foos . read 
where foos n = foo <$> [0..n] 

W ten sposób, niezależnie od argumentów, których używasz x, ten sam zostanie ponownie użyty.

[email protected]:/tmp$ time runhaskell fermata.hs <<< 1 
[0,0] 
real 0m23.199s 
user 0m23.177s 
sys 0m0.045s 
[email protected]:/tmp$ time runhaskell fermata.hs <<< 100 
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 
real 0m22.629s 
user 0m22.601s 
sys 0m0.052s 
+0

FTR: 'time runhaskell' nie jest właściwym sposobem testowania wydajności ... – leftaroundabout

+0

Użyłem podobnego rozwiązania (eta-abstrakcja) w kodzie źródłowym PureScript (ale z różnych powodów, aby obejść ścisłość tego języka). Chociaż linters i inne narzędzia mogą sugerować, aby go usunąć, ponieważ służy pozornie nie jest przydatnym celem. Miałem nadzieję na coś bardziej wyraźnego. Mimo że działa to, wydaje się, że wyciek szczegółów realizacji, które nie są tak oczywiste dla zwykłego czytelnika kodu. – wereHamster

+0

Cóż, w Haskell ten wzór nie jest taki niezwykły. I dlatego, że nie służy to żadnemu innemu celowi, ale powoduje zamknięcie funkcji CAF, powiedziałbym, że powinno to być wystarczająco oczywiste, aby doświadczyć Haskelera, o co chodzi. – leftaroundabout