What happens when I compose * c + in Haskell?

I'm trying to understand the result

(*) . (+) 

in Haskell. I know that the composition operator is just a standard composition of mathematical functions, therefore

 (f . g) = f (gx) 

But:

 (*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a 

I am trying to understand a signature of this type. I expected that I could do something like:

 ((*) . (+)) 1 2 :: Num a => a -> a = (* (+ 1 2)) 

What's the point (*). (+) type signature? I tried to play with it with something like (just matching its signature):

 ((*) . (+)) 1 (\x -> x + 1) 1 

But this does not compile. I try to go through the logical steps in compiling them, but I do not quite understand how this works with this result (and what the result is).

+45
functional-programming haskell function-composition
Dec 27 '14 at 4:02
source share
5 answers

I understand how you feel. I found that the composition of the function is quite difficult to understand at first. What helped me figure out was type signatures. Consider:

 (*) :: Num x => x -> x -> x (+) :: Num y => y -> y -> y (.) :: (b -> c) -> (a -> b) -> a -> c 

Now that you write (*) . (+) (*) . (+) , this is actually the same as (.) (*) (+) (i.e. (*) is the first argument (.) and (+) is the second argument (.) ):

 (.) :: (b -> c) -> (a -> b) -> a -> c |______| |______| | | (*) (+) 

Therefore, a signature of type (*) (i.e., Num x => x -> x -> x ) combines with b -> c :

 (*) :: Num x => x -> x -> x -- remember that `x -> x -> x` | |____| -- is implicitly `x -> (x -> x)` | | b -> c (.) (*) :: (a -> b) -> a -> c | | | |‾‾‾‾| Num x => xx -> x (.) (*) :: Num x => (a -> x) -> a -> x -> x 

Therefore, a signature of type (+) (i.e., Num y => y -> y -> y ) combines with Num x => a -> x :

 (+) :: Num y => y -> y -> y -- remember that `y -> y -> y` | |____| -- is implicitly `y -> (y -> y)` | | Num x => a -> x (.) (*) (+) :: Num x => a -> x -> x | | | | |‾‾‾‾| |‾‾‾‾| Num y => yy -> yy -> y (.) (*) (+) :: (Num (y -> y), Num y) => y -> (y -> y) -> y -> y 

I hope this clarifies where Num (y -> y) and Num y come from. You still have a very strange function like (Num (y -> y), Num y) => y -> (y -> y) -> y -> y .

Which makes him so strange that he expects both y and y -> y be Num instances. It is clear that y must be an instance of Num , but how y -> y ? Creating y -> y instance of Num seems illogical. It may not be right.

However, this makes sense when you look at which functional composition is actually:

 ( f . g ) = \z -> f ( gz) ((*) . (+)) = \z -> (*) ((+) z) 

So you have the function \z -> (*) ((+) z) . Therefore, z must be an explicit Num instance, because (+) applies to it. Thus, the type \z -> (*) ((+) z) is equal to Num t => t -> ... , where ... is the type (*) ((+) z) that we recognize in an instant .

Therefore, ((+) z) is of type Num t => t -> t , because it requires one more number. However, before it is applied to another number, (*) will be applied to it.

Therefore, (*) expects ((+) z) be an instance of Num , so t -> t is expected to be an instance of Num . Thus, ... is replaced by (t -> t) -> t -> t and the restriction Num (t -> t) added, which leads to the type (Num (t -> t), Num t) => t -> (t -> t) -> t -> t .

The way you really want to combine (*) and (+) uses (.:) :

 (.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d f .: g = \xy -> f (gxy) 

Therefore, (*) .: (+) Coincides with \xy -> (*) ((+) xy) . Now, two arguments are given (+) , ensuring that ((+) xy) really just Num t => t , and not Num t => t -> t .

Therefore, ((*) .: (+)) 2 3 5 is (*) ((+) 2 3) 5 , which is (*) 5 5 , which is 25 , which I believe is what you want.

Note that f .: g can also be written as (f .) . g (f .) . g , and (.:) can also be defined as (.:) = (.) . (.) (.:) = (.) . (.) . You can read about it here:

What does (f.). Does g mean in Haskell?

Hope this helps.

+67
Dec 27 '14 at 4:57
source share

(*) and (+) both have a signature like Num a => a -> a -> a Now, if you compose them, you get something scared.

 (*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a 

This is because (*) and (+) expect two “arguments”.

(+) with one argument gives you a function. The operator . expects this function ( a -> a , which you see).

Here is the value (*) . (+) (*) . (+)

  xfy (*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a 

(*) . (+) (*) . (+) maps xfy to ((x +) * f) y , where f is a function from a to a , which is ALSO a number. The reason (*) suggests that the function must match the types when it expects two arguments, but this function must be a number because (*) only works with numbers.

Indeed, this function does not make any sense.

+9
Dec 27 '14 at 4:38
source share

Some extensions first:

 {-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances #-} 

As other answers show, your function

 weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a weird xg = (x +) * g 

But this function has unearthly semantics.

There is the concept of lists of differences . Accordingly, there is the concept of difference integers. I saw that they were used only in the dependent typed setup (for example, here , but this is not the only case). Relevant part of the definition

 instance Enum DiffInt where toEnum n = (n +) fromEnum n = n 0 instance Num DiffInt where n + m = n . m n * m = foldr (+) id $ replicate (fromEnum n) m 

This doesn't make much sense in Haskell, but it can be useful with dependent types.

Now we can write

 test :: DiffInt test = toEnum 3 * toEnum 4 

or

 test :: DiffInt test = weird 3 (toEnum 4) 

In both cases, fromEnum test == 12 .

EDIT

You can avoid using the TypeSynonymInstances extension:

 {-# LANGUAGE FlexibleContexts, FlexibleInstances #-} weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a weird xg = (x +) * g instance (Enum a, Num a) => Enum (a -> a) where toEnum n = (toEnum n +) fromEnum n = fromEnum $ n (toEnum 0) instance (Enum a, Num a) => Num (a -> a) where n + m = n . m n * m = foldr (+) id $ replicate (fromEnum n) m type DiffInt = Int -> Int 

As before, we can write

 test' :: DiffInt test' = weird 3 (toEnum 4) 

But now we can also write

 -- difference ints over difference ints type DiffDiffInt = DiffInt -> DiffInt test'' :: DiffDiffInt test'' = weird (toEnum 3) (toEnum (toEnum 4)) 

and

 main = print $ fromEnum $ fromEnum test' 

outputs 12 .

EDIT2 Added better links.

+7
Dec 27 '14 at 7:43
source share

Let be:

 m = (*) a = (+) 

then

 (ma) x = (m (ax)) = m (ax) 

Now m expects a Num a as a parameter, on the other hand (ax) , i.e. (x +) is a unary function (a -> a) by definition (+) . I assume that the GHC has tried to combine the two types, so if you have a type that is both a number and a unary function, m can take a number and a unary function and return a unary function, since they are considered the same .

As @Syd pointed out, this union does not make sense for any ordinary number types, such as integers and floating point numbers.

+2
Dec 27 '14 at 4:57
source share

There are good answers here, but let me quickly point out a few steps that you did wrong.

Firstly, the correct determination of the composition of the function

 (f . g) x = f (gx) 

you lowered x to LHS. Then you must remember that in Haskell, hxy same as (hx) y . So, contrary to what is expected,

 ((*) . (+)) 1 2 = (((*) . (+)) 1) 2 = ((*) ((+) 1)) 2 = ((+) 1) * 2, 

and now you see why this fails. Besides,

 ((*) . (+)) 1 (\x -> x + 1) 1 

does not work because the Num (Int -> Int) restriction Num (Int -> Int) not met.

+2
Dec 28 '14 at 13:19
source share



All Articles