2013-09-27 3 views
1

Скажем, у нас есть две функции типа (a -> b -> c). Я хочу иметь одну функцию, которая при применении к a и b даст d, объединенную с c по заданной функции (c -> c -> d). Я пришел к этому решению с помощью стрелок:Объединение двух точных функций

combine :: (a -> b -> c) -> (a -> b -> c) -> (c -> c -> d) -> (a -> b -> d) 
combine f g op = ((uncurry op) .) . (uncurry (&&&)) . (f &&& g) 

Есть ли способ сделать это в более изящном способе, или обобщать его применимым к функциям с большой арностью (например (a -> b -> c -> d) -> (a -> b -> c -> d) -> (d -> d -> e) -> (a -> b -> c -> e))?

ответ

7

Не бойтесь быть явным. Для меня это гораздо легче читать:

combine :: (a -> b -> c) -> (a -> b -> c) -> (c -> c -> d) -> (a -> b -> d) 
combine f g op = \a b -> op (f a b) (g a b) 

не выглядит уродливой для более арностей:

combine3 f g op = \a b c -> op (f a b c) (g a b c) 
+1

Я думаю, что было бы лучше с 'op' как infix:' 'f a b' op' g a b''. Я подозреваю, что некоторые люди могут не согласиться; часть этого заключается в том, что мой редактор выделяет инфиксные вещи по-разному, что делает его намного легче читать. –

+0

Это прекрасно, спасибо! Это всегда кажется таким легким, когда вы уже видели ответ :) –

3

Хотя я согласен, явный лямбда может быть путь здесь, есть альтернативы для двоичных данных f и g. Для унарных f и g решение является элементарным.

combine f g op = liftM2 op f g 

для бинарного f и g, мы можем сделать их унарными по uncurrying их, а затем применить такое же решение! Это делает двоичную версию

combine2 f g op = curry $ liftM2 op (uncurry f) (uncurry g) 

Я просто размещаю ее там как альтернативу.

+0

Это потрясающе. Используя 'liftM2' на операторе, чтобы воспользоваться функциями' (-> r) 'в качестве монадов. Brilliant! – pyrospade

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