1

In the first hunk, filterF is implemented with foldMap

import Data.List

pred :: a -> Bool
pred = undefined

wrapperOfA :: (Applicative f, Monoid (f a)) => a -> Bool -> f a 
wrapperOfA a condition = if condition then pure a else mempty

-- foldMap :: (Foldable t, Monoid  f a) => (a -> f a) -> t a -> f a
filterF :: (Applicative f, Foldable t, Monoid (f a)) => (a -> Bool) -> t a -> f a 
filterF pred = foldMap ((<*>) wrapperOfA  pred)
filterF (<3) [5,4,3,2,1] :: [Int]
-- [2,1]

... which utilises some Applicative's apply function, aka <*> typically infixed. Now, the type of <*> is:

:t (<*>)
--(<*>) :: forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b

But replacing it with a hole gives its type as

-- (a0 -> Bool -> f0 a0) -> (a -> Bool) -> a -> f a
-- namely, from typechecking
filterF :: (Applicative f, Foldable t, Monoid (f a)) => (a -> Bool) -> t a -> f a 
filterF pred = foldMap (_ wrapperOfA pred)
-- Found hole ‘_’ with type: (a0 -> Bool -> f0 a0) -> (a -> Bool) -> a -> f a
-- Where: ‘a’ is a rigid type variable bound by the type signature for interactive:IHaskell136.filterF :: (Applicative f, Foldable t, Monoid (f a)) => (a -> Bool) -> t a -> f a at :1:12
--       ‘f’ is a rigid type variable bound by the type signature for interactive:IHaskell136.filterF :: (Applicative f, Foldable t, Monoid (f a)) => (a -> Bool) -> t a -> f a at :1:12
--       ‘a0’ is an ambiguous type variable
--       ‘f0’ is an ambiguous type variable
-- Relevant bindings include
--   pred :: a -> Bool (bound at :2:9)
--   filterF :: (a -> Bool) -> t a -> f a (bound at :2:1)
-- In the expression: _
-- In the first argument of ‘foldMap’, namely ‘(_ wrapperOfA pred)’
-- In the expression: foldMap (_ wrapperOfA pred)

Basically, wrapperOfA does not look like f (a -> b) as <*> would imply, nor does pred look like type f a. Yet it works and type checks - why?

dcl04
  • 89
  • 8

3 Answers3

4

The (<*>) there uses the Applicative instance for functions. In...

-- Writing it infix, for the sake of clarity. 
filterF pred = foldMap (wrapperOfA <*> pred)

... wrapperOfA has type a -> (Bool -> f a), and pred has type a -> Bool. That being so, wrapperOfA <*> pred has type a -> f a, as expected. If we substitute the implementation of (<*>) for functions (see the question linked to above for details), we get...

filterF pred = foldMap (\a -> wrapperOfA a (pred a))

... which makes it clear what is going on.

duplode
  • 33,731
  • 7
  • 79
  • 150
  • 1
    Writing it as `\a -> wrapperOfA a $ pred a` makes the translation more clear, that `(<*>)` is function application threaded with environment `a`: `wrapperOfA <*> pred` – Iceland_jack Sep 24 '17 at 20:50
0

<*> uses the Applicative instance for functions

 (<*>) :: f (a -> b) -> f a -> f b

where f is a (->) r , namely a function from the r domain.

Substituting, the first argument of <*> becomes a function from the r domain AND the a domain, then returning a b (a function of 2 arguments, since type r -> (a -> b) is equivalent to r -> a -> b), and the second argument becomes a function from the r domain returning an a.

To produce a (->) r b, we should apply the second-argument (a function) to the r parameter, and the resulting a is passed as a second argument to the thing in front of <*>. That is,

(<*>) wrapperOfA pred r = wrapperOfA r (pred r)

This leads me to a follow-up question/comment to bring the focus to foldMap and filterF which I now rename filterH.

    filterH :: (a -> Bool) -> t a -> h a
    foldMap :: (a -> f a) -> t a -> f a
    wrapperOfA <*> pred :: a -> h a

where h is any applicative and monoid with corresponding pure and mempty but is as yet undefined. So I needed to request a return type eg ::[Int] in order for it to compile&evaluate. Can filterH be used without having to specify my own type h? Am I missing something in this solution?

This is a question from the textbook "haskell programming from first principles", in the foldables section.

dcl04
  • 89
  • 8
  • 1
    I'm not sure if I get your follow-up question. You can use `filterH` without specifying a concrete choice of `h` in the same way you can use `(<*>) :: Applicative f => f (a -> b) -> f a -> f b` without specifying a concrete choice of `f`. – duplode Jun 14 '17 at 18:37
  • (By the way, it's better not to add follow-up questions to answers. The usual thing to do is using comments for that; or, if the question drifts too far from the original topic, asking a new question.) – duplode Jun 14 '17 at 18:41
0

Maybe you'll find easier this solution:

import Control.Conditional (select)

filterF :: (Applicative f, Foldable t, Monoid (f a)) => (a -> Bool) -> t a -> f a
filterF f = foldMap (select f pure mempty)

Here, select is just a functional if-then-else, where you provide functions for the condition, the true case and the else case.

Kutyel
  • 8,575
  • 3
  • 30
  • 61