2015-10-21 19 views
14

Mam funkcję (*~). Większość kosztów oceny x *~ y pochodzi z inspekcji drugi argument, z grubsza wzdłuż tych linii:Częściowa ocena praworęcznych sekcji operatorów

(*~) :: a -> b d -> c d a 
x *~ y = case y' of 
      Bar -> cheapFunction y' x 
      Baz -> cheapFunction2 y' x 
      Quux -> cheapFunction3 y' x 
    where 
    y' = expensive y 

Czy istnieje jakiś sposób, aby przekonać GHC częściowo ocenić odcinki operatora jak (*~ y)?

Próbowałem przepisanie go tak:

(*~) = flip go 
    where 
    go y = let y' = expensive y 
      in case y' of 
       Bar -> cheapFunction y' 
       Baz -> cheapFunction2 y' 
       Quux -> cheapFunction3 y' 

ale nie wydaje się, aby pomóc. Myślę, że może to być spowodowane tym, że flip wymaga wszystkich swoich argumentów przed wykonaniem przerzucania?

Jednym ze sposobów jest po prostu przerzucenie operatora, ale czyta się o wiele bardziej naturalnie, gdy kosztowny operand znajduje się po prawej stronie, ponieważ jest wyrównany z istniejącą notacją.

Czy mogę odpowiednio wypróbować {-# RULE #-}? Jeśli tak, co powinna ona powiedzieć? (Nie jestem pewien, jak daleko składnia będzie wyglądała, zanim reguły będą szukać dopasowań, między innymi).

+1

Nie jestem pewien, co częściowa ocena by kupić. 'y'' będzie już udostępnione. Czy chcesz dokonać przypomnienia? Musisz samodzielnie dodać pamięć. Jaką regułę chciałbyś napisać? –

+1

Co stanie się, jeśli użyjesz niestandardowego 'flipa '? 'flip 'f x = \ y -> wbudowany f y x' – dfeuer

+2

@ReinHenrichs Czy' y'' będzie już udostępniony? Jeśli tak, to moje zrozumienie jest dość zepsute i chciałbym przyjąć odpowiedź wyjaśniającą, w jaki sposób. Jeśli zrobię 'fmap (* ~ y) someLongList', czy nie będzie to za każdym razem przeliczone? –

Odpowiedz

5

Aby uruchomić taką optymalizację, musisz upewnić się, że twoja funkcja została zainspirowana. Przed deklaracją funkcji należy umieścić pragma {-# INLINE (*~) #-}. Nie mogę zagwarantować, że rozwiąże to twój problem, ale jest to jedyny sposób, w jaki widzę, że się do niego zbliża. Sprawdziłbym wygenerowany rdzeń kodu za pomocą narzędzia takiego jak "ghc-core", aby się upewnić.

Jednak twój problem jest tylko oznaką niewłaściwego składu kodu. Twoja funkcja wykonuje wiele niepowiązanych rzeczy. expensive y powinien po prostu być z tego zbudowany, wtedy twój problem zostanie usunięty jako taki. To jest wzór użycia powinien być x *~ expensive y zamiast x *~ y.

+1

To jest dobry punkt i rozwiąże problem. Ale typ wyniku "drogiego" jest obecnie ukrytym szczegółem implementacji, ponieważ nie jest on naprawdę interesujący dla użytkowników biblioteki, która eksponuje '(* ~)'. –

+2

W rzeczywistości, nie byłby to również ukryty szczegół implementacji w twoim przypadku, po prostu wystawiałbyś go inaczej. Będziesz także zaciemniał swoje intencje i ukrywał to przed systemem typu, a zatem musiałby w inny sposób informować użytkownika o tym, w jaki sposób ma być używana twoja funkcja specjalna. To nie byłoby dobre z punktu widzenia użyteczności. I znowu byłoby to sprzeczne z separacją obaw. Masz już listę wskazań, że twoje podejście jest nieprawidłowe, więc lepiej pomyśl dwa razy. –

+0

Dobre punkty. –