Хаскелл:как произносится? [закрытый]
Как вы произносите эти функции в прикладном типе:
(<*>) :: f (a -> b) -> f a -> f b
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
(то есть, если бы они не были операторами, как бы они назывались?)
в качестве примечания, если бы вы могли переименовать pure к чему-то более дружественному к нематематикам, как бы вы это назвали?
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' sMonadнапример: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связано с элементом идентичности aMonoid, так что другие хорошие имена для него может быть что-нибудь, предлагающее единичное значение, нулевую операцию или такое.
это было долго, так что подведем итог:
(<*>)это просто модифицированное приложение функции, поэтому вы можете либо прочитать его как "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 foverMaybe a.вы можете переименовать
pureдоof, как и многие библиотеки JavaScript. В JS вы можете создатьMaybeСMaybe.of(a).кроме того, Вики Хаскелла имеет страницу о произношении операторов языка здесь
мне всегда нравилось
wrap. Возьмите значение и оберните его в функтор, Аппликатив, монаду. Он также хорошо работает при использовании в предложении с конкретными примерами:[],Maybeи т. д. "Он принимает значение и обертывает его вX".
(<*>) -- Tie Fighter (*>) -- Right Tie (<*) -- Left Tie pure -- also called "return"источник: программирования Haskell из первых принципов, Крис Аллен и Джули Моронуки
Comments