Аппликативные функторы
WelcomeДобро toпожаловать partво 2вторую ofчасть ourсерии seriesо onмонадах monadsи andдругих otherфункциональных functionalструктур. structures.Мы We'llпродолжим continueготовить buildingсобирать ourнашу foundationбазу onизучая theseидеи ideasапликативных byфункторов. exploringЕсли theвы conceptвсё ofеще applicativeне functors.имеете Ifтвердое youпонимание don'tфункторов, yetпересмотрите haveпервую aчасть solidэтой graspсерии. onЕсли basicвы functors,считаете, makeчто sureуже toготовы reviewк partмонадам, 1то ofможете thisсмело series!переходить Ifк youчасти think you're ready for monads already, you can move onto part 3!3.
IfВ you'veэтой neverчасти writtenприведенные Haskellпримеры before,можно nowбудет isопробовать the perfect time! Get Haskell installed on your computer by following the instructions from our Beginners Checklist!
You can follow along with all the examples in this part by usingна GHCI. But you can also take a quick peek at our Monads Github Repository! The ApplicativesComplete module has a few references for Applicative instances we see in this article.
FUNCTORSФункторы FALLINGстановятся SHORTкороткими
InВ partпервой 1,части, weмы discussedобсудили theфунктор Functorтипа typeclass.класса. WeМы foundнашли, itто allowsчто usэто toпозволяет runнам transformationsзапустить onпреобразования dataданных regardlessв ofзависимости howот theтого, dataво isчто wrapped.обернуты Noданные. matterНе ifважно, ourявляются dataли wereнаши inданные aList
, List,Maybe
, aEither
Maybe,или anдаже Either,свой orсобственный evenтип, aмы customможем type,просто weвызывать couldfmap
. simplyОднако, callчто fmap.случится However,когда whatмы happensпопробуем whenобъединить weобернутые tryданные? toДля combineпримера, wrappedесли data?мы Forпопробуем instance,произвести ifэти weвычисления tryс toпомощью haveGHCI, GHCIмы interpretполучим theseошибку calculations, we'll get type errors:типа:
>> (Just 4) * (Just 5)
>> Nothing * (Just 2)
CanМогут functorsли helpфункции usпомочь here?нам Weтут? canМы useможем исопльзовать fmap
toчтобы wrapобернуть multiplicationумножение byс theпомощью particularчастичной wrappedобертывания Maybe
value:значения:
>> let f = (*) <$> (Just 4)
>> :t f
f :: Num a => Maybe (a -> a)
>> (*) <$> Nothing
Nothing
ThisЭто givesдает usчастичную aфункци partialобернутую functionв wrappedMaybe
. inНо aмы Maybe.до Butсих weпор stillне cannotможет unwrapразвернуть thisэто andи applyприменить itк to (Just
5)5inв aобщем genericстиле. fashion.Поэтому Soнам weнужно haveобратиться toк resortкоду toспециально codeдля specificтипа to the Maybe
: type:
funcMaybe :: Maybe (a -> b) -> Maybe a -> Maybe b
funcMaybe Nothing _ = Nothing
funcMaybe (Just f) val = f <$> val
ThisЭто obviouslyочевидно won'tне workбудет withработать otherс functorsдругими types.типами функторов.
APPLICATIVESПриложения TOв THE RESCUEпомощь
ThisТо isчто exactlyтакое whatапликативные theтипы Applicativeклассов, typeclassговорят isдве for.главные It has two main functions:функции:
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
TheЧистая pureфнукция functionпринимает takesкакое-то someзначение valueи andобертывает wrapsего itв inминимальный aконтекст. minimal context. TheФункция <*>
function,вызывает calledпоследующиее sequentialприложение, application,которое takesпринимает two2 parameters.параметра. First,Первый it- takesпринимает aфункци functionобернутую wrappedв inконекст. theВторое context.- Next,обернутое aзначение. wrappedВывод value.- Itsрезультат outputприменения isфункции theк resultзначению, ofпреобразованное applyingв theконтексте. functionЭкземпляр toназывается theаппликативный value,функтор rewrappedтак inкак theон context.позволяет Anнам instanceприменять isобернутую calledфункцию. anТак applicativeкак functorпоследюущее becauseприменение itпринимает allowsобернутую usфункцию, toмы applyычасто aначинаем wrappedс function.обертки Sinceчего-то sequentialчистого applicationили takesfmap
. aЭто wrappedбудет function,понятнее weна often begin by wrapping something with either pure or fmap. This will become more clear with some examples.примерах.
Let'sДля firstначала considerпредставим multiplyingперемножение Maybe
values.значений. IfЕсли weмы areумножаем multiplyна byпостоянное aзначение, constantмы valueможем weиспользовать canфункторный useподход. theНо functorмы approach.можем Butтак weже canиспользовать alsoаппликативный useподход theобернув applicativeпостоянную approachфункцию byв wrappingчистую theи constantзатем functionиспользовать inпоследовательное pure and then using sequential application:применение:
>> (4 *) <$> (Just 5)
Just 20
>> (4 *) <$> Nothing
Nothing
>> pure (4 *) <*> (Just 5)
Just 20
>> pure (4 *) <*> Nothing
Nothing
NowТеперь ifесли weмы wantхотим to multiplyумножить 2 maybeMaybe
values,значения, weмы startначинаем byоборачивать wrappingпростую theфункцию bareпроизведения multiplicationв functionчистую. inЗатем pure.последовательно Thenприменяем weоба sequentiallyMaybe
apply both Maybe values:значения:
>> pure (*) <*> (Just 4) <*> (Just 5)
Just 20
>> pure (*) <*> Nothing <*> (Just 5)
Nothing
>> pure (*) <*> (Just 4) <*> Nothing
Nothing
IMPLEMENTINGРеализация APPLICATIVESаппликативов
FromПо theseэтим examples,примерам, weмы canможем tellсказать, theчто Applicativeэкземпляры instanceАппликативов forдля Maybe
isреализованны implementedточно, exactlyкак howмы weожидаем. wouldЧистая expect.функция Theпросто pureоборачивает functionначение simplyс wrapsпомощью aJust
. valueЗатем withсвязывает Just.веще Thenвмете, toесли chainдругие thingsфункции together,или ifзнаения eitherбудут theNothing
, functionмы orпросто theвыводим valueNothing
. isВ Nothing,противном weслучае outputприменяем Nothing.функцию Otherwiseк applyзначение theи functionпереоборачиваем toс theпомощью value and re-wrap with Just.Just
.
instance Applicative Maybe where
pure = Just
(<*>) Nothing _ = Nothing
(<*>) _ Nothing = Nothing
(<*>) (Just f) (Just x) = Just (f x)
TheЭкземпляр Applicativeаппликатива instanceдля forList
Listsбудет isнемного aинтереснее. littleОн moreможет interesting.вести Itсебя doesn'tна exactlyтак giveкак theмы behavior we might first expect.ожидаем.
instance Applicative [] where
pure a = [a]
fs <*> xs = [f x | f <- fs, x <- xs]
TheЧистая pureфункция function- isто whatчто weмы expect.ожидаем. WeМы takeпринимаем aзначение valueи andоборачиваем wrapего itкак asодиночку aв singletonсписок. inКогда aмы list.связываем Whenоперации, weмы chainпринимаем operations,LIST
weфункций. nowМы takeдолжны aожидать LISTприменения ofкаждой functions.функции Weк mightзначению expectв toсоответствующей applyпозиции. eachОднако, functionна toсамом theделе valueмы inприменяем theфункцию correspondingиз position.первого However,списка whatк actuallyкаждому happensзначению isиз weвторого. applyКогда everyу functionнас inолько theодна firstфункция, listэтот toрезаультат everyимеет valueпонятноее inповедение. theНо secondкогда list.у Whenнас weнесколько haveфункций, onlyпоявляется one function, this results in familiar mapping behavior. But when we have multiple functions, we see the difference:отличие.
>> pure (4 *) <*> [1,2,3]
[4,8,12]
>> [(1+), (5*), (10*)] <*> [1,2,3]
[2,3,4,5,10,15,10,20,30]
ThisТут makesлегко itсделать easyопределенные toоперации, doкак certainнахождение operations,попарных likeрезультатов findingдвух every pairwise product of two lists:списков:
>> pure (*) <*> [1,2,3] <*> [10,20,30]
[10,20,30,20,40,60,30,60,90]
YouВы mightвозможно beгадаете, wonderingкак howмы weсделаем mightпаралельное doприменение parallelфункторов. applicationНапример, ofмы functions.можемт Forхотеть instance,использовать weвторой mightсписок wantиз toпримера useвыше, theно secondиметь listрезультат example above, but have the result be [2,10,30]
. ThereДля isэтого aесть constructконструкт forпод this,названием calledZiplist
, ZipList!это Itновый isтип aвокрут newtypeсписка, aroundдля list,которого whoseповедение Applicativeэкземпляр instanceаппликатива isи designed to use this behavior.предусмотренно.
>> import Control.Applicative
>> ZipList [(1+), (5*), (10*)] <*> [5,10,15]
ZipList {getZipList = [6,50,150]}
CONCLUSIONВыводы
ThatЕсли wrapsвсе upэто thisкажется partнепонятным, onне applicativeбойтесь functors.вернуться Ifк this all seemed really confusing, don't be afraid to go back to partчасти 1 andи makeубедиться, sureчто youу haveвас aесть solidчеткое understandingпонимание ofтого, normalчто functorsтакое first.функторы. IfЕсли youвам feelвсё goodясно, aboutвы yourготовы knowledge,перейти you'reк nowчасти ready3, toгде moveмы ontoнаконец partиспачкаемся 3 where we finally get down and dirty with monads!монадами.
AllВсе theseэти conceptsидея areгораздно aпроще lotпонять easierесли toпопытаться understandисполнить ifкод youиз canпримеров try out the code examples for yourself. If you've never programmed in Haskell before, it's not hard to get started! Download our Beginners Checklist to learn how!самостоятельно.