“Monads” apply a function that returns a wrapped value.
“Maybe” is a Functor, an Applicative, and a Monad.
Suppose “half” is a function that only works on even numbers:
const { Option, Some, None, Result } = require('@es2/result-option') // 0.2.0
const half = (num) => (num % 2 === 0)
? Some(num / 2)
: None()
const test1 = Some(100).chain(half).extractOr(0)
console.log('test1: ', test1) // 50
const test1_1 = Some(2).chain(half).extractOr(0)
console.log('test1_1: ', test1_1) // 1
const test2 = Some(3).chain(half).extractOr(0)
console.log('test2: ', test2) // 0
const test3 = Some(4).chain(half).extractOr(0)
console.log('test3: ', test3) // 2
const test4 = Some(None).chain(half).extractOr(0)
console.log('test4: ', test4) // 0
const test5 = Some(20).chain(half).chain(half).chain(half).extractOr(0)
console.log('test5: ', test5) // 0
const test6 = Some(20).chain(half).chain(half).extractOr(0)
console.log('test6: ', test6) // 5
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
instance Monad Maybe where
Nothing >>= func = Nothing
Just val >>= func = func val
half x = if even x
then Just (x `div` 2)
else Nothing
> Just 2 >>= half
-- Just 1
> Just 3 >>= half
-- Nothing
> Just 4 >>= half
-- Just 2
> Nothing >>= half
-- Nothing
> Just 20 >>= half >>= half >>= half
-- Nothing
> Just 20 >>= half >>= half
-- Just 5
-- If we have a Just value, we can extract the underlying value it contains through pattern matching.
half x = if even x
then Just (x `div` 2)
else Nothing
zeroAsDefault :: Maybe Int -> Int
zeroAsDefault mx = case mx of
Nothing -> 0
Just x -> x
calc1 = Just 2 >>= half
calc2 = Just 3 >>= half
calc3 = Just 4 >>= half
calc4 = Just 5 >>= half
calc5 = Just 6 >>= half
main = do
(\x -> putStrLn ("just 2: " ++ show x )) ( zeroAsDefault calc1 ) -- 1
(\x -> putStrLn ("just 3: " ++ show x )) ( zeroAsDefault calc2 ) -- 0
(\x -> putStrLn ("just 4: " ++ show x )) ( zeroAsDefault calc3 ) -- 2
(\x -> putStrLn ("just 5: " ++ show x )) ( zeroAsDefault calc4 ) -- 0
(\x -> putStrLn ("just 6: " ++ show x )) ( zeroAsDefault calc5 ) -- 3
The Either monad is sometimes used to represent a value which is either correct or an error, the Left constructor is used to hold an error value and the Right constructor is used to hold a correct value.
-
import Text.Read
validateAge :: String -> Either String Int
validateAge input =
case readMaybe input of
Nothing -> Left "Invalid input. Not a number"
Just age -> case age of
_ | age < 0 -> Left "Error: Invalid age. It must be greater than zero."
_ | age <= 18 -> Left "Error: Below legal age to sign the contract."
_ | age > 200 -> Left "Error: Invalid age. Impossible age."
_ -> Right age
main :: IO ()
main = do
(\x -> putStrLn ("-100: " ++ show x )) ( validateAge "-100" )
(\x -> putStrLn ("16: " ++ show x )) ( validateAge "16" )
The IO monad wraps computations in the following context: “This computation can read information from or write information to the terminal, file system, operating system, and/or network”.
If you want to get user input, print a message to the user, read information from a file, or make a network call, you’ll need to do so within the IO Monad.
Function called main, has type IO (), every program starts in the IO monad.
-
toList :: String -> [Integer]
toList input = read ("[" ++ input ++ "]")
main :: IO ()
main = do
putStrLn "What is your name?" >> getLine
>>= \name -> putStrLn ("Hello, " ++ name ++ "!")
type Login = String
type Password = String
users :: [(Login, Password)]
users =
[ ("admin", "1234")
, ("begemot", "0000")
]
askPassword :: IO Bool
askPassword = do
putStr "Enter Login: "
login <- getLine
putStr "Enter password: "
password <- getLine
return $ maybe False (\p -> p == password) $ lookup login users
main :: IO ()
main = do
passwordIsCorrect <- askPassword
putStrLn $ if passwordIsCorrect
then "Welcome!"
else "Incorrect password"