1

Reading Function application with $ in Learn You Haskell book I found example of applying $ to list of functions.

map ($ 3) [(4+), (10*), (^2), sqrt]

I wanted try similar thing and reduce example to applying to one function

fmap ($ 3) (4+) 

But I am getting errors which I don't understand

• Non type-variable argument in the constraint: Num (a -> b)
  (Use FlexibleContexts to permit this)
• When checking the inferred type
    it :: forall a b. (Num (a -> b), Num a) => (a -> b) -> b

Could you help me understand why it works in the first case but it doesn't in the second? How can I achieve desired effect?

Thank you

  • 1
    Minor clarification: I get a different error when I try `fmap ($ 3) (4+)` in GHCi ("Non type-variable argument in the constraint [...]"). Are you using GHC 7.10 or later (cf. [this question](https://stackoverflow.com/q/31503707/2751851) and the P.S. to the accepted answer)? If so, could you please double-check what the error message is? – duplode Apr 04 '18 at 13:12
  • Thank you @duplode for clarification question. I am using ghc 8.0.2. I am getting now same error as you wrote. I will update description. Maybe I copied wrong error from previous experiments with getting this right. – Šefčíková Jana Apr 04 '18 at 15:12
  • Both work as you want in my GHCi. The only difference is the second argument which must be a list, not a tuple. `map ($ 3) [(4 + )]` and `fmap ($ 3) [(4 + )]` You can have a wrapper function to make a tuple into a partial function in a list if desired. – fp_mora Apr 05 '18 at 19:50

3 Answers3

6

You need map in the first example because you have a whole container full of functions, and each of them you want to apply to the number. In that example, you could indeed also replace map with fmap, which does work on any container (on any functor).

Prelude> fmap ($ 3) [(4+), (10*), (^2), sqrt]    -- list functor
[7.0,30.0,9.0,1.7320508075688772]
Prelude> fmap ($ 3) (Just (4+))                  -- `Maybe` functor
Just 7
Prelude> fmap ($ 3) (do y<-readLn; return (y+))  -- `IO` functor
> 100
103

However, (4+) by itself is not a function wrapped in any functor, it's just a function by itself. So, you don't really need any fmap:

Prelude> ($ 3) (4+)
7

Of course you could simplify that even further to 4+3...

If for some reason you do need to use fmap regardless, you'd need it to operate in the identity functor:

Prelude> :m +Data.Functor.Identity
Prelude Data.Functor.Identity> fmap ($ 3) (Identity (4+))
Identity 7

The identity functor is a very boring container that just always contains exactly one element.


That's not unrealistic BTW: in Haskell we like to keep code as generic as possible. You may have a function that is able to work with arbitrary functors (more typically, arbitrary monads, which are special functors) but may want to use it in the trivial context of just one element contained. Or, you may want to stack different monad functionalities as monad transformers; then you'll generally start with Identity as the “vanilla monad”.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • My initial reaction to the question was "You can't do that! Wait... you *should* be able to do that, because the identity functor is still a functor... Hmm..." Do you know if there's a reason Haskell can't automatically interpolate identity functors where they're semantically required? – Daniel McLaury Apr 04 '18 at 12:58
  • 2
    @DanielMcLaury how would you know when they are semantically required? You'd need a classification between “functor-y values” and “plain-ish values”. But in Haskell, types are ab initio black boxes – a polymorphic function has no way to investigate whether the type it works with has any extra structure. What you can do is require that a type must be functor-wrapped, but to typecheck that the compiler must be able to clearly separate it to something of the form `f a`. For instance `Int` is not of this form, but there's actually no way to prove that from within the language. – leftaroundabout Apr 04 '18 at 13:11
  • ...mind though: you _can_ write, for example, `7 :: Identity Int`, because `Identity Int` has a `Num` instance and can thus be instantiated directly with number literals. – leftaroundabout Apr 04 '18 at 13:13
  • @DanielMcLaury Also, on a slightly pedantic note, `Identity` isn't the only way to trivially make a functorial value out of anything. There is also, at least, `Const`. – duplode Apr 04 '18 at 14:40
  • Thank you @leftaroundabout, I didn't know `($3) (4+)` works :) Could you please elaborate how (4+) is not a functor ? Aren't functions functors ? – Šefčíková Jana Apr 04 '18 at 15:21
  • @ŠefčíkováJana yes, functions _are_ functors, in the sense that there is `instance Functor ((->) a)`. But this instance unfortunately makes it hard to see which mapping is a functor-map and which is just a plain function; IMO it would be better if that instance didn't exist. (There's a more explicit name for the function functor which avoids the confusion: [Reader](http://hackage.haskell.org/package/transformers-0.5.5.0/docs/Control-Monad-Trans-Reader.html).) – leftaroundabout Apr 04 '18 at 15:32
  • Also, technical nitpicking: `(4+)` is not a functor and neither is `[1,2,3]`, those are just _container values_. The actual functor is the _function type constructor_, or the _list type constructor_. – leftaroundabout Apr 04 '18 at 15:37
  • Thank you for note, I didn't met so far 'container' values term, is this name for instances such as (4+), [1,2,3] ?I am also not sure what is 'plain' function. With my limited knowledge, I don't see any difference between functions (4+) and ($3). Why we suddenly use Identity ? – Šefčíková Jana Apr 04 '18 at 16:00
  • 1
    The main difference between the functions `(4+)` and `($3)` is in the type: the former is just a simple `Int -> Int` endofunction, whereas `($3)` is a _higher-order function_ of type `(Int -> Int) -> Int`. — “Why we suddenly use Identity” – well, we _don't_, normally; as I explained, you would usually just _apply_ the function without ever considering any functors. Function application is just juxtaposition in Haskell, thus `($3) (4+)`. – leftaroundabout Apr 04 '18 at 16:09
2

I would say the main issue is that your (4 +) is not wrapped correctly inside a functor. If you inspect the type of fmap you'll get:

Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b

In your first example, all the functions were wrapped in a list. Here if you change your (4 +) into [ (4 +) ], it will work.

As for the explanation about functor, you'll probably find more documentation online (or in the comment section :D Thanks for the feedback)

Vinz
  • 5,997
  • 1
  • 31
  • 52
  • 3
    This has nothing to do with `Monad`s, but with `Functor`s. – Willem Van Onsem Apr 04 '18 at 11:57
  • Does it makes sense to replace 'monad' with 'functor' in my answer or would it be nonsense ? – Vinz Apr 04 '18 at 11:59
  • 1
    well you write "wrapped [correctly] inside a monad", but a monad is not a constructor, you can not really *wrap* something in a Monad I think. Here the idea is that `(4 +)` is a functor (since `(->) a` is a functor), but that does not work nicely with `$ 3`. – Willem Van Onsem Apr 04 '18 at 12:13
  • 2
    To be fair a monad is just a monoid in the category of endofunctors, meaning it's an endofunctor together with some extra structure. So if it makes sense to talk about"wrapping something in a functor," it makes an equal amount of sense to talk about "wrapping something in a monad," although I don't love either piece of terminology. That said `fmap` is for arbitrary endofunctors, not just monads, so it's a better thing to use here. – Daniel McLaury Apr 04 '18 at 12:53
2

fmap applies a function to values found in some functorial value. In fmap ($ 3) [(4+), (10*), (^2), sqrt], the list is the functorial value, and the function ($ 3) is applied to its elements. As for fmap ($ 3) (4+), leftaroundabout correctly points out that:

However, (4+) by itself is not a function wrapped in any functor, it's just a function by itself.

In this case, there is a complementary way to look at it. (4+) is a functorial value; however, the functor isn't the one you need. Functions are functors, and, for the purposes of fmap, the values "found in them" are the results of the function:

GHCi> :set -XTypeApplications
GHCi> :t fmap @((->) _)
fmap @((->) _) :: (a -> b) -> (w -> a) -> w -> b

fmap for functions applies a function to the results of another function, which amounts to function composition. So this...

GHCi> fmap (4*) (2+) 1
12

... is the same as:

GHCi> ((4*) . (2+)) 1
12

In your case, we have:

GHCi> :t (4+)
(4+) :: Num a => a -> a

So fmap f (4+) will apply f to the Num a => a result of (4+). The type of ($ 3), though, is:

GHCi> :t ($ 3)
($ 3) :: Num a => (a -> b) -> b

And so fmap ($ 3) will expect a functorial value with functions to be possibly found in it:

GHCi> :t fmap ($ 3)
fmap ($ 3) :: (Num a, Functor f) => f (a -> b) -> f b

Putting it all together, we get:

GHCi> :t fmap ($ 3) (4+)
fmap ($ 3) (4+) :: (Num (a -> b), Num a) => (a -> b) -> b

The type error this will lead to has to do with the Num (a -> b) constraint. If there are functions to be found in (4+), then 4 itself must be a function. As 4 is a numeric literal, its type must be also an instance of Num. However, there is no Num instance for functions. Trying fmap ($ 3) (4+) leads to an error which mentions Num (a -> b). That should suggest something is off:

GHCi> fmap ($ 3) (4+)

<interactive>:33:1: error:
    * Non type-variable argument in the constraint: Num (a -> b)
      (Use FlexibleContexts to permit this)
    * When checking the inferred type
        it :: forall a b. (Num (a -> b), Num a) => (a -> b) -> b

The "Non-type variable argument" complaint, though, is a bit of a distraction, induced by numeric literals being polymorphic. We can get a more straightforward error either by enabling FlexibleContexts and then trying to use fmap ($ 3) (4+) (which will lead to Num a => a being specialised to Integer thanks to the defaulting rules)...

GHCi> :set -XFlexibleContexts
GHCi> fmap ($ 3) (4+) (2*)

<interactive>:39:1: error:
    * No instance for (Num (Integer -> Integer))
        arising from a use of `it'
        (maybe you haven't applied a function to enough arguments?)
    * In the first argument of `print', namely `it'
      In a stmt of an interactive GHCi command: print it

... or by specialising the numeric type through a type annotation:

GHCi> fmap ($ 3) ((4 :: Integer)+)

<interactive>:42:13: error:
    * Couldn't match type `Integer' with `Integer -> b'
      Expected type: Integer -> Integer -> b
        Actual type: Integer -> Integer
    * In the second argument of `fmap', namely `((4 :: Integer) +)'
      In the expression: fmap ($ 3) ((4 :: Integer) +)
      In an equation for `it': it = fmap ($ 3) ((4 :: Integer) +)
    * Relevant bindings include
        it :: Integer -> b (bound at <interactive>:42:1)
duplode
  • 33,731
  • 7
  • 79
  • 150
  • Thank you @duplode, I can't parse understanding from this error for now. The problematic step is `fmap ($ 3) (4+) :: (Num (a -> b), Num a) => (a -> b) -> b`. I would expect that it return `a` and not `(a->b)->b`. Do you have any suggestion what I could study to understand better this type of evaluation ? – Šefčíková Jana Apr 04 '18 at 16:17
  • @ŠefčíkováJana As leftaroundabout explains in his answer, the type would be `Num a => a`, as you expected, if `fmap` wasn't there. In this case, `fmap` leads `($ 3)` to be applied to the *result* of `(4+)`, and not to `(4+)` itself. On "Do you have any suggestion what I could study to understand better this type of evaluation ?", there are a few questions here that illustrate the procedure rather well. I'll come back with a link shortly. – duplode Apr 04 '18 at 18:07
  • @ŠefčíkováJana [Here is a decent, if a little contrived, example](https://stackoverflow.com/q/43428053/2751851) of doing it in a systematic way. It is basically a matter of starting "from the outside" (in your case, from the type of `fmap`) and working your way "inside" by substituting the types. – duplode Apr 04 '18 at 18:40