IMO these things are best understood through the lens of Haskell's `Maybe` type.

Very simply, a `Maybe Int` for example can be either `Just 5` or `Nothing`. At its most basic form, it can be used with pattern matching. For example here's a haskell function that adds 2 to a Maybe if it's there:

add2 :: Maybe Int -> Maybe Int

add2 (Just n) = Just (n + 2)

add2 Nothing = Nothing

You can see we had to use pattern matching to "unwrap" the actual value of the maybe so we could add 2 to it. This is pretty inconvenient and pretty annoying especially if you're trying to do something more complex than simply adding 2. That's where `Functor` comes in.

`Functor` allows you to apply a function to the value inside the functor and get back the modified functor by using the `fmap` function. Here's what that definition looks like:

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

This definition is a bit complex so I'll break it down. This is saying "fmap is a function which is defined for any `Functor` `f`. This function accepts a different function from `a` to `b`; and a functor containing an `a`; and returns a functor containing a `b`." `a` and `b` can be any type. What makes something a functor at the very base level is whether or not this function is defined. You can think about it like mapping over a list in any other language because that's exactly what `fmap` is defined as for lists.

So in our `Maybe` example, we can use it like so:

add2 :: Maybe Int -> Maybe Int

add2 x = fmap (+ 2) x

-- or

add2 x = (+ 2) <$> x -- <$> is just an infix version of fmap; same exact function.

Much more convenient, and as an added bonus it looks a bit more like regular function application (in haskell anyway).

We're not quite to monads yet because there's a step between `Functor` and `Monad` and that's called `Applicative`. Instead of starting with the (somewhat confusing) definition, let me pose a question: what if I have a function with more than one argument that I want to pass `Maybe`s into? Like what if I wanted to add two `Maybe` values together? That's the problem applicative solves.

With functors we can do:

(+ 2) <$> x

But with an applicative instance we can do:

(+) <$> maybeA <*> maybeB

the result of which will be a `Maybe` containing the result of adding the two values inside. If either of the `Maybe` values are `Nothing` it will come out as `Nothing`. And this pattern is extendable for more than just two arguments as well. For example:

functionWithManyNonMaybeArguments <$> maybeA <*> maybeB <*> maybeC <*> maybeD

So to quickly summarize: functors allow you to apply a function to the inside of one "wrapped" value (like `Maybe`). Applicatives allow you to apply a function of many arguments to many "wrapped" values. Now, here's where we get to monads. Here's a situation you might be in:

someFn :: a -> Maybe c

someMaybe :: Maybe a

How would you feed that `someMaybe` value into the `someFn` function? You might guess to use `fmap` from functor, but let's look at what would happen:

fmap someFn someMaybe

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

-- so in this case

fmap :: Functor Maybe => (a -> Maybe c) -> Maybe a -> Maybe (Maybe c)

-- so

fmap someFn someMaybe :: Maybe (Maybe c)

Oof, double-wrapped `Maybe`; That's not right. This is the problem `Monad` solves with the `bind` function or `>>=` operator. The correct code for this would be:

bind :: Monad m => m a -> (a -> m b) -> m b -- again, >>= is just an infix version of bind; exact same thing.

someMaybe >>= someFn :: Maybe c

Also worth noting that for something to be a `Monad` it must also be `Applicative` and for something to be `Applicative` it must also be a `Functor`. So all monads are also applicatives and functors.

Sorry if you don't know any Haskell because this is pretty complex and very haskell-focused, but I kinda figured you wouldn't be asking these questions without a passing familiarity with some haskell. Feel free to ask any questions; I love talking about this stuff!