2013-11-15 4 views
2

У меня эту функциюPointfree стиль программирования в Haskell

rulesApply :: [PhrasePair] -> Phrase -> Phrase 

rulesApply pp = try (transformationsApply "*" reflect pp) 

Я хочу узнать, как сделать это pointfree.

try :: (a -> Maybe a) -> a -> a 
try f x = maybe x id (f x) 
transformationsApply :: Eq a => a -> ([a] -> [a]) -> ([([a], [a])] -> ([a] -> Maybe [a])) 
transformationsApply wc f pfPair list = foldr1 orElse (map (transformationApply wc f list) pfPair) 


rulesApply pp = try (transformationsApply "*" reflect pp) 

(transformationsApply "*" reflect) pp имеет тип Eq a => ([([a], [a])] -> ([a] -> Maybe [a]))

Мы видим, что

try :: (a -> Maybe a) -> a -> a 

так попробовать принимает функцию (a -> Maybe a) в качестве аргумента. и мы видим, что возвращаемый тип (transformationsApply "*" reflect) pp равен ([a] -> Maybe [a])), поэтому мы сможем писать.

rulesApply pp = try . (transformationsApply "*" reflect) pp 

но это дает сообщение об ошибке.

+3

В конце, вы можете сделать 'междусобойчик установить pointfree', который будет установлен инструмент командной строки, которая может взять функцию и преобразовать его в как можно ближе к pointfree, насколько это возможно. – bheklilr

ответ

1

Это довольно просто на самом деле:

rulesApply :: [PhrasePair] -> Phrase -> Phrase 
rulesApply = try . transformationsApply "*" reflect 

Point-свободное программирование не только об эстетике. Речь идет о приближении проблемы на более высоком уровне: вместо работы с переменными функций вы работаете с самими функциями, тем самым устраняя целую проблемную область.

Давайте проанализируем подпись оператора (.).

(.) :: (b -> c) -> (a -> b) -> (a -> c) 

Я намеренно поставил фигурные скобки вокруг a -> c, чтобы понять, что он принимает две функции для получения другой функции. В этом отношении это не сильно отличается от любого оператора на примитивные значения, например .:

(+) :: Int -> Int -> Int 

Теперь, не одержимы об этом и не ожидаем, что он подходит любую проблему на вашем пути. Это просто еще один инструмент в вашем кармане, который следует использовать надлежащим образом. Это наиболее распространенное использование во избежание избыточных лямбда. Вот некоторые примеры:

putStrLn . (++ "!") == \a -> putStrLn (a ++ "!") 

void . return == \a -> return a >> return() 

Вторым примером является в основном эквивалент const (return()) == \a -> return(), но я предпочитаю его по эстетическим причинам. Я думаю, что компилятор все равно оптимизирует этот материал.

9

Всякий раз, когда у вас есть что-то, что выглядит как

\x -> f (g x) 

вы можете превратить его в

f . g 

В этом случае, у вас есть

s   x = f (g         x ) 
rulesApply pp = try (transformationsApply "*" reflect pp) 

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

s   = \x -> f (g         x ) 
rulesApply = \pp -> try (transformationsApply "*" reflect pp) 

, который, в свою очередь, в соответствии с нашим правилом,

s   = f . g 
rulesApply = try . transformationsApply "*" reflect 
7

Это сравнительно легко удалить точки, но вы должны двигаться шаг за шагом.

rulesApply pp = try (transformationsApply "*" reflect pp) 

=== [partial application] 

rulesApply pp = try ((transformationsApply "*" reflect) pp) 

=== [definition of (.)] 

rulesApply pp = (try . transformationsApply "*" reflect) pp 

=== [eta reduction] 

rulesApply = try . transformationsApply "*" reflect 
Смежные вопросы