2015-06-10 2 views
5

Рассматриваются функции типа a -> b -> c и применяемые значения a1, a2 :: (Applicative f) => f a.

Я хочу построить функцию, которая может быть применена к функциям типа a -> b -> c для получения значений типа Applicative f :: f c. Я могу сделать это следующим образом:

g :: (Applicative f) => (a -> b -> c) -> f c 
g = \f -> f <$> a1 <*> a2 

(Явный лямбда является преднамеренным, как я рассматривает возможность строительства этой функции на любом уровне, а не только верхний уровень).

Если я пытаюсь написать g в точке свободного стиля:

g = (<$> a1 <*> a2) 

я получаю следующее Ошибка компиляции:

The operator `<$>' [infixl 4] of a section 
    must have lower precedence than that of the operand, 
     namely `<*>' [infixl 4] 
    in the section: `<$> gen1 <*> gen2' 

Я мог написать эту точку свободной реализации:

g = flip (flip liftA2 a1) a2 

но я чувствую, что это le ss readable, и проще реорганизовать реализацию на основе функции на основе infix, например, добавить другой аргумент, чем изменить выше, чтобы использовать liftA3.

можно написать цепочку композиций:

g = (<*> a2) . (<$> a1) 

Это достигается точка свободного стиля и просто добавить аргументы - но они получают предваряется на левой, а не добавляется справа, так вы теряете соответствие с типом функции (a -> b -> c). Более того, с большим количеством аргументов вы получаете гораздо более длинное выражение, чем просто использование лямбда, как в первой реализации.

Итак, есть ли хороший, простой способ написать раздел, который я хочу, или я застрял с лямбдой?

+0

в Scala вы можете иметь '_ <$> в <*> b' но Haskell не имеет такой синтаксис. –

+0

@ErikAllik Это интересный синтаксис! Интересно, кто-то написал для него расширение шаблона-haskell. Это было бы здорово. – AJFarmar

+0

, но '_' уже были сделаны набранными отверстиями; возможно, что-то подобное; хотя синтаксис лямбда довольно хорош. –

ответ

7

<*> работает на результат <$>, так:

g = (<*> a2) . (<$> a1) 
+2

@frasertweedale: В этом случае я просто использую ваш первый метод. Однако это не так, как будто это должна быть лямбда. 'g f = f <$> a1 <*> a2' – Ryan

+4

@frasertweedale: Даже если это не только верхний уровень, вы можете использовать' let' и т. д. Менее многословие не означает большей ясности. – Ryan

6

Я не совсем уверен, pointfree может быть лучше, чем использовать явный аргумент здесь, но еще пару идей:

  • Вы можете использовать flip инфикс:

    g = liftA2 `flip` a1 `flip` a2 
    
  • Вы можете использовать >>> из Control.Category, который перевернут .:

    g = (<$> a1) >>> (<*> a2) 
    
Смежные вопросы