Can someone explain the meaning of ((.) $ (.)) (==) 1 (1+) 0

On haskell.org I came across this free style feature called the owl.

((.)$(.)) 

Its signature is of type (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c .

This is equivalent to fabcd = ab (cd) and apparently ((.)$(.)) (==) 1 (1+) 0 returns True .

So my questions are:

  • What does a1 mean in a type signature? Is this related to a ?
  • Is (==) a kind of function equality operator? Because 0 (==) 0 throws an error in GHCi.
  • What does 1 (1+) 0 mean in this context? I do not see how this is even a valid expression.
  • Why does the expression return True ?
+7
syntax haskell pointfree
source share
2 answers
  • a1 is "just another type variable." It can mean anything, including a , but does not necessarily mean anything. Most likely, it differs from a .

  • (==) is a form of forced prefix == , the regular equality operator forms a class of type Eq . Usually you write a == b , but it's just syntactic sugar for (==) ab , the prefix application == .

  • 1 (1+) 0 does not mean anything specifically in this context, each of the three subexpressions is an independent argument for the “owl,” which ultimately takes four arguments.

  • We can go through reduction.

     ((.)$(.)) (==) 1 (1+) 0 === [ apply ] ((.)(.)) (==) 1 (1+) 0 === [ implicit association ] ((.)(.)(==)) 1 (1+) 0 === [ apply the definition: (fg) x = f (gx) ] ((.) (1 ==)) (1+) 0 === [ implicit association ] ((.) (1 ==) (1+)) 0 === [ apply the definition: (fg) x = f (gx) ] 1 == (1+0) === [addition] 1 == 1 === [equality] True 

As this page mentions, owl is equivalent to f

 fabcd = ab (cd) 

that is, he applies his first argument, the function of two arguments, to his second argument and the result of applying the third argument to his fourth. In the above example, ((.)$(.)) (==) 1 (1+) 0 means that you first apply (+1) to 0 , then combine 1 and (1+0) with (==) , which happened in our reduction.

In a broader sense, you might think of it as a function that modifies the binary operation a to slightly modify its second argument.

+15
source share

First write _B fgx = f (gx) = (f . g) x .

Since f $ x = fx , then (.)$(.) = _B $ _B = _B _B . Its type is obtained mechanically, since

 0. (.) :: ( b -> c ) -> ((a -> b) -> (a -> c)) 1. (.) :: (b1 -> c1) -> ((a1 -> b1) -> (a1 -> c1)) 2. (.) (.) :: {b ~ b1 -> c1, c ~ (a1 -> b1) -> (a1 -> c1)} (a -> b) -> (a -> c) :: (a -> b1 -> c1) -> a -> (a1 -> b1) -> (a1 -> c1) :: (a -> b -> c ) -> a -> (a1 -> b ) -> a1 -> c 

a and a1 are two different type variables, like b and b1 . But since there is no b or c in the final type, we can rename b1 and c1 back to only b and c to simplify. But not a1 .

We can read this type, in fact: it gets the f :: a -> b -> c binary function; x :: a value of the argument, g :: a1 -> b unary function and another value is y :: a1 and combines them in the only possible way that the types match:

  fx :: b -> c gy :: b fx (gy) :: c 

The rest has already been answered. Abbreviations are usually easier to perform in combinatorial equations, such as _B _B fxgy = _B (fx) gy = fx (gy) , with only two applications of the definition of _B (we can always add as many arguments as we need).

0
source share

All Articles