Consider
filterM (\x -> [True, False]) [1, 2, 3]
I just cannot understand the magic that Haskell does with this filterM
use case. The source code for this function is listed below:
filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
filterM _ [] = return []
filterM p (x:xs) = do
flg <- p x
ys <- filterM p xs
return (if flg then x:ys else ys)
With this use case, p
should be the lambda function (\x -> [True, False])
, and the first x
should be 1
. So what does flg <- p x
return? What exactly is the value of flg
for each recursion?
filtration, the process in which solid particles in a liquid or gaseous fluid are removed by the use of a filter medium that permits the fluid to pass through but retains the solid particles. Either the clarified fluid or the solid particles removed from the fluid may be the desired product.
The four primary types of filters include the low-pass filter, the high-pass filter, the band-pass filter, and the notch filter (or the band-reject or band-stop filter).
There are seven commonly used home air filter types: HEPA filters. UV light filters. Electrostatic filters.
The order of a filter is given as an integer value and is derived from the filter's transfer function. As an example, all other factors being equal, a fourth-order filter will roll off twice as fast as a second-order filter, and four times faster than a first-order unit.
By comparing the type signatures of filter and filterM we can see that filterM is just filter where the conditional expression yields a Bool within a context m and where the matching results are aggregated in the m context. The implementation of filterM in GCH base is as follows:
A filter is a circuit capable of passing (or amplifying) certain frequencies while attenuating other frequencies. Thus, a filter can extract important frequencies from signals that also contain undesirable or irrelevant frequencies. In the field of electronics, there are many practical applications for filters.
Four Major Types of Filters. The four primary types of filters include the low-pass filter, the high-pass filter, the band-pass filter, and the notch filter (or the band-reject or band-stop filter ). Take note, however, that the terms "low" and "high" do not refer to any absolute values of frequency, but rather they are relative values ...
Analog-to-digital conversion: Filters are placed in front of an ADC input to minimize aliasing. The four primary types of filters include the low-pass filter, the high-pass filter, the band-pass filter, and the notch filter (or the band-reject or band-stop filter ).
The list monad []
models non-determinism: a list of values [a]
represents a number of different possibilities for the value of a
.
When you see a statement like flg <- p x
in the list monad, flg
will take on each value of p x
in turn, i.e. True
and then False
in this case. The rest of the body of filterM
is then executed twice, once for each value of flg
.
To see how this happens in more detail, you need to understand the desugaring of do
notation and the implementation of the (>>=)
operator for the list monad.
do
notation gets desugared line-by-line into calls to the (>>=)
operator. For example the body of the non-empty filterM
case turns into
p x >>= \flg -> (filterM p xs >>= \ys -> return (if flg then x:ys else ys))
This is completely mechanical as it's in essence just replacing flg <-
before the expression with >>= \flg ->
after the expression. In reality pattern-matching makes this a little more complicated, but not much.
Next is the actual implementation of (>>=)
, which is a member of the Monad
type class and has a different implementation for each instance. For []
, the type is:
(>>=) :: [a] -> (a -> [b]) -> [b]
and the implementation is something like
[] >>= f = []
(x:xs) >>= f = f x ++ (xs >>= f)
So the loop happens in the body of (>>=)
. This is all in a library, no compiler magic beyond the desugaring of the do
notation.
An equivalent definition for (>>=)
is
xs >>= f = concat (map f xs)
which may also help you see what's happening.
The same thing then happens for the recursive call to filterM
: for each value of flg
, the recursive call is made and produces a list of results, and the final return
statement is executed for each element ys
in this list of result.
This "fan-out" on each recursive call leads to 2^3 = 8
elements in the final result of filterM (\x -> [True, False]) [1, 2, 3]
.
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