Why doesn't inverse function imply isomorphism - haskell

Let's say I have two functions named f :: a -> b and it's inverse g :: b -> a such that f . g ≡ id.
Now isn't g . f ≡ id ? (And hence implying isomorphism)
I tried to write a similar example and came up with this:
myRead :: String -> Int
myRead = read
myShow :: Int -> String
myShow = show
In ghci:
λ> myRead . myShow $ 3
3
λ> myShow . myRead $ "33"
"33"
But it seems that the inverse function doesn't imply isomorphism. So can anybody point me on what I'm doing wrong here ?

Here's a really trivial example. If A is the set {1,2} and B the set {1} then the functions:
f :: A -> B
f = const 1
g :: B -> A
g 1 = 1
have the relation f . g = id but not the relation g . f = id. A counterexample is
g (f 2) = 1
It turns out that if you have two functions such that f . g = id and g . f = id then that says a whole lot about the domain and codomain of those functions. In particular, it establishes an isomorphism which suggests that those two domains are in some sense equivalent.
From a category theoretic perspective it means that they are indistinguishable via the morphisms of the category. Category theory emphasizes that the morphisms of the category are the only way to gain information about an object, so this indistinguishability is very important.
When you've got only one-sided inverses, you still are learning a lot about the two domains... but simply not that they're isomorphic.
One thing a one-sided inverse gives you is an idempotent. An idempotent is a function i from a domain to itself (an endomorphism) such that i . i = i. Given any two functions where f . g = id, g . f is an idempotent and the proof is quite obvious:
i . i = (g . f) . (g . f) = g . f . g . f = g . (f . g) . f = g . f = i
Another good thing to think about is that every function f :: A -> B produces the "inverse-image" function inv f :: B -> (A -> Bool).
inv :: Eq b => (a -> b) -> b -> a -> Bool
inv f b a = f a == b
In more mathematical terms, the inverse image function is a mapping from the codomain B to subsets of the domain A such that every element in each such subset of A maps to the same element of B. These subsets partition A (this is the definition of a function).
If we have another function g :: B -> A such that g b is in the subset inv f b (i.e. inv f b (g b) == True for all b) then we have
f . g == id
but this is far weaker and more technical than A and B just being isomorphic. It just means that g is sending elements of B to subsets of A which f will send right back.
For instance, it admits a whole interesting notion of the "fibration" of a space.

Using your own example, myRead . myShow ≡ id, but
(myShow . myRead) "0xFF" = "255"
so myShow . myRead ≢ id, and you could also see this by a counting argument:
The type Int has finitely many values while String has infinitely many, so while it is possible to go from Int to String and back, going from the infinite type String to the finite type Int must discard information, so you can't always get back to the original string and therefore it is impossible to construct an isomorphism between Int and String.

