Подтвердить что ты не робот

Может ли кто-нибудь объяснить, где в этом коде появляются аппликативные экземпляры?

isAlphaNum :: Char -> Bool
isAlphaNum = (||) <$> isAlpha <*> isNum 

Я вижу, что это работает, но я не понимаю, откуда берутся экземпляры Applicative (или Functor).

4b9b3361

Ответ 1

Это экземпляр Applicative для ((->) r), функционирует из общего типа. Он объединяет функции с одним и тем же первым типом аргумента в одну функцию, дублируя один аргумент для использования для всех из них. (<$>) - составная функция, pure - const, и вот что (<*>) переводит на:

s :: (r -> a -> b) -> (r -> a) -> r -> b
s f g x = f x (g x)

Эта функция, возможно, более известна как комбинатор S.

Функтор ((->) r) также является монадой Reader, где общим аргументом является значение "среда", например:

newtype Reader r a = Reader (r -> a)

Я бы не сказал, что это общепринято для того, чтобы сделать функции точными, но в некоторых случаях это может улучшить ясность, когда вы привыкнете к идиоме. Например, приведенный вами пример, я могу читать очень легко, поскольку это означает, что "является символом буквы или цифры".

Ответ 2

Вы получаете экземпляры так называемых статических стрелок (см. "Аппликативное программирование с эффектами" Конора МакБрайда и др.) бесплатно из пакета Control.Applicative. Таким образом, любой тип источника в вашем случае Char дает примерный экземпляр, где любой другой тип a сопоставляется с типом Char -> a.

При объединении любого из них, скажем, примените функцию f :: Char -> a -> b к значению x :: Char -> a, семантика - это то, что вы создаете новую функцию Char -> b, которая будет кормить свой аргумент как в f, так и x вот так,

f <*> x = \c -> (f c) (x c)

Следовательно, как вы указываете, это делает ваш пример эквивалентным

isAlphaNum c = (isAlpha c) || (isNum c)

На мой взгляд, такие усилия не всегда необходимы, и было бы лучше, если бы у Haskell была более синтаксическая поддержка аппликаций (может быть, что-то вроде двухуровневых языков).

Ответ 3

Следует отметить, что вы получаете аналогичный эффект, используя функции лифта, например:

import Data.Char
import Control.Applicative 

isAlphaNum = liftA2 (||) isAlpha isNumber

Или, используя экземпляр monad ((- > ) r) вместо аппликативного:

import Data.Char
import Control.Monad 

isAlphaNum = liftM2 (||) isAlpha isNumber

[Отступление]

Теперь, когда вы знаете, как распределить один аргумент с двумя промежуточными функциями и результат с помощью двоичной функции, существует связанный с этим случай, когда вы хотите распределить два аргумента одной промежуточной функцией и результаты в двоичную функцию:

import Data.Function

orFst = (||) `on` fst

-- orFst (True,3) (False, 7)
--> True

Этот шаблон является, например, часто используется для функции compare.