2016-05-05 5 views
2

Скажем, у меня есть куча функций с типом Action -> Int -> Int (или эквивалентом), где Action - тип суммы, и каждая функция выполняет реальную работу только с одним из вариантов.Как правильно сочетать шаблоны шаблонов хэселла

data Action = Reset | Increment | Decrement 

tryReset :: Action -> Int -> Int 
tryReset a i = case a of 
    Reset -> 0 
    _ -> i 

tryIncrement :: Action -> Int -> Int 
tryIncrement a i = case a of 
    Increment -> i + 1 
    _ -> i 

tryDecrement :: Action -> Int -> Int 
tryDecrement a i = case a of 
    Decrement -> i - 1 
    _ -> i 

Есть ли способ, чтобы составить функции (например, как composedTogether), чтобы привести к выражению одного случая (optimisedCase), вместо множества примеров выражений (multipleCase)?

composedTogether :: Action -> Int -> Int 
composedTogether a = tryReset a . tryIncrement a . tryDecrement a 

optimisedCase :: Action -> Int -> Int 
optimisedCase Reset i = 0 
optimisedCase Increment i = i + 1 
optimisedCase Decrement i = i - 1 

multipleCase :: Action -> Int -> Int 
multipleCase a i = case a of 
    Decrement -> i - 1 
    _ -> case a of 
    Increment -> i + 1 
    _ -> case a of 
     Reset -> 0 
     _ -> i 

Или ghc уже волшебный и оптимизирует его автоматически?

ответ

4

Не стоит недооценивать оптимизатор GHC. Это результат ghc -ddump-simpl -O2 (GHC 7.10.1 здесь)

composedTogether = 
    \ (a_aoc :: Action) (eta_B1 :: Int) -> 
    case a_aoc of _ [Occ=Dead] { 
     Reset -> Optimization.composedTogether1; 
     Increment -> 
     case eta_B1 of _ [Occ=Dead] { GHC.Types.I# x_ayY -> 
     GHC.Types.I# (GHC.Prim.+# x_ayY 1) 
     }; 
     Decrement -> 
     case eta_B1 of _ [Occ=Dead] { GHC.Types.I# x_ayN -> 
     GHC.Types.I# (GHC.Prim.-# x_ayN 1) 
     } 
    } 

Как вы можете видеть, все получили встраиваемые.

Для этого мне нужно было optimisedCase. В противном случае я получил

с тех пор, как GHC обнаружил эквивалентные версии.

Мой совет: забудьте об этих микро-оптимизации, включите -O2 и дайте компилятору выполнить свою работу.

Это, как говорится, не стоит переоценивать то, что может сделать оптимизатор! : -P Когда это имеет значение, проверьте сгенерированный Core.

+0

Иногда оптимизатор делает слишком много! На днях мне потребовалось более часа, чтобы выяснить, как предотвратить GHC от выделения thunk, чтобы получить общий доступ, который мне не нужен, когда я не хотел выделения! – dfeuer

0
optimisedCase :: Action -> Int -> Int 
optimisedCase Reset i = 0 
optimisedCase Increment i = i + 1 
optimisedCase Decrement i = i - 1 

является предпочтительным обозначения и гораздо чище (что эквивалентно синтаксисе случае)

+0

Спасибо, я отредактирую свой вопрос, чтобы показать предпочтительную нотацию. –

+1

Просто, чтобы уточнить, это не тот ответ, который я искал. Я искал способ комбинировать функции 'tryReset',' tryIncrement' и 'tryDecrement' с использованием комбинаторов (например, состав функций), чтобы обеспечить эффективный код; вместо того, чтобы вручную писать эффективный код. –

0

После того, как думать об этом немного. Я думаю, что это возможно, если я использую церковную версию Action.

import Data.Monoid (Endo(..)) 

data Action' a = Action' 
    { onReset :: a 
    , onIncrement :: a 
    , onDecrement :: a 
    } 

instance Functor Action' where 
    fmap f a = Action' (f $ onReset a) (f $ onIncrement a) (f $ onDecrement a) 

tryReset' :: Action' (Endo Int) 
tryReset' = Action' (Endo $ const 0) mempty mempty 

tryIncrement' :: Action' (Endo Int) 
tryIncrement' = Action' mempty (Endo succ) mempty 

tryDecrement' :: Action' (Endo Int) 
tryDecrement' = Action' mempty mempty (Endo pred) 

composeAction' :: Monoid a => Action' a -> Action' a -> Action' a 
composeAction' x y = Action' 
    (onReset x `mappend` onReset y) 
    (onIncrement x `mappend` onIncrement y) 
    (onDecrement x `mappend` onDecrement y) 

composedTogether' :: Action' (Endo Int) 
composedTogether' = tryReset' 
    `composeAction'` tryIncrement' 
    `composeAction'` tryDecrement' 

action :: Action' a -> Action -> a 
action a Reset = onReset a 
action a Increment = onIncrement a 
action a Decrement = onDecrement a 

doComposedTogether' :: Action -> Int -> Int 
doComposedTogether' = action (appEndo <$> composedTogether') 

Мой следующий вопрос: это лучший способ сделать это? Есть ли уже существующая библиотека, которая это делает? Призмы?

Смежные вопросы