Efekty
Odpřednášena v úterý 18. 5.
Byl zde zadán #U22
Functor
Ze serveru stahujete data typu
data Video = Video { length :: Int, authors :: List String, year :: Int }
Nevíte ale, zda se vám jej opravdu povedlo stáhnout, jinými slovy, máte jen Maybe Video
. Napište nyní funkce:
addToAuthors :: String -> Maybe Video -> Maybe Video
changeIn :: (Video -> Video) -> Maybe Video -> Maybe Video
- jak byste přepsali
addToAtuhors
pomocíchangeIn
- jak byste přepsali
runOverMaybe :: forall b. (Video -> b) -> Maybe Video -> Maybe b
Přepište všechny funkce výše pomocí posledního runOverMaybeVideo
.
Kromě videa máte také k dispozici celý playlist List Video
. Napište následující funkce:
getYears :: List Video -> List Int
getMergedAuthors :: List Video -> List String
runOverList :: forall b. (Video -> b) -> List Video -> List b
Přepište věci výše pomocí runOverListVideo
.
Porovnejme
runOverMaybeVideo :: forall b. (Video -> b) -> Maybe Video -> Maybe b
runOverListVideo :: forall b. (Video -> b) -> List Video -> List b
Očividně se taková funkce hodí pro různé "Containery", můžeme zobecnit na
runOverMaybeVideo :: forall b. (Video -> b) -> Maybe Video -> Maybe b
runOverListVideo :: forall b. (Video -> b) -> List Video -> List b
runOverVideo :: forall f b. (Video -> b) -> f Video -> f b
Jak by vypadal typ runOverStringy
? Nebo runOverNumber
?
runOverVideo :: forall f b. (Video -> b) -> f Video -> f b
runOverString :: forall f b. (String -> b) -> f String -> f b
runOverNumber :: forall f b. (Number -> b) -> f Number -> f b
Šlo by to nějak dál zobecnit, abychom nemuseli mít tolik runOver
-ů? Ano!
runOverVideo :: forall f b. (Video -> b) -> f Video -> f b
runOverString :: forall f b. (String -> b) -> f String -> f b
runOverNumber :: forall f b. (Number -> b) -> f Number -> f b
runOver :: forall f a b. (a -> b) -> f a -> f b
Tento runOver
je v Purescriptu a Haskellu známý jako map
, popř. infixně jako <$>
, a je kolem něj postavena typová třída Functor
. Jinými slovy, kdo chce být instancí Functor
, musí nějak implementovat tento map
.
Shrnutí
- Low:
Functor
je vše, na co může volatmap
- Middle:
Functor
je druh krabice, z které vždy nějakou věc (typua
) vytáhnu, zavolám na ni funkcia -> b
, a vrátím novou hodnotu (typub
) zpět na původní místo. - High:
Functor
, potažmomap
, se stará o to, aby u typů zůstaly zachovány efekty.
Apply + Applicative
(1) Máte k dispozici funkci checkNatural :: Int -> Maybe Natural
, která kontroluje, zda vaše číslo je či není přirozené. Použijte tuto funkci k tomu, abyste ověřili věk psa.
data Dog = MakeDog { age :: Natural }
-- MakeDog je datový konstruktor: funkce :: Natural -> Dog
checkAge :: Int -> Maybe User
(2) Máte ověřit, zda uživatel při registraci zadal mail a datum narození ve správném formátu. Vašim úkolem je napsat funkci checkInput :: String -> String -> Maybe User
pro typ
data User = MakeUser { email :: Email, born :: Date } -- MakeUser je funkce :: Email -> Date -> User
K dispozici máte funkce checkEmail :: String -> Maybe Email
a checkDate :: String -> Maybe Date
a chcete je vhodně zkombinovat.
Narazili jsme na problém — nemůžeme použít map
, protože máme funkci Maybe (Date -> User)
, zatímco pro map
bychom potřebovali jen (Date -> User)
. Hodila by se nám funkce
something :: forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
potažmo v zobecněné podobě
something :: forall f a b. f (a -> b) -> f a -> f b
Tato funkce v Purescriptu už naštěstí existuje. Najděte její jméno a infixní alias (využijte název této sekce).
Shrnutí
- Low: Typy z
Apply
mají funkci(<*>) :: f (a -> b) -> f a -> f b
, která vlastně takový rozšířenýmap
. Oprotimap
nám dovoluje, aby i aplikovaná funkce byla zabalena v nějakémf
. - Middle:
Apply
je podobně jakoFunctor
druh krabice — dovoluje nám však mít v krabici i našia -> b
funkci, kterou vždy spolu sa
vytáhneme z krabice, spočítámeb
a výsledek strčíme zpět do krabice. - High:
Apply
nám pomáhá řetězit efekty. "Vytahování z krabice" je vlastně "spouštění efektu" aApply
se stará o to, že spustíme nejprve efekt naší funkcea -> b
, poté efekt hodnotya
, a nakonec tento spojený efekt přeneseme na hodnotub
.
Většinou se místo Apply
používá rovnou Applicative
, což je její podtřída, která definuje ještě zabalovací funkci pure :: a -> f a
.
class Apply f <= Applicative f where pure :: forall a. a -> f a