- pure
def add(a: int, b: int) -> int:
return a + b
def square(a: int) -> int:
return a * a
- impure
def read_int() -> int:
return int(input())
def print_int(a: int) -> None:
print(a)
return None
All functions are pure
add :: Integer -> Integer -> Integer
add = \a -> \b -> a + b
square :: Integer -> Integer
square = \a -> a * a
tips: currify,
$(N \times N) \to N$ to$N \to N \to N$
tips: type can be infered automatically
What about IO function???
an example:
main = do
putStrLn "Greetings! What is your name?"
inpStr <- getLine
putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!"
do?? it is syntax sugar
let's take a even simpler example
main = putStrLn "Hello World"
what is putStrLn
??
putStrLn :: String -> IO ()
tips:
()
is unit type, it has only a single value() :: ()
So what is IO
then?
type IO :: * -> *
where *
is just type, so IO is a mapping from type to type
more example...
type Integer :: *
type [] :: * -> *
type Maybe :: * -> *
type Maybe Integer :: *
IO
is a monad
Imagine, all the world out side of the program is named World
, so maybe we can set...
putStrLn :: String -> World -> World
getLine :: World -> (World, String)
then we can concat all IO operator one by one like a pipe.
But how to abstract them ?
if we set
IO a = World -> (World, a)
then we have
putStrLn :: String -> IO ()
getLine :: IO String
Great!
Sometimes we do not want set all the world as the state, but specific data structure.
type Control.Monad.State.State :: * -> * -> *
For any type a
, Control.Monad.State.State a :: * -> *
is a monad.
For example
Control.Monad.State.State Integer :: * -> *
which map a type to another type operating on a integer as the state.
type Functor :: (* -> *) -> Constraint
class Functor f where
fmap :: (a -> b) -> f a -> f b
-- Functors must preserve identity morphisms
-- fmap id = id
-- Functors preserve composition of morphisms
-- fmap (f . g) == fmap f . fmap g
type Monad :: (* -> *) -> Constraint
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
-- return a >>= k = k a
-- m >>= return = m
-- m >>= (\x -> k x >>= h) = (m >>= k) >>= h
-- every monad is also a functor
-- fmap fab ma = ma >>= (return . fab)
Examples
helloWorld :: IO ()
helloWorld = (putStr "Hello") >> (putStr "World") >> (return 2333) >> (putStrLn "!!!")
echo :: IO ()
echo = (getLine) >>= \x -> (putStrLn x)
helloWorld :: IO ()
helloWorld = do
putStr "Hello"
putStr "World"
putStrLn "!!!"
echo :: IO ()
echo = do
x <- getLine
putStrLn x
for other monad ...
import Control.Monad.State
stateOperation :: State Int Int
stateOperation = do
modify (\x -> x + 1)
get
runState stateOperation 0
runState (stateOperation >> stateOperation) 0
What if multiple monad combined?? -> Monad Transformer
import Control.Monad.State
import System.IO
singleUpdate :: StateT Int IO ()
singleUpdate = do
input <- liftIO readLn
modify (+ input)
currentState <- get
liftIO $ putStrLn $ show currentState
multipleUpdate :: StateT Int IO ()
multipleUpdate = do
singleUpdate
singleUpdate
singleUpdate
singleUpdate
singleUpdate
main :: IO ()
main = do
finalState <- execStateT multipleUpdate 0
return ()