Хаскелл:как произносится? [закрытый]



Как вы произносите эти функции в прикладном типе:



(<*>) :: f (a -> b) -> f a -> f b
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a


(то есть, если бы они не были операторами, как бы они назывались?)



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

592   5  

5 ответов:

Извините, я действительно не знаю свою математику, поэтому мне любопытно, как произносятся функции в прикладном типе

знание вашей математики, или нет, в значительной степени не имеет значения здесь, я думаю. Как вы, вероятно, знаете, Хаскелл заимствует несколько бит терминологии из различных областей абстрактной математики, в первую очередь Теория Категорий, откуда мы получаем функторы и монады. Использование этих терминов в Haskell несколько отличается от официальной математические определения, но они обычно достаточно близки, чтобы быть хорошими описательными терминами в любом случае.

The Applicative класс типа сидит где-то между Functor и Monad, поэтому можно было бы ожидать, что он будет иметь аналогичную математическую основу. Документация для Control.Applicative модуль начинается с:

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

Мда.

class (Functor f) => StrongLaxMonoidalFunctor f where
    . . .

не так броско, как Monad, я думаю.

что все это в основном сводится к тому, что Applicative не соответствует ни одной концепции, которая особенно интересные математически, так что нет готовых терминов, лежащих вокруг, которые захватывают то, как он используется в Haskell. Итак, отложите математику на данный момент.


если мы хотим знать, как назвать (<*>) это может помочь узнать, что это в основном означает.

так в чем же дело Applicative во всяком случае, и почему do мы называем это что?

что Applicative количество на практике-это способ поднять произвольные функции в Functor. Рассмотрим комбинацию Maybe (возможно, самый простой нетривиальный Functor) и Bool (также Самый простой нетривиальный тип данных).

maybeNot :: Maybe Bool -> Maybe Bool
maybeNot = fmap not

функции fmap позволяет нам поднять not на Bool для работы на Maybe Bool. Но что делать, если мы хотим поднять (&&)?

maybeAnd' :: Maybe Bool -> Maybe (Bool -> Bool)
maybeAnd' = fmap (&&)

Ну, это не то, что мы хотим на всех! На самом деле, это практически бесполезно. Мы можем попытаться быть умными и прокрасться еще Bool на Maybe через заднюю дверь...

maybeAnd'' :: Maybe Bool -> Bool -> Maybe Bool
maybeAnd'' x y = fmap ($ y) (fmap (&&) x)

...но это нехорошо. Во-первых, это неправильно. Во-вторых, это некрасиво. Мы могли бы продолжать попытки, но оказывается, что есть нет способа поднять функцию нескольких аргументов для работы с произвольным Functor. Раздражает!

с другой стороны, мы могли бы сделать это легко, если мы использовали Maybe ' s Monad например:

maybeAnd :: Maybe Bool -> Maybe Bool -> Maybe Bool
maybeAnd x y = do x' <- x
                  y' <- y
                  return (x' && y')

теперь, это много хлопот просто перевести простую функцию-вот почему Control.Monad обеспечивает функцию, чтобы сделать это автоматически, liftM2. 2 в его названии относится к тому, что он работает на функциях ровно двух аргументов; аналогичные функции существуют для 3, 4 и 5 функций аргументов. Эти функции лучше, но не идеально, а указание количества аргументов некрасиво и неуклюже.

что приводит нас к бумага, которая ввела класс Аппликативного типа. В нем авторы делают по существу два замечания:

  • поднимать функции мульти-аргумента в Functor - это очень естественная вещь, чтобы сделать
  • это не требует полного возможности Monad

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

все это подводит нас к следующему пункту: (<*>) просто представляет приложение функции--так почему произносите его иначе, чем вы делаете пробел "оператор сопоставления"?

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

ap :: (Monad m) => m (a -> b) -> m a -> m b

здесь ap это, конечно, сокращение от "применить". Так как любой Monad может быть Applicative и ap требуется только подмножество функций, присутствующих в последнем, мы можем, пожалуй, сказать, что если (<*>) не оператор, он должен называться ap.


мы также можем подходить с другой стороны. Элемент Functor операция подъема называется fmap потому что это обобщение map операции над списками. Какая функция в списках будет работать как (<*>)? Вот что ap делает по спискам, конечно, но это не особенно полезно само по себе.

