Last active
January 22, 2017 04:52
-
-
Save rayshih/54bd0ff03769361728f3b0600767eae8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| data Program next = Output String next | Done | |
| p = Output "Hello World" (Output "Good Bye" Done) | |
| -- an interpreter to run | |
| run :: Program (Program (Program next)) -> IO () | |
| run (Output s1 (Output s2 Done)) = do | |
| putStrLn s1 | |
| putStrLn s2 | |
| -- and then you can intepret it any way you want | |
| -- for example to an array | |
| test :: Program (Program (Program next)) -> [String] | |
| test (Output s1 (Output s2 Done)) = [s1, s2] | |
| main :: IO () | |
| main = run p | |
| -- main = print $ test p | |
| -- but the type is not useful | |
| -- so try to make it better | |
| {- | |
| run :: Program next -> IO () | |
| run (Output s next) = do | |
| putStrLn s | |
| run next | |
| run Done = return () | |
| -} | |
| -- but we cannot do that be cause | |
| {- | |
| run (Output s1 (Output s2 Done)) | |
| -} | |
| -- doesn't have same type of this | |
| {- | |
| run (Output s2 Done) | |
| -} | |
| -- So how? fix point come to help |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- what is fix point? | |
| -- given a function f | |
| -- when x = f x | |
| -- then x is the fix point of x | |
| -- | |
| -- a hard example: y combinator | |
| yCom g = f where f = g f | |
| genFib f 0 = 0 | |
| genFib f 1 = 1 | |
| genFib f n = f (n - 1) + f (n - 2) | |
| fib = yCom genFib | |
| main = print $ map fib [0..6] | |
| -- an even harder example: fix point of functor | |
| -- that is | |
| -- a data type which is a fix point of functor (which is type function |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| data Fix f = Fx (f (Fix f)) | |
| data IS next = Output String next | Done | |
| program :: Fix IS | |
| program = | |
| (Fx (Output "Hello" | |
| (Fx (Output "World" | |
| (Fx Done))))) | |
| runProgram :: Fix IS -> IO () | |
| runProgram (Fx (Output s next)) = do | |
| putStrLn s | |
| runProgram next | |
| runProgram (Fx Done) = return () | |
| main = runProgram program | |
| {- | |
| Nice! now we can intepret program with any length of instructions | |
| but where is the input? | |
| -} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| data Fix f = Fx (f (Fix f)) | |
| -- in order to handle input | |
| -- 1. we need add another instruction | |
| -- 2. and for pass the read string to next instruction | |
| -- we need to change `next` to `(String -> next)` | |
| data IS next = Input (String -> next) | |
| | Output String next | |
| | Done | |
| program :: Fix IS | |
| program = | |
| (Fx (Input (\s -> | |
| (Fx (Output ("Hello " ++ s) | |
| (Fx Done)))))) | |
| runProgram :: Fix IS -> IO () | |
| runProgram (Fx (Input genNext)) = do | |
| s <- getLine | |
| runProgram $ genNext s | |
| runProgram (Fx (Output s next)) = do | |
| putStrLn s | |
| runProgram next | |
| runProgram (Fx Done) = return () | |
| main = runProgram program | |
| -- cool! now we can do some user interaction | |
| -- but the program construction is really unpleasant | |
| -- we can make Fix IS a monad!! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- because monad need a `return` | |
| -- so we need to move the terminal node to Fix | |
| -- and now we can rename Fix to Free | |
| data Free f r = Free (f (Free f r)) | Pure r | |
| data IS next = Input (String -> next) | |
| | Output String next | |
| -- in order to declare the monad instance for Fix IS | |
| -- we need to make IS a Functor | |
| instance Functor IS where | |
| fmap f (Input genNext) = Input $ f . genNext | |
| fmap f (Output s next) = Output s $ f next | |
| -- and then the monad | |
| -- but to define monad instance we need functor and applicative defined | |
| -- (this part can be skipped) | |
| instance (Functor f) => Functor (Free f) where | |
| fmap f (Free x) = Free $ fmap (fmap f) x | |
| fmap f (Pure r) = Pure $ f r | |
| instance (Functor f) => Applicative (Free f) where | |
| pure = Pure | |
| Pure f <*> Pure a = Pure $ f a | |
| Pure f <*> Free ma = Free $ fmap f <$> ma | |
| Free ma <*> b = Free $ (<*> b) <$> ma -- TODO figure out what is this | |
| -- then monad | |
| -- this is the most insteresting part | |
| instance (Functor f) => Monad (Free f) where | |
| (Free x) >>= f = Free (fmap (>>= f) x) | |
| (Pure r) >>= f = f r | |
| -- what does this do? | |
| -- assume that | |
| -- | |
| -- f :: s -> Free IS () | |
| -- | |
| -- and we have a free monad | |
| -- | |
| -- fm = (Free (Input (\s -> Pure s))) | |
| -- | |
| -- then | |
| -- | |
| -- fm >>= f | |
| -- | |
| -- // by free monad definition (Free part | |
| -- = Free (fmap (>>= f) (Input (\s -> Pure s))) | |
| -- | |
| -- // by IS Functor definition | |
| -- = Free (Input (\s -> (Pure s >>= f))) | |
| -- | |
| -- // by free monad definition (Pure part | |
| -- = Free (Input (\s -> f s)) | |
| -- | |
| -- so the what the monad does is | |
| -- | |
| -- carry the monad generator to the end of the "Free Monad List" | |
| -- and apply the generator with the value r and got the "next" instruction | |
| -- now we have monad so we can do this! | |
| input = (Free (Input (\s -> Pure s))) | |
| output s = (Free (Output s (Pure ()))) | |
| program :: Free IS () | |
| program = do | |
| s <- input | |
| output $ "Hello " ++ s | |
| runProgram :: Free IS () -> IO () | |
| runProgram (Free (Input genNext)) = do | |
| s <- getLine | |
| runProgram $ genNext s | |
| runProgram (Free (Output s next)) = do | |
| putStrLn s | |
| runProgram next | |
| runProgram (Pure r) = return r | |
| main = runProgram program | |
| -- Now there is a free monad, but seems complicated, | |
| -- can we simplify it? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| {-# LANGUAGE DeriveFunctor #-} | |
| data Free f r = Free (f (Free f r)) | Pure r deriving (Functor) | |
| data IS next = Input (String -> next) | |
| | Output String next | |
| deriving (Functor) | |
| -- gch is so powerful to derive Functor automatically!! | |
| instance (Functor f) => Applicative (Free f) where | |
| pure = Pure | |
| Pure f <*> Pure a = Pure $ f a | |
| Pure f <*> Free ma = Free $ fmap f <$> ma | |
| Free ma <*> b = Free $ (<*> b) <$> ma -- TODO figure out what is this | |
| instance (Functor f) => Monad (Free f) where | |
| (Free x) >>= f = Free (fmap (>>= f) x) | |
| (Pure r) >>= f = f r | |
| -- and reduce duplicte code | |
| liftF i = Free (fmap Pure i) | |
| input = liftF $ Input (\s -> s) | |
| output s = liftF $ Output s () | |
| program :: Free IS () | |
| program = do | |
| s <- input | |
| output $ "Hello " ++ s | |
| runProgram :: Free IS () -> IO () | |
| runProgram (Free (Input genNext)) = do | |
| s <- getLine | |
| runProgram $ genNext s | |
| runProgram (Free (Output s next)) = do | |
| putStrLn s | |
| runProgram next | |
| runProgram (Pure r) = return r | |
| main = runProgram program | |
| -- futhermore there is a Free Monad built by others, we can just use it! | |
| -- so what are the benefits? pure!!! | |
| -- let's go back to the problem of first file |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| {-# LANGUAGE DeriveFunctor #-} | |
| data Free f r = Free (f (Free f r)) | Pure r deriving (Functor) | |
| instance (Functor f) => Applicative (Free f) where | |
| pure = Pure | |
| Pure f <*> Pure a = Pure $ f a | |
| Pure f <*> Free ma = Free $ fmap f <$> ma | |
| Free ma <*> b = Free $ (<*> b) <$> ma -- TODO figure out what is this | |
| instance (Functor f) => Monad (Free f) where | |
| (Free x) >>= f = Free (fmap (>>= f) x) | |
| (Pure r) >>= f = f r | |
| data IS next = Input (String -> next) | |
| | Output String next | |
| deriving (Functor) | |
| liftF i = Free (fmap Pure i) | |
| input = liftF $ Input (\s -> s) | |
| output s = liftF $ Output s () | |
| -- let's do some computation | |
| readInt :: Free IS Int | |
| readInt = read <$> input | |
| program :: Free IS () | |
| program = do | |
| a <- readInt | |
| b <- readInt | |
| output $ show $ a + b | |
| runProgram :: Free IS () -> IO () | |
| runProgram (Free (Input genNext)) = do | |
| s <- getLine | |
| runProgram $ genNext s | |
| runProgram (Free (Output s next)) = do | |
| putStrLn s | |
| runProgram next | |
| runProgram (Pure r) = return r | |
| -- we can write different interpreter | |
| testProgram :: Free IS () -> [String] -> [String] | |
| testProgram (Free (Input genNext)) (x : xs) = | |
| testProgram (genNext x) xs | |
| testProgram (Free (Output s next)) xs = | |
| s : testProgram next xs | |
| testProgram (Pure r) _ = [] | |
| test1 = if testProgram program ["1", "2"] == ["3"] | |
| then putStrLn "PASS" | |
| else putStrLn "FAIL" | |
| test2 = if testProgram program ["3", "-2"] == ["3"] | |
| then putStrLn "PASS" | |
| else putStrLn "FAIL" | |
| main = do | |
| test1 | |
| test2 | |
| -- Yeah!! That's right, we can interpret the program as a pure function! | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment