I'm new to Haskell and trying to understand how does this work?
sequenceA [(+3),(+2),(+1)] 3
I have started from the definition
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
And then unfolded recursion into this
(:) <$> (+3) <*> $ (:) <$> (+2) <*> $ (:) <$> (+1) <*> pure []
(:) <$> (+3) <*> $ (:) <$> (+2) <*> $ (:) <$> (+1) <*> []
But here i don't understand for which applicative functor operator <*>
will be called, for ((->) r)
or for []
(:) <$> (+1) <*> []
Can somebody go step by step and parse sequenceA [(+3),(+2),(+1)] 3
step by step? Thanks.
Sequencing DNA means determining the order of the four chemical building blocks - called "bases" - that make up the DNA molecule. The sequence tells scientists the kind of genetic information that is carried in a particular DNA segment.
Next-generation sequencing involves three basic steps: library preparation, sequencing, and data analysis. Find resources to help you prepare for each step and see an example workflow for microbial whole-genome sequencing, a common NGS application.
DNA Sequencing is the method that determines the order of the four nucleotides bases (adenine, thymine, cytosine, and guanine) that make up the DNA molecule and convey important genetic information. In the DNA double helix, the four bases bond with the specific partner to form units called base pairs (bp).
This can be seen from the type of sequenceA:
sequenceA :: (Applicative f, Traversable t) => t (f a) -> f (t a)
The argument's outer type has to be a Traverable
, and its inner type has to be Applicative
.
Now, when you give sequenceA a list of functions (Num a) => [a -> a]
the list will be the Traversable
, and the things inside the list should be Applicative
. Therefore, it uses the applicative instance for functions.
So when you apply sequenceA to [(+3),(+2),(+1)]
, the following computation is built:
sequenceA [(+3),(+2),(+1)] = (:) <$> (+3) <*> sequenceA [(+2),(+1)]
sequenceA [(+2),(+1)] = (:) <$> (+2) <*> sequenceA [(+1)]
sequenceA [(+1)] = (:) <$> (+1) <*> sequenceA []
sequenceA [] = pure []
Let's look at the last line. pure []
takes an empty list and puts it inside some applicative structure. As we've just seen, the applicative structure in this case is ((->) r)
. Because of this, sequenceA [] = pure [] = const []
.
Now, line 3 can be written as:
sequenceA [(+1)] = (:) <$> (+1) <*> const []
Combining functions this way with <$>
and <*>
results in parallel application. (+1)
and const []
are both applied to the same argument, and the results are combined using (:)
Therefore sequenceA [(+1)]
returns a function that takes a Num a => a
type value, applies (+1)
to it, and then prepends the result to an empty list, \x -> (:) ((1+) x) (const [] x)
= \x -> [(+1) x]
.
This concept can be extended further to sequenceA [(+3), (+2), (+1)]
. It results in a function that takes one argument, applies all three functions to that argument, and combines the three results with (:)
collecting them in a list: \x -> [(+3) x, (+2) x, (+1) x]
.
it is using instance Applicative ((->) a)
.
Try this in ghci:
Prelude> :t [(+3),(+2),(+1)]
[(+3),(+2),(+1)] :: Num a => [a -> a]
Prelude> :t sequenceA
sequenceA :: (Applicative f, Traversable t) => t (f a) -> f (t a)
and pattern match the argument type: t = [], f = (->) a
and the Applicative constraint is on f.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With