на самом деле, есть, пожалуй, более естественная интерпретация списков. Что? приходит на ум, когда вы смотрите на следующий тип подписи?

listApply :: [a -> b] -> [a] -> [b]

есть что-то настолько заманчивое в идее выстраивания списков параллельно, применяя каждую функцию в первом к соответствующему элементу второго. К сожалению, для нашего старого друга Monad, это простая операция нарушает законы монады если списки имеют разную длину. Но это делает штраф Applicative в этом случае (<*>) становится способом шнуровать вместе обобщенная версия zipWith, так что, возможно, мы можем себе представить, называя его fzipWith?


эта идея молнии на самом деле приносит нам полный круг. Вспомните, что математические вещи раньше, о моноидальных функторах? Как следует из названия, это способ объединения структуры моноидов и функторов, оба из которых являются знакомыми классами типа Haskell:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

class Monoid a where
    mempty :: a
    mappend :: a -> a -> a

как бы они выглядели, если бы вы положили их в коробку вместе и немного встряхнули ее? От Functor мы будем держать идею структура не зависит от типа параметра, и Monoid мы сохраним общую форму функций:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ?
    mfAppend :: f ? -> f ? -> f ?

мы не хотим предполагать, что есть способ создать действительно "пустой" Functor, и мы не можем вызвать значение произвольного типа, поэтому мы исправим тип mfEmpty как f ().

мы тоже не хотим принуждать mfAppend нужен последовательный параметр типа, так что теперь у нас есть это:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f ?

каков тип результата для mfAppend? У нас есть два произвольных типа, о которых мы ничего не знаем, поэтому у нас не так много вариантов. Самое разумное-просто сохранить оба:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f (a, b)

в какой момент mfAppend теперь явно обобщенная версия zip по спискам, и мы можем реконструировать Applicative легко:

mfPure x = fmap (\() -> x) mfEmpty
mfApply f x = fmap (\(f, x) -> f x) (mfAppend f x)

это также показывает нам, что pure связано с элементом идентичности a Monoid, так что другие хорошие имена для него может быть что-нибудь, предлагающее единичное значение, нулевую операцию или такое.


это было долго, так что подведем итог:

  • (<*>) это просто модифицированное приложение функции, поэтому вы можете либо прочитать его как "ap" или "apply", либо полностью удалить его так, как вы бы использовали обычное приложение функции.
  • (<*>) также грубо обобщает zipWith в списках, поэтому вы можете прочитать его как "zip-функторы с", аналогично чтение fmap "карта функтор с".

первый ближе к цели Applicative тип класса--как следует из названия--так что это то, что я рекомендую.

на самом деле, я призываю либеральное использование и не произношение всех поднятых операторов приложений:

  • (<$>), который поднимает функцию с одним аргументом в Functor
  • (<*>), который связывает мульти-аргумент функция через Applicative
  • (=<<), которая связывает функцию, которая вводит Monad на существующее вычисление

все три, в глубине души, просто регулярное приложение функции, приправленный немного.

поскольку у меня нет амбиций улучшить технический ответ C. A. McCann, я займусь более пушистым:

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

в качестве альтернативы, тем более, что нет конца постоянной тоске и предательству, наполненным криком против Monad версия, под названием "return" Я предлагаю другое название, которое предлагает свои функции в способ, который может удовлетворить самый императивный из императивных программистов и самый функциональный из них...ну, надеюсь, все могут жаловаться на то же самое: inject.

принимает значение. "Внедрить" его в Functor,Applicative,Monad, или что-нибудь. Я голосую за "inject", и я одобрил это сообщение.

вкратце:

  • <*> вы можете называть это применить. Так что Maybe f <*> Maybe a читается как применить Maybe f over Maybe a.

  • вы можете переименовать pure до of, как и многие библиотеки JavaScript. В JS вы можете создать Maybe С Maybe.of(a).

кроме того, Вики Хаскелла имеет страницу о произношении операторов языка здесь

мне всегда нравилось wrap. Возьмите значение и оберните его в функтор, Аппликатив, монаду. Он также хорошо работает при использовании в предложении с конкретными примерами:[],Maybe и т. д. "Он принимает значение и обертывает его в X".

(<*>) -- Tie Fighter
(*>)  -- Right Tie
(<*)  -- Left Tie
pure  -- also called "return"

источник: программирования Haskell из первых принципов, Крис Аллен и Джули Моронуки

Comments

    Ничего не найдено.