Your push takes a stack and returns a stack. The first step to making your pop work like that is to take a stack and return a stack -- rather than returning a list, which is a different data type!
pop :: Stack a -> Maybe (Stack a)
pop (Stack []) = Nothing
pop (Stack (x:xs)) = Just (Stack xs)
When combining multiple calls to push, you use $:
push 1 $ push 2 $ push 3 (Stack [])
The ($) function takes an output of push and feeds it to another call to push. So it has a type that accepts a push output and passes it to a push-like function:
($) ::
-- the type of push foo
(Stack a -> Stack a) ->
-- the type of a previous push's output
Stack a ->
-- the output type we want from pushing twice
Stack a
Its implementation is very simple:
f $ stack = f stack
There's one more minor wrinkle you probably wouldn't think of at first: should f $ g $ stack mean (f $ g) $ stack or f $ (g $ stack)? For what we're doing, we want the latter. We have to tell the compiler this, and it can be done with a fixity declaration:
infixr 0 $
This says that repeated applications of $ should group to the right. There's also infixl when things should group to the left, and infix for when the compiler should not automatically group things. You can ignore the number for now.
The second step to making your pop work like push does is to invent a function that takes an output of pop and feeds it to another call to pop. Since pop returns a Maybe (Stack a), we'll need to have it take one of those. For historical reasons, I'm going to pick the name (=<<), but you could pick whatever name you like better -- maybe $+ or something to signify that it's a "souped-up" $. What type should it have? Well, it should take a pop output and feed it to pop. So:
(=<<) ::
-- the type of pop
(Stack a -> Maybe (Stack a)) ->
-- the type of previous pop's output
Maybe (Stack a) ->
-- the output we want from doing two pops
Maybe (Stack a)
Its implementation is a bit more work than ($)'s was, but not much:
f =<< x = case x of
Just stack -> f stack
Nothing -> Nothing -- what else can you do? there's no stack to feed to f
As before, we need a fixity declaration, and as before we want right-associativity.
infixr 1 =<<
Now we can chain calls to pop:
pop =<< pop =<< pop (Stack [1,2,3])
Enjoy!
P.S. Actually $ has a more general type than the one I showed here:
($) :: (a -> b) -> a -> b
It turns out our =<< has a more general type than the one shown above, too:
(=<<) :: (a -> Maybe b) -> Maybe a -> Maybe b
P.P.S. Next you will want to mix push and pop. You will soon discover that there could be very many variants of $ and =<< that you need to write... in each spot below, you could either choose to have a Maybe or not:
(Maybe? a -> Maybe? b) -> Maybe? a -> Maybe? b
Many of these are actually handled by the polymorphism in $'s existing type -- the ones where you make the same choice of Maybe/not Maybe in the first and third location and in the second and fourth location. You might have fun thinking about the remaining possibilities. Can they all be written? (No. Why not?)
In practice most people get most of their work done with just four operations:
($) :: (a -> b) -> a -> b
(=<<) :: (a -> Maybe b) -> Maybe a -> Maybe b
(<$>) :: (a -> b) -> Maybe a -> Maybe b
pure :: a -> Maybe a
(The last one, which doesn't exactly fit the pattern, is used when the conversion you want to do is to remove a Maybe from before an input to a function, without changing its output type.)