2
module Tf0 where

all' :: (a -> Bool) -> [a] -> Bool
all' p = foldr (&&) True . map p 

main :: IO()

xs = [1,3,5,7,9]
xs1 = [1,3,11,4,15]

k1 = all' (<10) xs
k2 = all' (<10) xs1
k3 = all' (<10)

main = print k1

Questions:

  1. In the function definition (all' p = foldr (&&) True . map p) there is no list as an input while the function Type shows a list as an input ([a]), yet trying to check the function (see bellow k1, k2, k3) shows that a list is needed.

  2. How can the function be defined without the list?

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
Atir
  • 163
  • 6
  • 1
    Writing `f x = g x . h x` is the same as writing `f x y = g x (h x y)`, by definition of the composition operator `.`. The first style is referred to as "point-free style" (or, jokingly, point-less style). – chi Jan 31 '17 at 11:54
  • One word: currying. – chepner Jan 31 '17 at 14:28

1 Answers1

4

One of the fundamental aspects of Haskell is that conceptually a function always takes one argument. Indeed take for instance the function (+):

(+) :: Int -> Int -> Int

(technically it is (+) :: Num a => a -> a -> a, but let us keep things simple).

Now you can argue that (+) takes two arguments, but conceptually it takes one argument. Indeed: the signature is in fact

(+) :: Int -> (Int -> Int)

So you feed it one integer and now it returns a function, for instance:

(+) 5 :: Int -> Int

So you have, by giving it a 5, constructed a function that will add 5 to a given operand (again a function that takes one argument).

Now applying this to your question, the signature of all' is in fact:

all' :: (a -> Bool) -> ([a] -> Bool)

So if you apply all' with one argument, it returns a function that maps a list [a] to a Bool. Say for the sake of argument that we set p to \_ -> True, then we return:

foldr (&&) True . map (\_ -> True) :: [a] -> Bool

so indeed a function that takes a list [a] and maps it on a Bool. In a next phase you apply a list to that (returning) function.

In other words, functional programming can be seen as a long chain of specializing a function further and further until it is fully grounded and can be evaluated.

Haskell only provides this in an elegant way such that you do not have to think about functions generating functions. But conceptually, this happens all the time. Given you want to implement a function f :: a -> b -> c, you can do this like:

f x y = g (x+1) y

(with g :: a -> b -> c), but you can also decide to leave a parameter out, and define it as:

f x = g (x+1)

since g (x+1) will return a function that can apply with the y eventually.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 1
    I'd avoid the section `(5+)` for this, since it exploits a special syntax, and instead use plain application `(+) 5` which follows the spirit of the argument. – chi Jan 31 '17 at 11:52
  • @chi: that is indeed a good suggestion: modified. Thanks. – Willem Van Onsem Jan 31 '17 at 12:23
  • Thank you for your good explanations, however,I would like to get an answer for the following: Given (from your example that two (different?) functions:f x y = g (x+1) y and f x = g (x+1), how does the second function( f x = g (x+1)) "Know" that a second parameter (y) is expected? – Atir Jan 31 '17 at 14:30
  • @Atir: because Haskell knows the type signature of `g` and `g :: a -> (b -> c)` means `g (x+1)` will "collapse" to `g (x+1) :: b -> c`, so it reasons it needs a `b` to generate a `c`. – Willem Van Onsem Jan 31 '17 at 14:32
  • @Atir when you define `f x = g (x+1)`, you get two types inferred, `f :: Num x => x -> a` and `g :: Num x => x -> a`. Depending on *later/other* uses of both/either `f` and `g` in your code, that `a` may get instantiated yet further; in particular, if `g x y` is found, `a` would become `b -> c` for some `b` and `c`. You can even try this at the GHCi prompt: try `let f x = g (x+1) ; g y = g (y-1)` and then e.g. `let f x = g (x+1) ; g y z = g z (y+1)` (with `:set +t` enabled to see the types). *This* is what is referred to by "every function has one argument". – Will Ness Jan 31 '17 at 22:25
  • @WillemVanOnsem terminology: we "bind" a value to a *name*; we *apply* a value to a function. – Will Ness Jan 31 '17 at 22:44
  • @WillNess: usually when people are rather strict about terminology I think about [this xkcd comic](https://xkcd.com/747/). But nevertheless in order to keep the blood pressure of my fellow Haskellers acceptable, (modified) ;P – Willem Van Onsem Jan 31 '17 at 22:49
  • using wrong terms quickly gets very confusing unless a reader *knows* already what you mean. newcomers *don't*, yet. – Will Ness Jan 31 '17 at 22:50
  • 1
    @WillNess: yeah, I was joking, I hope you got that :) I hope you're not angery, usually I appreciate people being able to put things in perspective :) – Willem Van Onsem Jan 31 '17 at 22:51
  • 1
    :) ___________________ – Will Ness Jan 31 '17 at 22:51