If g :: X -> Y is surjective, then there is not necessarily a reverse function f :: Y -> X. Yet, a surjective function g can reverse some function f.
Suppose for every y in Y there is a unique value x in X that f finds. It is feasible to specify a function g that for every such x in X finds y in Y such that g . f == id. This statement shows existence of unique x for all y, but this does not tell anything about existence of unique y for all x (i.e. the uniqueness is not guaranteed). (I am not even touching how g is built - you'd need axiom of choice).

Related

Flip functor instance Haskell

I need to write the Functor instances for the Flip datatype:
data K a b = K a
newtype Flip f a b = Flip (f b a) deriving (Eq, Show)
instance Functor (Flip K a) where
fmap=undefined
The solution I was given in class is:
instance Functor (Flip K a) where
fmap f (Flip (K b)) = Flip (K (f b))
I really don't understand what's going on here and I'm beginning to doubt my whole understanding of data types and functors. What I do understand is this (please correct me if any of this is wrong):
K is a data type that turns 2 parameters into a structure K a ( that only keeps the first parameter)
Flip is a datatype that turns 3 arguments into a structure with one
Because in fmap :: (a-> b) -> f a -> f b, f a has kind *, to write the Functor instance of Flip, we write it on the last type in Flip. Aka f and a are "constants" in a way, and we write the functor for the type b. I would write something like:
instance Functor (Flip f a) where
fmap f (Flip x y z) = fmap Flip x y (f z)
I know that that is completely wrong but I'm not sure why.
Also, why would we bring K into the Functor instance of Flip? Can someone explain thoroughly the process of coming up with this solution and why it is correct?
K is a data type that turns 2 parameters into a structure K a ( that only keeps the first parameter)
This isn't quite right. K a b is a data type formed using two parameters, but it's not really right to say that it "turns them into" anything. Instead, it's simply just stating to the world that there now exists a new type: K a b. "So what?" you might ask. Well, the second half of the data type defines how to make new values of this type. That part says, "You can make a new value of type K a b with this function I'll call K which has type a -> K a b." It's really important to recognize that there is a distinction between the type K and the constructor K.
So, it's not that K "only keeps the first parameter"—it's that the constructor K (which is a function) happens to not take any arguments of type b.
Flip is a datatype that turns 3 arguments into a structure with one
Just as above, this is not quite right. The Flip declaration states that there can be values of type Flip f a b, and the only way to make them is by using the constructor Flip that has type f b a -> Flip f a b.
In case you're wondering how I'm coming up with the type signatures for the constructors K and Flip, it's not actually mysterious, and you can double check by typing :t K or :t Flip into GHCi. These types are assigned based entirely on the right hand side of the data type declaration. Also, note that the type name and constructor don't have to be the same. For instance, consider this data type:
data Foo a = Bar Int a | Foo String | Baz a a
This declares a type Foo a with three constructors:
Bar :: Int -> a -> Foo a
Foo :: String -> Foo a
Baz :: a -> a -> Foo a
Basically, each of the types after the constructor name are the arguments, in order, to the constructor.
Because in fmap :: (a-> b) -> f a -> f b, f a has kind *, to write the Functor instance of Flip, we write it on the last type in Flip. Aka f and a are "constants" in a way, and we write the functor for the type b.
This is basically right! You could also say that f has kind * -> *. Since Flip has kind (* -> *) -> * -> * -> *, you need to provide it two type arguments (the first of kind * -> * and the second of kind *) to get it to the right kind. Those first two arguments become fixed ("constants" in a way) in the instance.
I would write something like: ... I know that that is completely wrong but I'm not sure why.
The reason your instance is completely wrong is that you've mixed up the type with the constructor. It doesn't make sense to put (Flip x y z) in the pattern position where you did because the constructor Flip only takes one argument—remember, it's type is Flip :: f b a -> Flip f a b! So you'd want to write something like:
instance Functor (Flip f a) where
fmap f (Flip fxa) = ...
Now, what do you fill in for the ...? You have a value fxa :: f x a, and you have a function f :: x -> y, and you need to produce a value of type f y a. Honestly, I don't know how to do that. After all, what is a value of typ f x a? We don't know what f is?!
Also, why would we bring K into the Functor instance of Flip? Can someone explain thoroughly the process of coming up with this solution and why it is correct?
We saw just above that we can't write the Functor instance for an arbitrary f, but what we can do is write it for a particular f. It turns out that K is just such a particular f that works. Let's try to make it work:
instance Functor (Flip K a) where
fmap f (Flip kxa) = ...
When f was arbitrary, we got stuck here, but now we know that kxa :: K x a. Remember that the only way to make a value of type K x a is using the constructor K. Therefore, this value kxa must have been made using that constructor, so we can break it apart as in: kxa ⩳ K x' where x' :: x. Let's go ahead and put that into our pattern:
fmap f (Flip (K x')) = ...
Now we can make progress! We need to produce a value of type Flip K a y. Hmm. The only way to produce a value of type Flip is using the Flip constructor, so let's start with that:
fmap f (Flip (K x')) = Flip ...
The Flip constructor at type Flip K a y takes a value of type K y a. The only way to produce one of those is with the K constructor, so let's add that:
fmap f (Flip (K x')) = Flip (K ...)
The K constructor at type K y a takes a value of type y, so we need to provide a value of type y here. We have a value x' :: x and a function f :: x -> y. Plugging the first into the second gives us the value we need:
fmap f (Flip (K x')) = Flip (K (f x'))
Just rename x' to b, and you have exactly the code your teacher provided.
DDub wrote in their answer:
You have a value fxa :: f x a, and you have a function f :: x -> y, and you need to produce a value of type f y a. Honestly, I don't know how to do that. After all, what is a value of type f x a? We don't know what f is?!
And I agree, but I woulld like to add a bit. Your teacher's idea as to how to deal with this is pretty cool (things like this K come in quite handy when you are trying to write down some counterexample, like here), and yet, I reckon we can make this code way broader. I use Data.Bifunctor.
So, what are Bifunctors? They are just what their name says: a * -> * -> * type (which we call bifunctors as well sometimes, yet they are not the same thing) which allows mapping over its both arguments (snippet from the source):
class Bifunctor p where
-- | Map over both arguments at the same time.
--
-- #'bimap' f g ≡ 'first' f '.' 'second' g#
bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
bimap f g = first f . second g
{-# INLINE bimap #-}
-- | Map covariantly over the first argument.
--
-- #'first' f ≡ 'bimap' f 'id'#
first :: (a -> b) -> p a c -> p b c
first f = bimap f id
{-# INLINE first #-}
-- | Map covariantly over the second argument.
--
-- #'second' ≡ 'bimap' 'id'#
second :: (b -> c) -> p a b -> p a c
second = bimap id
{-# INLINE second #-}
So, here is how I would go about that:
instance Bifunctor f => Functor (Flip f a) where
fmap x2y (Flip fxa) = Flip (first x2y fxa)
Speaking of your teacher's code, it's a very nice idea, yet a more narrow one as K is a Bifunctor:
instance Bifunctor K where
bimap f _g (K a) = K (f a)
A lawful one:
bimap id id (K a) = K (id a) = id (K a)
As it says in the link above, having bimap only written down, that's the only law we need to worry about.
We just need to use sane and helpful naming, and suddenly it all becomes simple and clear (as opposed to torturous and contorted):
data K b a = MkK b -- the type (K b a) "is" just (b)
newtype Flip f a b = MkFlip (f b a) -- the type (Flip f a b) "is" (f b a)
deriving (Eq, Show)
instance Functor (Flip K a) where
-- fmap :: (b -> c) -> Flip K a b -> Flip K a c
fmap g (MkFlip (MkK b)) = MkFlip (MkK (g b))
-- MkK b :: K b a
-- MkFlip (_ :: K b a) :: Flip K a b
There's not even one question arising in our minds now looking at this, not one doubt we aren't able to immediately resolve.
Using same names for types and for data constructors while teaching, as well as using f both for "f"unction and "f"unctor, is pure abuse of the students.
Only when you've become fed up with all the Mks and don't feel they are helpful to you in any way, you can safely and easily throw them away, as experts usually do.

Given an n-arg type constructor, what are some ways to reduce the number of arguments?

For instance, for Either, there are two arguments L and R.
What would be several ways of constructing both of the single-parameter type constructors from Either: Either L and (pseudo code) Either _ R?
One way, I suppose, is to declare a type alias:
type EitherL L = Either L R -- for some fixed R
But are there ways to do this inline without needing to construct type aliases?
Things don't always translate directly. In particular, you have to decide what you want to be first-class and what can be second-class.
-- You can do this, but it must always
-- be fully applied.
type FlipF f a b = f b a
-- This is more exciting, as it can
-- be partially applied.
newtype Flip f a b = Flip {unFlip :: f b a}
Now
Either a b = FlipF Either b a
Either a b ~= Flip Either b a
Currying you have multiple options too.
type CurryF f a b = f '(a, b)
newtype Curry f a b = Curry {unCurry :: f '(a, b)}
Uncurrying is a bit trickier.
type family UncurryF f ab where
UncurryF f '(a, b) = f a b
data Uncurry1 f ab where
Uncurry1 :: f a b -> f '(a, b)
newtype Uncurry2 f ab = Uncurry2
{reCurry2 :: f (Fst ab) (Snd ab)}
type family Fst ab where
Fst '(a, _) = a
type family Snd ab where
Snd '(_, b) = b
Which uncurry you need will be somewhat application dependent.

Representing Integers as Functions (Church Numerals?)

Given the following function definition and assuming similar definitions for all positive integers give the type definition and code for a function called plus that will take as arguments two such functions representing integers and return a function that represents the sum of the two input integers. E.g. (plus one two) should evaluate to a function that takes two arguments f x and returns (f(f(f x))).
one f x = f x
two f x = f (f x)
three f x = f (f (f x)))
etc.
I am new to functional programming and I can't get my head around this. I firstly don't know how I can define the functions for all the positive integers without writing them out (which is obviously impossible). As in, if I have plus(sixty, forty), how can my function recognize that sixty is f applied 60 times to x?
I am meant to be writing this in Miranda, but I am more familiar with Haskell, so help for either is welcome.
Apply equational reasoning1, and abstraction. You have
one f x = f x -- :: (a -> b) -> a -> b
two f x = f (f x) -- = f (one f x) -- :: (a -> a) -> a -> a
three f x = f (f (f x)) -- = f (two f x) -- :: (a -> a) -> a -> a
-- ~~~~~~~~~~~
Thus, a successor function next is naturally defined, so that three = next two. Yes, it is as simple as writing next two instead of three in the equation above:
next :: ((b -> c) -> a -> b) -> (b -> c) -> a -> c
-- three f x = next two f x = f (two f x) -- `two` is a formal parameter
-- ~~~~~~~~~~~
next num f x = f (num f x) -- generic name `num`
zero :: t -> a -> a
zero f x = x
This captures the pattern of succession. f will be used as a successor function, and x as zero value. The rest follows. For instance,
plus :: (t -> b -> c) -> (t -> a -> b) -> t -> a -> c
plus two one f x = two f (one f x) -- formal parameters two, one
-- = f (f (one f x)) -- an example substitution
-- = f (f (f x) -- uses the global definitions
-- = three f x -- for one, two, three
i.e. one f x will be used as a zero value by two (instead of the "usual" x), thus representing three. A "number" n represents a succession of n +1 operations.
The above, again, actually defines the general plus operation because two and one are just two formal function parameters:
Prelude> plus three two succ 0 -- built-in `succ :: Enum a => a -> a`
5
Prelude> :t plus three two
plus three two :: (a -> a) -> a -> a
Prelude> plus three two (1:) [0]
[1,1,1,1,1,0]
The key thing to gasp here is that a function is an object that will produce a value, when called. In itself it's an opaque object. The "observer" arguments that we apply to it, supply the "meaning" for what it means to be zero, or to find a successor, and thus define what result is produced when we make an observation of a number's value.
1i.e. replace freely in any expression the LHS with the RHS of a definition, or the RHS with the LHS, as you see fit (up to the variables renaming of course, to not capture/shadow the existing free variables).
To convert a number to a numeral you can use something like:
type Numeral = forall a . (a -> a) -> (a -> a)
toChurch :: Int -> Numeral
toChurch 0 _ x = x
toChurch n f x = f $ toChurch (pred n) f x
fromChurch :: Numeral -> Int
fromChurch numeral = numeral succ 0
You don't need to recognize how many times the function is calling f. For example, to implement succ, which adds 1 to a Church numeral, you can do something like this:
succ n f x = f (n f x)
Then you first use n to apply f however many times it needs to, and then you do the final f yourself. You could also do it the other way round, and first apply f once yourself and then let n do the rest.
succ n f x = n f (f x)
You can use a similar technique to implement plus.

When is a composition of catamorphisms a catamorphism?

From page 3 of http://research.microsoft.com/en-us/um/people/emeijer/Papers/meijer94more.pdf:
it is not true in general that catamorphisms are closed under composition
Under what conditions do catamorphisms compose to a catamorphism? More specifically (assuming I understood the statement correctly):
Suppose I have two base functors F and G and folds for each: foldF :: (F a -> a) -> (μF -> a) and foldG :: (G a -> a) -> (μG -> a).
Now suppose I have two algebras a :: F μG -> μG and b :: G X -> X.
When is the composition (foldG b) . (foldF a) :: μF -> X a catamorphism?
Edit: I have a guess, based on dblhelix's expanded answer: that outG . a :: F μG -> G μG must be the component at μG of some natural transformation η :: F a -> G a. I don't know whether this is right. (Edit 2: As colah points out, this is sufficient but not necessary.)
Edit 3: Wren Thornton on Haskell-Cafe adds: "If you have the right kind of distributivity property (as colah suggests) then things will work out for the particular case. But, having the right kind of distributivity property typically amounts to being a natural transformation in some appropriately related category; so that just defers the question to whether an appropriately related category always exists, and whether we can formalize what "appropriately related" means."
When is the composition (fold2 g) . (fold1 f) :: μF1 -> A a catamorphism?
When there exists an F1-algebra h :: F1 A -> A such that fold1 h = fold2 g . fold1 f.
To see that catamorphisms are in general not closed under composition, consider the following generic definitions of type-level fixed point, algebra, and catamorphism:
newtype Fix f = In {out :: f (Fix f)}
type Algebra f a = f a -> a
cata :: Functor f => Algebra f a -> Fix f -> a
cata phi = phi . fmap (cata phi) . out
For catamorphisms to compose we would need
algcomp :: Algebra f (Fix g) -> Algebra g a -> Algebra f a
Now try writing this function. It takes two functions as arguments (of types f (Fix g) -> Fix g and g a -> a respectively) and a value of type f a, and it needs to produce a value of type a. How would you do that? To produce a value of type a your only hope is to apply the function of type g a -> a, but then we are stuck: we have no means to turn a value of type f a into a value of type g a, have we?
I am not sure whether this is of any use for your purposes, but an example of a condition under which one can compose to catamorphisms is if we have a morphism from the result of the second cata to the fixed point of the second functor:
algcomp' :: (Functor f, Functor g) =>
(a -> Fix g) -> Algebra f (Fix g) -> Algebra g a -> Algebra f a
algcomp' h phi phi' = cata phi' . phi . fmap h
(Disclaimer: This is outside my area of expertise. I believe I'm correct (with caveats provided at different points), but ... Verify it yourself.)
A catamorphism can be thought of as a function that replaces constructors of a data type with other functions.
(In this example, I will be using the following data types:
data [a] = [] | a : [a]
data BinTree a = Leaf a | Branch (BinTree a) (BinTree a)
data Nat = Zero | Succ Nat
)
For example:
length :: [a] -> Nat
length = catamorphism
[] -> 0
(_:) -> (1+)
(Sadly, the catamorphism {..} syntax is not available in Haskell (I saw something similar in Pola). I've been meaning to write a quasiquoter for it.)
So, what is length [1,2,3]?
length [1,2,3]
length (1 : 2 : 3 : [])
length (1: 2: 3: [])
1+ (1+ (1+ (0 )))
3
That said, for reasons that will become apparent later, it is nicer to define it as the trivially equivalent:
length :: [a] -> Nat
length = catamorphism
[] -> Zero
(_:) -> Succ
Let's consider a few more example catamorphisms:
map :: (a -> b) -> [a] -> b
map f = catamorphism
[] -> []
(a:) -> (f a :)
binTreeDepth :: Tree a -> Nat
binTreeDepth = catamorphism
Leaf _ -> 0
Branch -> \a b -> 1 + max a b
binTreeRightDepth :: Tree a -> Nat
binTreeRightDepth = catamorphism
Leaf _ -> 0
Branch -> \a b -> 1 + b
binTreeLeaves :: Tree a -> Nat
binTreeLeaves = catamorphism
Leaf _ -> 1
Branch -> (+)
double :: Nat -> Nat
double = catamorphism
Succ -> Succ . Succ
Zero -> Zero
Many of these can be nicely composed to form new catamorphisms. For example:
double . length . map f = catamorphism
[] -> Zero
(a:) -> Succ . Succ
double . binTreeRightDepth = catamorphism
Leaf a -> Zero
Branch -> \a b -> Succ (Succ b)
double . binTreeDepth also works, but it is almost a miracle, in a certain sense.
double . binTreeDepth = catamorphism
Leaf a -> Zero
Branch -> \a b -> Succ (Succ (max a b))
This only works because double distributes over max... Which is pure coincidence. (The same is true with double . binTreeLeaves.) If we replaced max with something that didn't play as nicely with doubling... Well, let's define ourselves a new friend (that doesn't get along as well with the others). For a binary operators that double doesn't distribute over, we'll use (*).
binTreeProdSize :: Tree a -> Nat
binTreeProdSize = catamorphism
Leaf _ -> 0
Branch -> \a b -> 1 + a*b
Let's try to establish sufficient conditions for two catamorphisms two compose. Clearly, any catamorphism will quite happily be composed with length, double and map f because they yield their data structure without looking at the child results. For example, in the case of length, you can just replace Succ and Zero with what ever you want and you have your new catamorphism.
If the first catamorphism yields a data structure without looking at what happens to its children, two catamorphisms will compose into a catamorphism.
Beyond this, things become more complicated. Let's differentiate between normal constructor arguments and "recursive arguments" (which we will mark with a % sign). So Leaf a has no recursive arguments, but Branch %a %b does. Let's use the term "recursive-fixity" of a constructor to refer to the number of recursive arguments it has. (I've made up both these terms! I have no idea what proper terminology is, if there is one! Be wary of using them elsewhere!)
If the first catamorphism maps something into a zero recursive fixity constructor, everything is good!
a | b | cata(b.a)
===============================|=========================|================
F a %b %c .. -> Z | Z -> G a b .. | True
If we map children directly into a new constructor, we're also good.
a | b | cata(b.a)
===============================|=========================|=================
F a %b %c .. -> H %c %d .. | H %a %b -> G a b .. | True
If we map into a recursive fixity one constructor...
a | b | cata(b.a)
===============================|=========================|=================
F a %b %c .. -> A (f %b %c..) | A %a -> B (g %a) | Implied by g
| | distributes over f
But it isn't iff. For example, if there exist g1 g2 such that g (f a b..) = f (g1 a) (g2 b) .., that also works.
From here, the rules will just get messier, I expect.
Catamorphisms de-construct a data structure into a result value. So, in general, when you apply a catamorphism, the result is something completely different, and you cannot apply another catamorphism to it.
For example, a function that sums all elements of [Int] is a catamorphism, but the result is Int. There is no way how to apply another catamorphism on it.
However, some special catamorphisms create a result of the same type as the input. One such example is map f (for some given function f). While it de-constructs the original structure, it also creates a new list as its result. (Actually, map f can be viewed both as a catamorphism and as an anamorphism.) So if you have such a class of special catamorphisms, you can compose them.
If we consider semantic equivalence, the composition of two catamorphisms is a catamorphism, when the first one is a hylomorphism:
cata1 . hylo1 = cata2
For example (Haskell):
sum . map (^2) = foldl' (\x y -> x + y^2) 0

need to know what <*> <$> and . do in haskell

What are these operators doing?
(.) :: (b -> c) -> (a -> b) -> a -> c
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
I don't have any idea when I see the signatures. Perhaps some example with a simple and easy to understand explanation will help me.
I am also learning Haskell, and my recommendation is to have a look into Learn You a Haskell for Great Good!, and more precisely:
for (.) read Function composition
for <$> and <*> read Applicative functors
In essence:
(.) is function composition: if you have g :: a -> b and f :: b -> c then f . g is essentially f(g(x)): first use g on an a to get a b and then use f on that b to get a c
<$> takes a function taking an a and returning a b, and a functor that contains an a, and it returns a functor that contains a b. So <$> is the same as fmap :: (a -> b) -> f a -> f b
<*> takes a functor that contains a function taking an a and returning a b, and a functor that contains an a, and it returns a functor that contains a b. So <*> kind of extract the function from a functor and applies it to an arguments also inside a functor, and finally returns the result into a functor
Note the explanations that you find in the book chapters are better than my attempt above
Maybe you learn via examples (like I do), so here are some simple ones you can mess around with in GHCI.
(.) - Function Composition
-- (.) :: (b -> c) -> (a -> b) -> a -> c
> f = (+1)
> g = (*2)
> a = f . g
> a 0
1 -- f( g( 0 ) ) or (0 * 2) + 1
> b = g . f
> b 0
2 -- g( f( 0 ) ) or (0 + 1) * 2
<$> - Functor
-- (<$>) :: Functor f => (a -> b) -> f a -> f b
> a = (*2)
> b = Just 4
> a <$> b
Just 8
<*> - Applicative
-- (<*>) :: Applicative f => f (a -> b) -> f a -> f b
> a = Just (*2)
> b = Just 4
> a <*> b
Just 8
I hope that helps.
The (.) operator composes functions. For example, \x -> f (g x) is the same as f . g. You can do this for arbitrary functions, e.g. \x -> f (g (h x)) equals f . g . h.
The <$> and <*> operators are not defined in terms of functionality. Their functionality depends on the actual type f that they are applied on. The <$> operator is an alternative for the fmap function in the Functor library. For example, for the Maybe type it takes the left operand and only applies it if the right operand is a Just value. So in order to find out what these operators do, just have a look at the implementations for the specific types.
I'm a a newbie to Haskell and sometimes Haskell type declarations confuse me, too.
It's easy to get lost at first because the tutorial says that the Capitalization naming convention is usually for type declaration and the camelCase naming convention is usually for variable.
Actually, this belongs to a higher technique in Haskell, may be polymorphism. Just think of f, a and b as some kind type variables - variables that handle type. And class in Haskell is not for Object like OOP but for type. So Functor f means that type f belongs to class Functor and so on.
If you replace these letter a, b, c with some type - called instance - for example String Int Char. It will make sense:
(.) :: (Int -> Char) -> (String -> Int) -> String -> Char
(<$>) :: Functor Maybe => (String -> Int) -> Maybe String -> Maybe Int -- type `Maybe` belongs to class `Functor`
...
While the common uses of <$> and <*> is obscured by the fact that they are in a typeclass, you can usually read the haddock documentation for this information. Use Hoogle if you have a hard time finding to which module a function belongs.

Resources