Int
can behave like a multitude of entities — a compared entity, an ordered entity, an enumerated entity, and so on.Maybe
values, and trees. In Haskell, they are described by the Functor
type class, which contains only one type class method: fmap
. fmap
is of type fmap :: (a -> b) -> fa -> fb
, which says, “Give me a function that accepts a and returns b and a box containing a (or several a) inside, and I will return the box with b
(or several b
) inside. ”It applies the function to the item inside the box.Maybe
values ​​have an additional context that the calculations might have failed. In relation to lists, the context is that the value may be multiple or absent. fmap
applies a function to a value, keeping its context.Functor
, it must be * -> *
, which means that it takes exactly one particular type as a type parameter. For example, Maybe
can be made an instance, since it takes one type parameter to produce a particular type, such as Maybe Int
or Maybe String
. If the type constructor takes two parameters, like Either,
we must partially apply the type constructor until it takes only one parameter. Therefore, we cannot write Functor Either where
, but we can write Functor (Either a) where
. Then, if we imagined that fmap
intended only for working with Either a
, it would have the following type description: fmap :: (b -> c) -> Either ab -> Either ac
Either a
part is fixed because Either a
takes only one type parameter.Functor
: []
and Maybe
, Either a
, and also the type Tree
that we created in Chapter 7. You saw how you can display them using functions for the sake of good. Now let's take a look at the IO
instance.IO String
type has any value, it means that it is an I / O action that will go out into the real world and get some string for us, which it then returns as a result. We can use <-
in the do syntax to bind this result to a name. In Chapter 8, we talked about how I / O actions look like boxes with small legs that go outside and get some value for us from the outside world. We can see what they brought, but after viewing we need to wrap the value back into IO
. Considering this analogy of boxed legs, you can understand how IO
acts as a functor.IO
is an instance of Functor
. When we use fmap
to display an I / O action with a function, we want to get back an I / O action that does the same thing, but our function applies to its resulting value. Here is the code: instance Functor IO where fmap f action = do result <- action return (f result)
do
syntax to glue the two actions together and create one new one. In the implementation for fmap
we create a new I / O action that first performs the initial I / O action, giving the result the name result
. Then we execute return (f result)
. Recall that return
is a function that creates an I / O action that does nothing, but only returns something as its result.do
block produces will always return the resulting value of its last action. That's why we use return
to create an I / O action that doesn’t really do anything, but simply returns f result
as the result of a new I / O action. Take a look at this piece of code: main = do line <- getLine let line' = reverse line putStrLn $ "You said " ++ line' ++ " backwards!" putStrLn $ "Yes, you really said" ++ line' ++ " backwards!
fmap
: main = do line <- fmap reverse getLine putStrLn $ "You said " ++ line ++ " backwards!" putStrLn $ "Yes, you really said" ++ line ++ " backwards!"
Just "blah"
using fmap reverse
, getting Just "halb"
, we can display getLine
using fmap reverse
. getLine
is an I / O action that has an IO String
type and displaying it with reverse
gives us an I / O action that will go out into the real world and get a string, and then apply reverse
to its result. In the same way that we can apply a function to what's inside the Maybe
box, we can apply the function to what's inside the IO
box, but it must go to the real world to get something. Then, when we bind the result to a name using <-
, the name will reflect the result to which reverse
has already been applied.fmap (++"!") getLine
I / O operation of fmap (++"!") getLine
behaves exactly like getLine
, except that the result is always added to the "!"
In the end!fmap
worked only with IO
, it would have the type fmap :: (a -> b) -> IO a -> IO b
. fmap
takes a function and an I / O operation, and returns a new I / O operation, similar to the old one, except that a function is applied to the result contained in it.fmap
. If you want to apply several functions to some data inside a functor, you can declare your function at the top level, create a lambda function, or, ideally, use a composition of functions: import Data.Char import Data.List main = do line <- fmap (intersperse '-' . reverse . map toUpper) getLine putStrLn line
$ runhaskell fmapping_io hello there EREHT- -OLLEH
intersperse '-' . reverse . map toUpper
function intersperse '-' . reverse . map toUpper
intersperse '-' . reverse . map toUpper
intersperse '-' . reverse . map toUpper
takes a string, displays it with toUpper
, applies reverse
to this result, and then applies intersperse '-'
to this result. This is a more beautiful way to write the following code:(\xs -> intersperse '-' (reverse (map toUpper xs)))
Functor
that we have been dealing with all the time is (->) r
. Stand still What the hell does (->) r
mean? The type of the function r -> a
can be rewritten as (->) ra
, just as we can write 2 + 3
as (+) 2 3
. When we perceive it as (->) ra
, we see (->)
slightly different light. It's just a type constructor that takes two type parameters, as Either
does.Functor
. That is why we cannot make (->)
instance of Functor
; however, if you partially apply it before (->) r
, this does not pose any problems. If the syntax would allow partial use of type constructors using sections (as we can use +
by doing (2+)
, which is similar to (+) 2
), you could write (->) r
as (r->)
.Control.Monad.Instances
. instance Functor ((->) r) where fmap fg = (\x -> f (gx))
fmap
type: fmap :: (a -> b) -> fa -> fb
f
, which is the role that our copy of the functor plays, with (->) r
. This allows us to understand how fmap
should behave in the case of this particular instance. Here is the result: fmap :: (a -> b) -> ((->) ra) -> ((->) rb)
(->) ra
and (->) rb
in infix form, i.e.r -> a
and r -> b
, as we usually do with functions: fmap :: (a -> b) -> (r -> a) -> (r -> b)
Maybe
displaying with a function should produce Maybe
, and displaying a list with a function should produce a list. What does the previous type tell us? We see that it takes a function from a
to b
and a function from r
to a
and returns a function from r
to b
. Does this remind you of anything? Yes, the composition of functions! We attach the output r -> a
to the input a -> b
to get the function r -> b
, which is exactly the composition of the functions. Here is another way to record this instance: instance Functor ((->) r) where fmap = (.)
fmap
to functions is simply a composition of functions. In the script, import Control.Monad.Instances
, since this is the module where this instance is defined, then load the script and try to play with the display of functions: ghci> :t fmap (*3) (+100) fmap (*3) (+100) :: (Num a) => a -> a ghci> fmap (*3) (+100) 1 303 ghci> (*3) `fmap` (+100) $ 1 303 ghci> (*3) . (+100) $ 1 303 ghci> fmap (show . (*3)) (*100) 1 "300"
fmap
as an infix function to make a similarity with .
was obvious. In the second input line, we display (+100)
with (*3)
, which gives a function that accepts input, applies it (+100)
, and then applies (*3)
to this result. Then we apply this function to 1
.(+3)
, we can consider the value as the final result of the function, and the context is that we have to apply this function to something to get the result. Applying fmap (*3)
to (+100)
will create another function that acts the same as (+100)
, but before returning the result, (*3)
will be applied to this result.fmap
is a composition of functions when applied to functions is not too useful at the moment, but at least it is very interesting. It also changes our consciousness a little and allows us to see how entities that act more like calculations than boxes ( IO
and (->) r
) can be functors. The display of the calculation using the function returns the same type of calculation, but the result of this calculation is changed by the function.fmap
should follow, let's think again about the type of fmap
: fmap :: (a -> b) -> fa -> fb
a -> b -> c
actually takes only one parameter of type a
, and then returns the function b -> c
, which takes one parameter and returns c
. That is why calling a function with an insufficient number of parameters (its partial application) returns us back to a function that takes several parameters that we missed (if we again perceive functions as if they accept several parameters). Therefore, a -> b -> c
can be written as a -> (b -> c)
to make currying more obvious.fmap :: (a -> b) -> (fa -> fb)
, we can take fmap
not as a function that takes one function and a value of a functor and returns the value of a functor, but as a function which takes a function and returns a new function that is the same as the previous one, except that it takes the value of the functor as a parameter and returns the value of the functor as a result. It takes the function a -> b
and returns the function fa -> fb
. This is called "lifting function." Let's play with this idea using the command :t
in GHCi: ghci> :t fmap (*2) fmap (*2) :: (Num a, Functor f) => fa -> fa ghci> :t fmap (replicate 3) fmap (replicate 3) :: (Functor f) => fa -> f [a]
fmap (*2)
expression is a function that takes a functor f
over numbers and returns a functor over numbers. This functor can be a list, the value Maybe
, Either String
, or something else. The fmap (replicate 3)
expression fmap (replicate 3)
will get a functor over any type and return the functor over the list of elements of this type. This becomes even more obvious if we partially apply, say, fmap (++"!")
, And then assign it to a name in GHCi.fmap
two ways:fmap (replicate 3) :: (Functor f) => fa -> f [a]
means that the function will work with any functor. What exactly it will do depends on the functor. If we apply fmap (replicate 3)
to the list, the fmap
implementation will be chosen for the list, i.e. just map
. If we apply it to Maybe a
, it will apply replicate 3
to the value inside Just
. If this value is Nothing
, then it will remain Nothing
. Here are some examples: ghci> fmap (replicate 3) [1,2,3,4] [[1,1,1],[2,2,2],[3,3,3],[4,4,4]] ghci> fmap (replicate 3) (Just 4) Just [4,4,4] ghci> fmap (replicate 3) (Right "blah") Right ["blah","blah","blah"] ghci> fmap (replicate 3) Nothing Nothing ghci> fmap (replicate 3) (Left "foo") Left "foo"
fmap
to a functor should only display a functor using a function — nothing more. This behavior is described in the laws of functors. All copies of Functor
must follow these two laws. Haskell does not force these laws to be carried out automatically, so you should check them yourself when creating a functor. All Functor
instances in the standard library obey these laws.id
to the value of a functor, then the value of the functor we get should be the same as the original value of the functor. In a bit more formal, this means that fmap id = id
. Essentially, it says that if we apply fmap id
to the value of a functor, it should be the same as simply applying id
to the value. Recall that id
is an identity function that simply returns its parameter unchanged. It can also be written as \x -> x
. If we take the value of a functor as something that can be displayed, then the law fmap id = id
looks rather trivial and obvious. ghci> fmap id (Just 3) Just 3 ghci> id (Just 3) Just 3 ghci> fmap id [1..5] [1,2,3,4,5] ghci> id [1..5] [1,2,3,4,5] ghci> fmap id [] [] ghci> fmap id Nothing Nothing
fmap
, for example, for Maybe
, we can see why the first law of functors is true: instance Functor Maybe where fmap f (Just x) = Just (fx) fmap f Nothing = Nothing
id
plays the role of the parameter f
in this implementation. We see that if we apply fmap id
to Just x
, the result will be Just (id x)
, and since id
simply returns its parameter, we can conclude that Just (id x)
is equal to Just x
. Therefore, we now know that if we apply id
to the Maybe
value created using the Just
value constructor, we get the same value back.id
to Nothing
returns the same value is trivial. Therefore, from these two equalities in the implementation of fmap
we see that the law fmap id = id
is observed.fmap (f . g) = fmap f . fmap g
fmap (f . g) = fmap f . fmap g
. Or if we write it differently, then for any value of the functor x
following should be fmap (f . g) x = fmap f (fmap gx)
: fmap (f . g) x = fmap f (fmap gx)
.fmap
to it, nothing happens behind the scenes except the display, and that it will act as an entity that can be displayed — that is, a functor.fmap
for that type, and then using the method we used to check if Maybe
obeys the first law. So, to test how the second law of functors holds for Maybe
, if we apply fmap (f . g)
to Nothing
, we get Nothing
, because applying any function to Nothing
gives Nothing
. If we do fmap f (fmap g Nothing)
, we get Nothing
for the same reasons.Maybe
when the value is Nothing
. , Just
? , fmap (f . g) (Just x)
, , Just ((f . g) x)
, Just (f (gx))
. fmap f (fmap g (Just x))
, , fmap g (Just x)
Just (gx)
. , fmap f (fmap g (Just x))
fmap f (Just (gx))
, , Just (f (gx))
. data CMaybe a = CNothing | CJust Int a deriving (Show)
C
. , Maybe a, Just
. CJust
Int
, . a
, , , CMaybe a
. : ghci> CNothing CNothing ghci> CJust 0 "haha" CJust 0 "haha" ghci> :t CNothing CNothing :: CMaybe a ghci> :t CJust 0 "haha" CJust 0 "haha" :: CMaybe [Char] ghci> CJust 100 [1,2,3] CJust 100 [1,2,3]
CNothing
, . CJust
, , — . Functor
, , fmap
, , 1
. instance Functor CMaybe where fmap f CNothing = CNothing fmap f (CJust counter x) = CJust (counter+1) (fx)
Maybe
, fmap
, ( CJust
), , 1
. . : ghci> fmap (++"ha") (CJust 0 "ho") CJust 1 "hoha" ghci> fmap (++"he") (fmap (++"ha") (CJust 0 "ho")) CJust 2 "hohahe" ghci> fmap (++"blah") CNothing CNothing
ghci> fmap id (CJust 0 "haha") CJust 1 "haha" ghci> id (CJust 0 "haha") CJust 0 "haha"
id
. , CMaybe
. Functor
, , , .CMaybe
, , . , , , , . CMaybe
, , . ! , CMaybe
, , Int , fmap
.fmap
— . , , , , , , .Functor
, , , . , , . , , , .Just 3
fmap (*) (Just 3)
, ? Maybe
Functor
, Just
, Just
. fmap (*) (Just 3)
Just ((*) 3)
, Just (3 *)
, . Interesting! , Just
! ghci> :t fmap (++) (Just "hey") fmap (++) (Just "hey") :: Maybe ([Char] -> [Char]) ghci> :t fmap compare (Just 'a') fmap compare (Just 'a') :: Maybe (Char -> Ordering) ghci> :t fmap compare "A LIST OF CHARS" fmap compare "A LIST OF CHARS" :: [Char -> Ordering] ghci> :t fmap (\xyz -> x + y / z) [3,4,5,6] fmap (\xyz -> x + y / z) [3,4,5,6] :: (Fractional a) => [a -> a -> a]
compare
, (Ord a) => a -> a -> Ordering
, Char -> Ordering
, compare
. (Ord a) => a -> Ordering
, a
Char
, a
, Char
. ghci> let a = fmap (*) [1,2,3,4] ghci> :ta a :: [Integer -> Integer] ghci> fmap (\f -> f 9) a [9,18,27,36]
Just (3 *)
Just 5
, Just (3 *)
Just 5
? , . , , \f -> f 9
, . , fmap
, , , . Just
, Just 5
, , .Control.Applicative
. : pure
<*>
. - , , - . : class (Functor f) => Applicative f where pure :: a -> fa (<*>) :: f (a -> b) -> fa -> fb
Applicative
, . , Applicative
, , , Functor
. , Applicative
, Functor
, fmap
.pure
. pure :: a -> fa
. f
. , , — , , — .pure
. « », , , . a -> fa
. , . pure
— , ( ) — , - .<*>
. : f (a -> b) -> fa -> fb
fmap :: (a -> b) -> fa -> fb
. <*>
fmap
. fmap
, , <*>
, , , , .Applicative
Maybe
: instance Applicative Maybe where pure = Just Nothing <*> _ = Nothing (Just f) <*> something = fmap f something
f
, , , instance Applicative Maybe where
instance Applicative (Maybe a) where
.pure
. , - . pure = Just
, Just
. pure x = Just x
.<*>
. Nothing
, . , Nothing
, Nothing
.Applicative
Functor
, , , <*>
. Nothing
, Just
, , . , Nothing
, Nothing
, fmap
, Nothing
. , Maybe
<*>
, Just
, . - Nothing
, Nothing
. ghci> Just (+3) <*> Just 9 Just 12 ghci> pure (+3) <*> Just 10 Just 13 ghci> pure (+3) <*> Just 9 Just 12 ghci> Just (++"hahah") <*> Nothing Nothing ghci> Nothing <*> Just "woot" Nothing
pure (+3)
Just (+3)
— . pure
, Maybe
( <*>
); Just
.Nothing
, - , Nothing
.Applicative
<*>
, , . , , : ghci> pure (+) <*> Just 3 <*> Just 5 Just 8 ghci> pure (+) <*> Just 3 <*> Nothing Nothing ghci> pure (+) <*> Nothing <*> Just 5 Nothing
+
, <*>
, , .<*>
, , : pure (+) <*> Just 3 <*> Just 5
(pure (+) <*> Just 3) <*> Just 5
+
— Maybe
, . , pure (+)
, Just (+)
. , Just (+) <*> Just 3
. Just (3+)
. - . 3
+
, 3
. , Just (3+) <*> Just 5
, Just 8
.pure f <*> x <*> y <*> ...
, , , . , , <*>
.pure f <*> x
fmap fx
. . , , . pure
. , , , . pure f <*> x <*> y <*> ...
, fmap fx <*> y <*> ...
. Control.Applicative
, <$>
, fmap
. : (<$>) :: (Functor f) => (a -> b) -> fa -> fb f <$> x = fmap fx
f
, , , f
, Functor
. f
, x
. , f
, .<$>
, f
, f <$> x <*> y <*> z
. , fxyz
.Just "johntra"
Just "volta"
, Maybe
. : ghci> (++) <$> Just "johntra" <*> Just "volta" Just "johntravolta"
ghci> (++) "johntra" "volta" "johntravolta"
<$>
<*>
, . ?(++) <$> Just "johntra" <*> Just "volta"
: (++)
, (++) :: [a] -> [a] -> [a]
Just "johntra"
. , Just ("johntra"++)
, Maybe ([Char] -> [Char])
. , (++)
a
Char
. Just ("johntra"++) <*> Just "volta"
, Just
Just "volta"
, Just "johntravolta"
. Nothing
, Nothing
.[]
) . ! []
Applicative
: instance Applicative [] where pure x = [x] fs <*> xs = [fx | f <- fs, x <- xs]
pure
. , , . , , , pure
. pure
. , Maybe
Nothing
, , pure
Maybe
Just
.pure
: ghci> pure "Hey" :: [String] ["Hey"] ghci> pure "Hey" :: Maybe String Just "Hey"
<*>
? <*>
, (<*>) :: [a -> b] -> [a] -> [b]
. . <*>
- , . , , , . . . .<*>
: ghci> [(*0),(+100),(^2)] <*> [1,2,3] [0,0,0,101,102,103,1,4,9]
ghci> [(+),(*)] <*> [1,2] <*> [3,4] [4,5,5,6,3,4,6,8]
<*>
, [(+),(*)] <*> [1,2]
, , [(1+),(2+),(1*),(2*)]
, . [(1+),(2+),(1*),(2*)] <*> [3,4]
, . ghci> (++) <$> ["ha","heh","hmm"] <*> ["?","!","."] ["ha?","ha!","ha.","heh?","heh!","heh.","hmm?","hmm!","hmm."]
100
"what"
, , [1,2,3]
, , , . - (+) <$> [1,2,3] <*> [4,5,6]
, +
, , .[2,5,10]
[8,10,11]
, : ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]] [16,20,22,40,50,55,80,100,110]
ghci> (*) <$> [2,5,10] <*> [8,10,11] [16,20,22,40,50,55,80,100,110]
*
. , 50
, : ghci> filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11] [55,80,100,110]
pure f <*> xs
fmap f xs
. pure f
— [f]
, [f] <*> xs
, , .Applicative
, , IO
. : instance Applicative IO where pure = return a <*> b = do f <- a x <- b return (fx)
pure
, , , pure
— return
. return
-, . - , - .<*>
IO
, (<*>) :: IO (a -> b) -> IO a -> IO b
. IO
- a
, , - f
. - b
x
. , f
x
. do
. (, do
, - .)Maybe
[]
<*>
, . IO
, , - . -, , -, . : myAction :: IO String myAction = do a <- getLine b <- getLine return $ a ++ b
getLine
return
, - a ++ b
. : myAction :: IO String myAction = (++) <$> getLine <*> getLine
getLine
— -, getLine :: IO String
. <*>
, , .getLine
, . (++) <$> getLine <*> getLine
, ' , , .(++) <$> getLine <*> getLine
IO String
. , -, , , -. : main = do a <- (++) <$> getLine <*> getLine putStrLn $ "The two lines concatenated turn out to be: " ++ a
Applicative
(->) r
. , , , . instance Applicative ((->) r) where pure x = (\_ -> x) f <*> g = \x -> fx (gx)
pure
, , , . - . pure
, . pure
(->) r
pure :: a -> (r -> a)
. ghci> (pure 3) "blah" 3
ghci> pure 3 "blah" 3
<*>
, , : ghci> :t (+) <$> (+3) <*> (*100) (+) <$> (+3) <*> (*100) :: (Num a) => a -> a ghci> (+) <$> (+3) <*> (*100) $ 5 508
<*>
, , . What is going on here? (+) <$> (+3) <*> (*100)
, , +
(+3)
(*100)
. (+) <$> (+3) <*> (*100) $ 5
, (+3)
(*100)
5
, 8
500
. +
8
500
, 508
. ghci> (\xyz -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5 [8.0,10.0,2.5]
\xyz -> [x, y, z]
, (+3)
, (*2)
(/2)
. 5
, \xyz -> [x, y, z]
.(->) r
Applicative
, , . , , .<*>
, .[(+3),(*2)] <*> [1,2]
, (+3)
1
2
, (*2)
1
2
, : [4,5,2,4]
. , [(+3),(*2)] <*> [1,2]
, , , . . : [4,4]
. [1 + 3, 2 * 2]
.Applicative
, , ZipList
, Control.Applicative
.ZipList a
, (ZipList)
(). : instance Applicative ZipList where pure x = ZipList (repeat x) ZipList fs <*> ZipList xs = ZipList (zipWith (\fx -> fx) fs xs)
<*>
, — , . . zipWith (\fx -> fx) fs xs
. zipWith
, .pure
. , . pure "haha"
ZipList (["haha","haha","haha"...
. , , pure
, - . , - . , . , pure f <*> xs
fmap f xs
. pure 3
ZipList [3]
, pure (*2) <*> ZipList [1,5,10]
ZipList [2]
, . , .ZipList a
Show
, getZipList
: ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100,100] [101,102,103] ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100..] [101,102,103] ghci> getZipList $ max <$> ZipList [1,2,3,4,5,3] <*> ZipList [5,3,1,2] [5,3,3,4] ghci> getZipList $ (,,) <$> ZipList "dog" <*> ZipList "cat" <*> ZipList "rat" [('d','c','r'),('o','a','a'),('g','t','t')]
(,,)
— , \xyz -> (x,y,z)
. , (,)
— , \xy -> (x,y)
.zipWith
, zipWith3
, zipWith4
, 7
. zipWith
, , . zipWith3
, , , . . , . , .pure f <*> x = fmap fx
. . :pure id <*> v = v
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
pure f <*> pure x = pure (fx)
u <*> pure y = pure ($ y) <*> u
Control.Applicative
, liftA2
: liftA2 :: (Applicative f) => (a -> b -> c) -> fa -> fb -> fc
liftA2 :: (Applicative f) => (a -> b -> c) -> fa -> fb -> fc liftA2 fab = f <$> a <*> b
(a -> b -> c) -> (fa -> fb -> fc)
. , , liftA2
, .Just 3
Just 4
. , , : ghci> fmap (\x -> [x]) (Just 4) Just [4]
Just 3
Just [4]
. Just [3,4]
? : ghci> liftA2 (:) (Just 3) (Just [4]) Just [3,4] ghci> (:) <$> Just 3 <*> Just [4] Just [3,4]
:
— , . , Just [3,4]
, Just 2
, Just [2,3,4]
? , . , , .sequenceA
: sequenceA :: (Applicative f) => [fa] -> f [a] sequenceA [] = pure [] sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
x
— , xs
— , ), sequenceA
, . , x
, , , ! sequenceA [Just 1, Just 2]
(:) <$> Just 1 <*> sequenceA [Just 2]
(:) <$> Just 1 <*> ((:) <$> Just 2 <*> sequenceA [])
sequenceA []
Just []
, : (:) <$> Just 1 <*> ((:) <$> Just 2 <*> Just [])
(:) <$> Just 1 <*> Just [2]
Just [1,2]
!sequenceA
— . , , , : sequenceA :: (Applicative f) => [fa] -> f [a] sequenceA = foldr (liftA2 (:)) (pure [])
pure []
. liftA2 (:)
, , . liftA2 (:)
, . ., , , . ghci> sequenceA [Just 3, Just 2, Just 1] Just [3,2,1] ghci> sequenceA [Just 3, Nothing, Just 1] Nothing ghci> sequenceA [(+3),(+2),(+1)] 3 [6,5,4] ghci> sequenceA [[1,2,3],[4,5,6]] [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] ghci> sequenceA [[1,2,3],[4,5,6],[3,4,4],[]] []
Maybe
, sequenceA
Maybe
, . Nothing
, Nothing
. , Maybe
, Nothing
.sequenceA
, . , , . sequenceA [(+3),(+2),(+1)] 3
(+3)
3
, (+2)
— 3
, (+1)
— 3
, .(+) <$> (+3) <*> (*2)
, , (+3)
(*2)
, +
. , , sequenceA [(+3),(*2)]
, . +
:
pure []
, .sequenceA
, , . , , . : ghci> map (\f -> f 7) [(>4),(<10),odd] [True,True,True] ghci> and $ map (\f -> f 7) [(>4),(<10),odd] True
Bool
True
, True
. — sequenceA
: ghci> sequenceA [(>4),(<10),odd] 7 [True,True,True] ghci> and $ sequenceA [(>4),(<10),odd] 7 True
[(>4),(<10),odd]
, . (Num a) => [a -> Bool]
(Num a) => a -> [Bool]
. , ?[ord, (+3)]
, ord
, (+3)
.[]
, sequenceA
. , . , , sequenceA
and then executed using a list generator: ghci> sequenceA [[1,2,3],[4,5,6]] [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] ghci> [[x,y] | x <- [1,2,3], y <- [4,5,6]] [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] ghci> sequenceA [[1,2],[3,4]] [[1,3],[1,4],[2,3],[2,4]] ghci> [[x,y] | x <- [1,2], y <- [3,4]] [[1,3],[1,4],[2,3],[2,4]] ghci> sequenceA [[1,2],[3,4],[5,6]] [[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]] ghci> [[x,y,z] | x <- [1,2], y <- [3,4], z <- [5,6]] [[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
(+) <$> [1,2] <*> [4,5,6]
x + y
, x
[1,2]
, y
[4,5,6]
. , . , sequenceA [[1,2],[3,4],[5,6]]
, [x,y,z]
, x
[1,2]
, y
[3,4]
. . , . .sequenceA
— , sequence
! - -, -. , [IO a]
IO [a]
, -, , - , , . -, .getLine
: ghci> sequenceA [getLine, getLine, getLine] heyh ho woo ["heyh","ho","woo"]
<$>
<*>
, , .Source: https://habr.com/ru/post/123767/
All Articles