Optional is essentially equivalent to Haskell's
Maybe monad, so that's a good starting place for understanding how these "wrapped values" behave.
Monads are ways of modelling some kind of additional behavior that can modify an existing type.
Maybe takes a type, and modifies it so that it can model both a one single instance or no instance of that type.
List modifies a type so that it models a sequence of instances of the given type.
Each of these typeclasses provides a way to apply a given function to the wrapped type, respecting the modifications that the wrapping type makes. For example, in Haskell,
Functors use the
<$> functions (different names for the same thing):
Functor f => (a -> b) -> f a -> f b
This takes a function and applies to to the wrapped elements
fmap (\x -> x + 1) (Just 1) -- Applies (+1) to the inner value, returning (Just 2)
fmap (\x -> x + 1) Nothing -- Applies (+1) to an empty wrapper, returning Nothing
fmap (\x -> x + 1) [1, 2, 3] -- Applies (+1) to all inner values, returning [2, 3, 4]
(\x -> x + 1) <$> [1, 2, 3] -- Same as above
Applicatives use the
Applicative f => f (a -> b) -> f a -> f b
This takes a wrapped function and applies it to the wrapped elements
(Just (\x -> x + 1)) <*> (Just 1) -- Returns (Just 2)
(Just (\x -> x + 1)) <*> Nothing -- Returns Nothing
Nothing <*> (Just 1) -- Returns Nothing
[(*2), (*4)] <*> [1, 2] -- Returns [2, 4, 4, 8]
There are two relevant functions in the Monad typeclass:
Monad m => a -> m a
Monad m => m a -> (a -> m b) -> m b (pronounced "bind")
return function bears no resemblance to the one you may be familiar with in C-style languages. It takes a raw, unwrapped value, and wraps it up in the desired monadic type.
makeJust :: a -> Maybe a
makeJust x = return x
let foo = makeJust 10 -- returns (Just 10)
The bind function lets you temporarily unwrap the inner elements of a Monad and pass them to a function that performs some action that wraps them back UP in the same monad. This can be used with the
return function in trivial cases:
[1, 2, 3, 4] >>= (\x -> return (x + 1)) -- Returns [2, 3, 4, 5]
(Just 1) >>= (\x -> return (x + 1)) -- Returns (Just 2)
Nothing >>= (\x -> return (x + 1)) -- Returns Nothing
Where it gets interesting is when you have functions to chain together that don't require you to use
return. I like to use
putStrLn as examples, which have the following signatures using the
String -> IO ()
You can call these functions like so:
getLine >>= (\x -> putStrLn x) -- Gets a line from IO and prints it to the console
getLine >>= putStrLn -- With currying, this is the same as above
You could also use the
>>= function to chain a few operations together.
-- Reads a line from IO, converts to a number, adds 10 and prints it
getLine >>= (return . read) >>= (return . (+10)) >>= putStrLn . show