parent
0c1f5319e0
commit
48e35067aa
20 changed files with 777 additions and 0 deletions
@ -0,0 +1,46 @@ |
|||||||
|
# 26 Monad Transformers |
||||||
|
|
||||||
|
## 26.3 EitherT |
||||||
|
|
||||||
|
see [src/EitherT.hs](./src/EitherT.hs) |
||||||
|
|
||||||
|
## 26.5 StateT |
||||||
|
|
||||||
|
see [src/StateT.hs](./src/StateT.hs) |
||||||
|
|
||||||
|
## 26.8 Wrap it up |
||||||
|
|
||||||
|
see [src/WrapItUp.hs](./src/WrapItUp.hs) |
||||||
|
|
||||||
|
## 26.9 Exercises: Lift More |
||||||
|
|
||||||
|
1. see [src/EitherT.hs](./src/EitherT.hs) |
||||||
|
2. see [src/StateT.hs](./src/StateT.hs) |
||||||
|
|
||||||
|
## 26.10 Exercises: Some Instances |
||||||
|
|
||||||
|
1. see [src/MaybeT.hs](./src/MaybeT.hs) |
||||||
|
2. see [src/ReaderT.hs](./src/ReaderT.hs) |
||||||
|
3. see [src/StateT.hs](./src/StateT.hs) |
||||||
|
|
||||||
|
## 26.12 Hypothetical Exercise |
||||||
|
|
||||||
|
see [src/Hypo.hs](./src/Hypo.hs) |
||||||
|
|
||||||
|
## 26.14 Chapter Exercises |
||||||
|
|
||||||
|
### Write the code |
||||||
|
|
||||||
|
see [src/WriteTheCode.hs](./src/WriteTheCode.hs) |
||||||
|
|
||||||
|
### Fix the code |
||||||
|
|
||||||
|
see [src/FixTheCode.hs](./src/FixTheCode.hs) |
||||||
|
|
||||||
|
### Hit counter |
||||||
|
|
||||||
|
see [hit-counter/Main.hs](./hit-counter/Main.hs) |
||||||
|
|
||||||
|
### Morra |
||||||
|
|
||||||
|
see [morra/src/Morra.hs](./morra/src/Morra.hs) |
@ -0,0 +1,57 @@ |
|||||||
|
{-# LANGUAGE OverloadedStrings #-} |
||||||
|
|
||||||
|
module Main where |
||||||
|
|
||||||
|
import Control.Monad.Trans.Class |
||||||
|
import Control.Monad.Trans.Reader |
||||||
|
import Data.IORef |
||||||
|
import qualified Data.Map as M |
||||||
|
-- import Data.Maybe (fromMaybe) |
||||||
|
import Data.Text.Lazy (Text) |
||||||
|
import qualified Data.Text.Lazy as TL |
||||||
|
import System.Environment (getArgs) |
||||||
|
import Web.Scotty.Trans |
||||||
|
|
||||||
|
import Control.Monad.IO.Class |
||||||
|
|
||||||
|
data Config = |
||||||
|
Config { |
||||||
|
counts :: IORef (M.Map Text Integer) |
||||||
|
, prefix :: Text |
||||||
|
} |
||||||
|
|
||||||
|
type Scotty = ScottyT Text (ReaderT Config IO) |
||||||
|
type Handler = ActionT Text (ReaderT Config IO) |
||||||
|
|
||||||
|
bumpBoomp :: Text -> M.Map Text Integer -> (M.Map Text Integer, Integer) |
||||||
|
bumpBoomp k m = let m' = M.insertWith (+) k 1 m |
||||||
|
in (m', (M.!) m' k) |
||||||
|
|
||||||
|
updateHitCounter :: Text -> ReaderT Config IO Integer |
||||||
|
updateHitCounter t = do |
||||||
|
c <- ask |
||||||
|
m <- liftIO $ readIORef (counts c) |
||||||
|
let (m', n) = bumpBoomp t m |
||||||
|
liftIO $ writeIORef (counts c) m' |
||||||
|
return n |
||||||
|
|
||||||
|
app :: Scotty () |
||||||
|
app = |
||||||
|
get "/:key" $ do |
||||||
|
unprefixed <- param "key" |
||||||
|
config <- lift ask |
||||||
|
let key' = mappend (prefix config) unprefixed |
||||||
|
newInteger <- lift $ updateHitCounter key' |
||||||
|
html $ |
||||||
|
mconcat [ "<h1>Success! Count was: " |
||||||
|
, TL.pack $ show newInteger |
||||||
|
, "</h1>" |
||||||
|
] |
||||||
|
|
||||||
|
main :: IO () |
||||||
|
main = do |
||||||
|
[prefixArg] <- getArgs |
||||||
|
counter <- newIORef M.empty |
||||||
|
let config = Config { counts = counter, prefix = TL.pack prefixArg } |
||||||
|
runR = flip runReaderT config |
||||||
|
scottyT 3000 runR app |
@ -0,0 +1,15 @@ |
|||||||
|
name: hit-counter |
||||||
|
version: 0.0.0 |
||||||
|
cabal-version: >= 1.8 |
||||||
|
build-type: Simple |
||||||
|
|
||||||
|
executable hit-counter |
||||||
|
hs-source-dirs: . |
||||||
|
main-is: Main.hs |
||||||
|
ghc-options: -Wall -threaded -O2 -rtsopts -with-rtsopts=-N |
||||||
|
extensions: OverloadedStrings |
||||||
|
build-depends: base >= 4 && < 5 |
||||||
|
, scotty |
||||||
|
, transformers |
||||||
|
, containers |
||||||
|
, text |
@ -0,0 +1,65 @@ |
|||||||
|
# This file was automatically generated by 'stack init' |
||||||
|
# |
||||||
|
# Some commonly used options have been documented as comments in this file. |
||||||
|
# For advanced use and comprehensive documentation of the format, please see: |
||||||
|
# https://docs.haskellstack.org/en/stable/yaml_configuration/ |
||||||
|
|
||||||
|
# Resolver to choose a 'specific' stackage snapshot or a compiler version. |
||||||
|
# A snapshot resolver dictates the compiler version and the set of packages |
||||||
|
# to be used for project dependencies. For example: |
||||||
|
# |
||||||
|
# resolver: lts-3.5 |
||||||
|
# resolver: nightly-2015-09-21 |
||||||
|
# resolver: ghc-7.10.2 |
||||||
|
# resolver: ghcjs-0.1.0_ghc-7.10.2 |
||||||
|
# |
||||||
|
# The location of a snapshot can be provided as a file or url. Stack assumes |
||||||
|
# a snapshot provided as a file might change, whereas a url resource does not. |
||||||
|
# |
||||||
|
# resolver: ./custom-snapshot.yaml |
||||||
|
# resolver: https://example.com/snapshots/2018-01-01.yaml |
||||||
|
resolver: lts-11.12 |
||||||
|
|
||||||
|
# User packages to be built. |
||||||
|
# Various formats can be used as shown in the example below. |
||||||
|
# |
||||||
|
# packages: |
||||||
|
# - some-directory |
||||||
|
# - https://example.com/foo/bar/baz-0.0.2.tar.gz |
||||||
|
# - location: |
||||||
|
# git: https://github.com/commercialhaskell/stack.git |
||||||
|
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a |
||||||
|
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a |
||||||
|
# subdirs: |
||||||
|
# - auto-update |
||||||
|
# - wai |
||||||
|
packages: |
||||||
|
- . |
||||||
|
# Dependency packages to be pulled from upstream that are not in the resolver |
||||||
|
# using the same syntax as the packages field. |
||||||
|
# (e.g., acme-missiles-0.3) |
||||||
|
# extra-deps: [] |
||||||
|
|
||||||
|
# Override default flag values for local packages and extra-deps |
||||||
|
# flags: {} |
||||||
|
|
||||||
|
# Extra package databases containing global packages |
||||||
|
# extra-package-dbs: [] |
||||||
|
|
||||||
|
# Control whether we use the GHC we find on the path |
||||||
|
# system-ghc: true |
||||||
|
# |
||||||
|
# Require a specific version of stack, using version ranges |
||||||
|
# require-stack-version: -any # Default |
||||||
|
# require-stack-version: ">=1.7" |
||||||
|
# |
||||||
|
# Override the architecture used by stack, especially useful on Windows |
||||||
|
# arch: i386 |
||||||
|
# arch: x86_64 |
||||||
|
# |
||||||
|
# Extra directories used by stack for building |
||||||
|
# extra-include-dirs: [/path/to/dir] |
||||||
|
# extra-lib-dirs: [/path/to/dir] |
||||||
|
# |
||||||
|
# Allow a newer minor version of GHC than the snapshot specifies |
||||||
|
# compiler-check: newer-minor |
@ -0,0 +1,2 @@ |
|||||||
|
import Distribution.Simple |
||||||
|
main = defaultMain |
@ -0,0 +1,154 @@ |
|||||||
|
module Morra |
||||||
|
(runGame, Game(..), Player(..), PlayerType(..)) |
||||||
|
where |
||||||
|
|
||||||
|
import Data.Maybe (listToMaybe) |
||||||
|
import qualified Data.Map as M |
||||||
|
|
||||||
|
import Control.Monad (forever) |
||||||
|
import Control.Monad.Trans.State |
||||||
|
import Control.Monad.IO.Class |
||||||
|
import Control.Monad.Trans.Class |
||||||
|
|
||||||
|
import System.Exit (exitSuccess) |
||||||
|
import System.Random (randomRIO) |
||||||
|
import System.Console.ANSI |
||||||
|
import System.IO (hFlush, stdout) |
||||||
|
|
||||||
|
-- Two player game |
||||||
|
-- Player chooses 0-5 and number |
||||||
|
-- AI chooses 0-5 and number |
||||||
|
|
||||||
|
type Source = (Integer, Integer) |
||||||
|
type Table = M.Map Source Integer |
||||||
|
type Guesses = (Integer, Integer) |
||||||
|
|
||||||
|
data PlayerType = Human | AI Guesses Table deriving (Show, Eq) |
||||||
|
|
||||||
|
data Player = Player { |
||||||
|
pType :: PlayerType, |
||||||
|
score :: Integer |
||||||
|
} deriving (Show, Eq) |
||||||
|
|
||||||
|
data Game = Game { |
||||||
|
playerA :: Player, |
||||||
|
playerB :: Player, |
||||||
|
pointsToWin :: Integer |
||||||
|
} deriving (Show, Eq) |
||||||
|
|
||||||
|
-- type Score = (Integer, Integer) |
||||||
|
|
||||||
|
type GameState = StateT Game IO |
||||||
|
|
||||||
|
isPlayerWinner :: (Game -> Player) -> Game -> Bool |
||||||
|
isPlayerWinner p g = (score . p) g >= pointsToWin g |
||||||
|
|
||||||
|
endGameMsg :: Bool -> Bool -> IO () |
||||||
|
endGameMsg True True = putStrLn "Everybody is a winner!" >> exitSuccess |
||||||
|
endGameMsg True False = putStrLn "Player A is a winner!" >> exitSuccess |
||||||
|
endGameMsg False True = putStrLn "Player B is a winner!" >> exitSuccess |
||||||
|
endGameMsg _ _ = return () |
||||||
|
|
||||||
|
gameEnd :: GameState () |
||||||
|
gameEnd = do |
||||||
|
g <- get |
||||||
|
let pA = isPlayerWinner playerA g |
||||||
|
pB = isPlayerWinner playerB g |
||||||
|
liftIO $ endGameMsg pA pB |
||||||
|
return () |
||||||
|
|
||||||
|
handleHumanPlayer :: IO (Integer, Integer) |
||||||
|
handleHumanPlayer = do |
||||||
|
putStr "Fingers and total (f, t): " |
||||||
|
hFlush stdout |
||||||
|
s <- getLine |
||||||
|
clearScreen |
||||||
|
case fmap fst . listToMaybe . reads $ s of |
||||||
|
Nothing -> do |
||||||
|
putStrLn "Incorrect format." |
||||||
|
handleHumanPlayer |
||||||
|
Just a -> return a |
||||||
|
|
||||||
|
handleAIPlayer :: Guesses -> Table -> IO (Integer, Integer) |
||||||
|
handleAIPlayer g t = do |
||||||
|
f <- randomRIO (0,5) |
||||||
|
g <- case M.lookup g t of |
||||||
|
Nothing -> randomRIO (0, 5) |
||||||
|
Just n -> return n |
||||||
|
return (f, f + g) |
||||||
|
|
||||||
|
updateAI :: Player -> Integer -> Player |
||||||
|
updateAI p n = case pType p of |
||||||
|
Human -> p |
||||||
|
AI g@(g1, g2) t -> p { pType = AI g' t' } |
||||||
|
where g' = (g2, n) |
||||||
|
t' = M.insert g n t |
||||||
|
|
||||||
|
updateAIs :: (Integer, Integer) -> GameState () |
||||||
|
updateAIs (gA, gB) = do |
||||||
|
g <- get |
||||||
|
let pA = playerA g |
||||||
|
pB = playerB g |
||||||
|
pA' = updateAI pA gB |
||||||
|
pB' = updateAI pB gA |
||||||
|
g' = g { playerA = pA' } |
||||||
|
put $ g' { playerB = pB' } |
||||||
|
|
||||||
|
handlePlayer :: (Game -> Player) -> GameState (Integer, Integer) |
||||||
|
handlePlayer p = do |
||||||
|
g <- get |
||||||
|
let player = p g |
||||||
|
case pType player of |
||||||
|
Human -> liftIO handleHumanPlayer |
||||||
|
AI g t -> liftIO $ handleAIPlayer g t |
||||||
|
|
||||||
|
handleGuessPlayerA :: Integer -> Integer -> GameState () |
||||||
|
handleGuessPlayerA guess total = |
||||||
|
if (guess == total) |
||||||
|
then do |
||||||
|
g <- get |
||||||
|
let p = playerA g |
||||||
|
s = score p |
||||||
|
put $ g { playerA = (p { score = s + 1 })} |
||||||
|
liftIO $ putStrLn $ "PlayerA guessed correctly!" |
||||||
|
else return () |
||||||
|
|
||||||
|
handleGuessPlayerB :: Integer -> Integer -> GameState () |
||||||
|
handleGuessPlayerB guess total = |
||||||
|
if (guess == total) |
||||||
|
then do |
||||||
|
g <- get |
||||||
|
let p = playerB g |
||||||
|
s = score p |
||||||
|
put $ g { playerB = (p { score = s + 1 })} |
||||||
|
liftIO $ putStrLn $ "PlayerB guessed correctly!" |
||||||
|
else return () |
||||||
|
|
||||||
|
runGame :: GameState () |
||||||
|
runGame = forever $ do |
||||||
|
gameEnd |
||||||
|
liftIO $ putStrLn "Turn PlayerA" |
||||||
|
(f, g) <- handlePlayer playerA |
||||||
|
liftIO $ putStrLn "Turn PlayerB" |
||||||
|
(f', g') <- handlePlayer playerB |
||||||
|
let total = f + f' |
||||||
|
liftIO $ do |
||||||
|
putStrLn "==========================" |
||||||
|
putStr $ "PlayerA showed: " ++ show f |
||||||
|
putStrLn $ " and guessed: " ++ show g |
||||||
|
putStr $ "PlayerB showed: " ++ show f' |
||||||
|
putStrLn $ " and guessed: " ++ show g' |
||||||
|
putStrLn $ "Total was: " ++ show total |
||||||
|
putStrLn "==========================" |
||||||
|
handleGuessPlayerA g total |
||||||
|
handleGuessPlayerB g' total |
||||||
|
liftIO $ putStrLn "==========================" |
||||||
|
-- update any ai |
||||||
|
updateAIs (f, f') |
||||||
|
-- print the player status |
||||||
|
g <- get |
||||||
|
let pA = playerA g |
||||||
|
pB = playerB g |
||||||
|
liftIO $ do |
||||||
|
putStrLn $ "PlayerA: " ++ show pA |
||||||
|
putStrLn $ "PlayerB: " ++ show pB |
@ -0,0 +1,33 @@ |
|||||||
|
name: morra |
||||||
|
version: 0.1.0.0 |
||||||
|
-- synopsis: |
||||||
|
-- description: |
||||||
|
homepage: https://github.com/githubuser/morra#readme |
||||||
|
license: BSD3 |
||||||
|
license-file: LICENSE |
||||||
|
author: Author name here |
||||||
|
maintainer: example@example.com |
||||||
|
copyright: 2018 Author name here |
||||||
|
category: Web |
||||||
|
build-type: Simple |
||||||
|
cabal-version: >=1.10 |
||||||
|
extra-source-files: README.md |
||||||
|
|
||||||
|
library |
||||||
|
hs-source-dirs: lib |
||||||
|
exposed-modules: Morra |
||||||
|
build-depends: base >= 4.7 && < 5 |
||||||
|
, transformers |
||||||
|
, random |
||||||
|
, ansi-terminal |
||||||
|
, containers |
||||||
|
default-language: Haskell2010 |
||||||
|
|
||||||
|
executable morra |
||||||
|
hs-source-dirs: src |
||||||
|
main-is: Main.hs |
||||||
|
default-language: Haskell2010 |
||||||
|
build-depends: base >= 4.7 && < 5 |
||||||
|
, transformers |
||||||
|
, morra |
||||||
|
, containers |
@ -0,0 +1,17 @@ |
|||||||
|
module Main where |
||||||
|
|
||||||
|
import Morra |
||||||
|
import Control.Monad.Trans.State |
||||||
|
import qualified Data.Map as M |
||||||
|
|
||||||
|
initialGame :: Game |
||||||
|
initialGame = Game { |
||||||
|
playerA = Player { pType = Human, score = 0 }, |
||||||
|
playerB = Player { pType = AI (0,0) M.empty, score = 0 }, |
||||||
|
pointsToWin = 3 |
||||||
|
} |
||||||
|
|
||||||
|
main :: IO () |
||||||
|
main = do |
||||||
|
-- putStrLn "Woop" |
||||||
|
evalStateT runGame initialGame |
@ -0,0 +1,65 @@ |
|||||||
|
# This file was automatically generated by 'stack init' |
||||||
|
# |
||||||
|
# Some commonly used options have been documented as comments in this file. |
||||||
|
# For advanced use and comprehensive documentation of the format, please see: |
||||||
|
# https://docs.haskellstack.org/en/stable/yaml_configuration/ |
||||||
|
|
||||||
|
# Resolver to choose a 'specific' stackage snapshot or a compiler version. |
||||||
|
# A snapshot resolver dictates the compiler version and the set of packages |
||||||
|
# to be used for project dependencies. For example: |
||||||
|
# |
||||||
|
# resolver: lts-3.5 |
||||||
|
# resolver: nightly-2015-09-21 |
||||||
|
# resolver: ghc-7.10.2 |
||||||
|
# resolver: ghcjs-0.1.0_ghc-7.10.2 |
||||||
|
# |
||||||
|
# The location of a snapshot can be provided as a file or url. Stack assumes |
||||||
|
# a snapshot provided as a file might change, whereas a url resource does not. |
||||||
|
# |
||||||
|
# resolver: ./custom-snapshot.yaml |
||||||
|
# resolver: https://example.com/snapshots/2018-01-01.yaml |
||||||
|
resolver: lts-11.12 |
||||||
|
|
||||||
|
# User packages to be built. |
||||||
|
# Various formats can be used as shown in the example below. |
||||||
|
# |
||||||
|
# packages: |
||||||
|
# - some-directory |
||||||
|
# - https://example.com/foo/bar/baz-0.0.2.tar.gz |
||||||
|
# - location: |
||||||
|
# git: https://github.com/commercialhaskell/stack.git |
||||||
|
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a |
||||||
|
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a |
||||||
|
# subdirs: |
||||||
|
# - auto-update |
||||||
|
# - wai |
||||||
|
packages: |
||||||
|
- . |
||||||
|
# Dependency packages to be pulled from upstream that are not in the resolver |
||||||
|
# using the same syntax as the packages field. |
||||||
|
# (e.g., acme-missiles-0.3) |
||||||
|
# extra-deps: [] |
||||||
|
|
||||||
|
# Override default flag values for local packages and extra-deps |
||||||
|
# flags: {} |
||||||
|
|
||||||
|
# Extra package databases containing global packages |
||||||
|
# extra-package-dbs: [] |
||||||
|
|
||||||
|
# Control whether we use the GHC we find on the path |
||||||
|
# system-ghc: true |
||||||
|
# |
||||||
|
# Require a specific version of stack, using version ranges |
||||||
|
# require-stack-version: -any # Default |
||||||
|
# require-stack-version: ">=1.7" |
||||||
|
# |
||||||
|
# Override the architecture used by stack, especially useful on Windows |
||||||
|
# arch: i386 |
||||||
|
# arch: x86_64 |
||||||
|
# |
||||||
|
# Extra directories used by stack for building |
||||||
|
# extra-include-dirs: [/path/to/dir] |
||||||
|
# extra-lib-dirs: [/path/to/dir] |
||||||
|
# |
||||||
|
# Allow a newer minor version of GHC than the snapshot specifies |
||||||
|
# compiler-check: newer-minor |
@ -0,0 +1,38 @@ |
|||||||
|
module EitherT where |
||||||
|
|
||||||
|
import Control.Monad.Trans.Class |
||||||
|
|
||||||
|
newtype EitherT e m a = EitherT { runEitherT :: m (Either e a) } |
||||||
|
|
||||||
|
|
||||||
|
instance Functor m => Functor (EitherT e m) where |
||||||
|
fmap f (EitherT ma) = EitherT $ (fmap . fmap) f ma |
||||||
|
|
||||||
|
instance Applicative m => Applicative (EitherT e m) where |
||||||
|
pure a = EitherT $ pure . pure $ a |
||||||
|
(EitherT mf) <*> (EitherT ma) = EitherT $ (fmap (<*>) mf) <*> ma |
||||||
|
|
||||||
|
instance Monad m => Monad (EitherT e m) where |
||||||
|
return = pure |
||||||
|
(EitherT ma) >>= k = EitherT $ do |
||||||
|
a <- ma |
||||||
|
case a of |
||||||
|
Right a' -> runEitherT $ k a' |
||||||
|
Left e -> return $ Left e |
||||||
|
|
||||||
|
swapEither :: Either e a -> Either a e |
||||||
|
swapEither (Left e) = Right e |
||||||
|
swapEither (Right a) = Left a |
||||||
|
|
||||||
|
swapEitherT :: Functor m => EitherT e m a -> EitherT a m e |
||||||
|
swapEitherT (EitherT ma) = EitherT $ fmap swapEither ma |
||||||
|
|
||||||
|
eitherT :: Monad m => (a -> m c) -> (b -> m c) -> EitherT a m b -> m c |
||||||
|
eitherT f g (EitherT ma) = do |
||||||
|
a <- ma |
||||||
|
case a of |
||||||
|
Left e -> f e |
||||||
|
Right a' -> g a' |
||||||
|
|
||||||
|
instance MonadTrans (EitherT e) where |
||||||
|
lift m = EitherT $ fmap Right m |
@ -0,0 +1,23 @@ |
|||||||
|
module FixTheCode where |
||||||
|
|
||||||
|
import Control.Monad.Trans.Maybe |
||||||
|
import Control.Monad |
||||||
|
|
||||||
|
import Control.Monad.IO.Class |
||||||
|
|
||||||
|
isValid :: String -> Bool |
||||||
|
isValid v = '!' `elem` v |
||||||
|
|
||||||
|
maybeExcite :: MaybeT IO String |
||||||
|
maybeExcite = do |
||||||
|
v <- liftIO getLine -- added liftIO |
||||||
|
guard $ isValid v |
||||||
|
return v |
||||||
|
|
||||||
|
doExcite :: IO () |
||||||
|
doExcite = do |
||||||
|
putStrLn "say something excite!" |
||||||
|
excite <- runMaybeT maybeExcite -- added runMaybeT |
||||||
|
case excite of |
||||||
|
Nothing -> putStrLn "MOAR EXCITE" |
||||||
|
Just e -> putStrLn $ "Good, was very excite: " ++ e |
@ -0,0 +1,20 @@ |
|||||||
|
module Hypo where |
||||||
|
|
||||||
|
import Control.Monad.Trans.Reader |
||||||
|
import Control.Monad.Trans.Maybe |
||||||
|
|
||||||
|
-- r -> Maybe a |
||||||
|
foo :: Int -> ReaderT Int Maybe Int |
||||||
|
foo a = return a |
||||||
|
|
||||||
|
|
||||||
|
-- (Reader r) (Maybe a) |
||||||
|
-- r -> Maybe a |
||||||
|
bar :: Int -> MaybeT (Reader Int) Int |
||||||
|
bar a = return a |
||||||
|
|
||||||
|
-- looks pretty much the same to me... |
||||||
|
main :: IO () |
||||||
|
main = do |
||||||
|
print $ runReaderT (foo 1) 5 |
||||||
|
print $ runReader (runMaybeT (bar 1)) 5 |
@ -0,0 +1,30 @@ |
|||||||
|
module MaybeT where |
||||||
|
|
||||||
|
import Control.Monad.Trans.Class |
||||||
|
import Control.Monad.IO.Class |
||||||
|
|
||||||
|
data MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) } |
||||||
|
|
||||||
|
instance Functor m => Functor (MaybeT m) where |
||||||
|
fmap f (MaybeT ma) = MaybeT $ (fmap . fmap) f ma |
||||||
|
|
||||||
|
instance Applicative m => Applicative (MaybeT m) where |
||||||
|
pure = MaybeT . pure . Just |
||||||
|
|
||||||
|
(MaybeT mf) <*> (MaybeT ma) = MaybeT $ |
||||||
|
(fmap (<*>) mf) <*> ma |
||||||
|
|
||||||
|
instance Monad m => Monad (MaybeT m) where |
||||||
|
return = pure |
||||||
|
|
||||||
|
(MaybeT ma) >>= k = MaybeT $ do |
||||||
|
a <- ma |
||||||
|
case a of |
||||||
|
Nothing -> return Nothing |
||||||
|
Just a' -> runMaybeT $ k a' |
||||||
|
|
||||||
|
instance MonadTrans (MaybeT) where |
||||||
|
lift m = MaybeT $ fmap Just m |
||||||
|
|
||||||
|
instance MonadIO m => MonadIO (MaybeT m) where |
||||||
|
liftIO = lift . liftIO |
@ -0,0 +1,102 @@ |
|||||||
|
module Morra where |
||||||
|
|
||||||
|
import Control.Monad (forever) |
||||||
|
import Control.Monad.Trans.State |
||||||
|
import Control.Monad.IO.Class |
||||||
|
|
||||||
|
|
||||||
|
import System.Exit (exitSuccess) |
||||||
|
import System.Random (randomRIO) |
||||||
|
|
||||||
|
-- Player chooses 0-5 and number |
||||||
|
-- AI chooses 0-5 and number |
||||||
|
|
||||||
|
pointsToWin :: Integer |
||||||
|
pointsToWin = 3 |
||||||
|
|
||||||
|
type Score = (Integer, Integer) |
||||||
|
type GameState = StateT Score IO |
||||||
|
|
||||||
|
iWin :: Score -> Bool |
||||||
|
iWin = (>= pointsToWin) . fst |
||||||
|
|
||||||
|
aiWin :: Score -> Bool |
||||||
|
aiWin = (>= pointsToWin) . snd |
||||||
|
|
||||||
|
gameWin :: GameState () |
||||||
|
gameWin = do |
||||||
|
s <- get |
||||||
|
if iWin s then |
||||||
|
liftIO $ do |
||||||
|
putStrLn "You win!" |
||||||
|
exitSuccess |
||||||
|
else return () |
||||||
|
|
||||||
|
gameLose :: GameState () |
||||||
|
gameLose = do |
||||||
|
s <- get |
||||||
|
if aiWin s then |
||||||
|
liftIO $ do |
||||||
|
putStrLn "AI wins!" |
||||||
|
exitSuccess |
||||||
|
else return () |
||||||
|
|
||||||
|
getFingers :: IO Integer |
||||||
|
getFingers = do |
||||||
|
putStrLn "How many fingers will you show (0-5)?" |
||||||
|
response <- getLine |
||||||
|
if response `elem` (map show [0..5]) |
||||||
|
then return $ read response |
||||||
|
else getFingers |
||||||
|
|
||||||
|
getGuess :: IO Integer |
||||||
|
getGuess = do |
||||||
|
putStrLn "How many fingers will be shown in total (0-10)?" |
||||||
|
response <- getLine |
||||||
|
if response `elem` (map show [0..10]) |
||||||
|
then return $ read response |
||||||
|
else getGuess |
||||||
|
|
||||||
|
getAIData :: IO (Integer, Integer) |
||||||
|
getAIData = do |
||||||
|
f <- randomRIO (0,5) |
||||||
|
g <- randomRIO (0,5) |
||||||
|
return (f, f + g) |
||||||
|
|
||||||
|
handleCorrectGuess :: GameState () |
||||||
|
handleCorrectGuess = do |
||||||
|
(hs,as) <- get |
||||||
|
put (hs + 1, as) |
||||||
|
|
||||||
|
handleAICorrectGuess :: GameState () |
||||||
|
handleAICorrectGuess = do |
||||||
|
(hs, as) <- get |
||||||
|
put (hs, as + 1) |
||||||
|
|
||||||
|
runGame :: GameState () |
||||||
|
runGame = forever $ do |
||||||
|
gameWin |
||||||
|
gameLose |
||||||
|
(f,g) <- liftIO $ do |
||||||
|
f <- getFingers |
||||||
|
g <- getGuess |
||||||
|
return (f,g) |
||||||
|
(f', g') <- liftIO getAIData |
||||||
|
if f == g + g' then handleCorrectGuess else return () |
||||||
|
if f' == g + g' then handleAICorrectGuess else return () |
||||||
|
s <- get |
||||||
|
liftIO $ do |
||||||
|
putStrLn $ "==================" |
||||||
|
putStrLn $ "AI showed: " ++ show f' |
||||||
|
putStrLn $ "AI guessed: " ++ show g' |
||||||
|
putStrLn $ "Total was: " ++ show (f + f') |
||||||
|
putStrLn $ "------------------" |
||||||
|
putStrLn $ "Score is: " ++ show s |
||||||
|
putStrLn $ "==================" |
||||||
|
|
||||||
|
|
||||||
|
initialState :: Score |
||||||
|
initialState = (0, 0) |
||||||
|
|
||||||
|
main :: IO () |
||||||
|
main = evalStateT runGame initialState |
@ -0,0 +1,28 @@ |
|||||||
|
module ReaderT where |
||||||
|
|
||||||
|
import Control.Monad.Trans.Class |
||||||
|
import Control.Monad.IO.Class |
||||||
|
|
||||||
|
data ReaderT r m a = ReaderT { runReaderT :: r -> m a } |
||||||
|
|
||||||
|
instance Functor m => Functor (ReaderT r m) where |
||||||
|
fmap f (ReaderT rma) = ReaderT $ \r -> fmap f (rma r) |
||||||
|
|
||||||
|
instance Applicative m => Applicative (ReaderT r m) where |
||||||
|
pure = ReaderT . const . pure |
||||||
|
|
||||||
|
(ReaderT rmf) <*> (ReaderT rma) = ReaderT $ \r -> |
||||||
|
(rmf r) <*> (rma r) |
||||||
|
|
||||||
|
instance Monad m => Monad (ReaderT r m) where |
||||||
|
return = pure |
||||||
|
|
||||||
|
(ReaderT rma) >>= k = ReaderT $ \r -> do |
||||||
|
a <- rma r |
||||||
|
runReaderT (k a) r |
||||||
|
|
||||||
|
instance MonadTrans (ReaderT r) where |
||||||
|
lift = ReaderT . const |
||||||
|
|
||||||
|
instance MonadIO m => MonadIO (ReaderT r m) where |
||||||
|
liftIO = lift . liftIO |
@ -0,0 +1,29 @@ |
|||||||
|
module StateT where |
||||||
|
|
||||||
|
import Control.Monad.Trans.Class |
||||||
|
import Control.Monad.IO.Class |
||||||
|
|
||||||
|
data StateT s m a = StateT { runStateT :: s -> m (a, s) } |
||||||
|
|
||||||
|
instance Functor m => Functor (StateT s m) where |
||||||
|
fmap f st = StateT $ \s -> |
||||||
|
fmap (\(a, s') -> (f a, s')) $ runStateT st s |
||||||
|
|
||||||
|
instance Monad m => Applicative (StateT s m) where |
||||||
|
pure a = StateT $ \s -> pure (a, s) |
||||||
|
stf <*> sta = StateT $ \s -> do |
||||||
|
(f, s') <- runStateT stf s |
||||||
|
(a, s'') <- runStateT sta s' |
||||||
|
return (f a, s'') |
||||||
|
|
||||||
|
instance Monad m => Monad (StateT s m) where |
||||||
|
return = pure |
||||||
|
st >>= k = StateT $ \s -> do |
||||||
|
(a, s') <- runStateT st s |
||||||
|
runStateT (k a) s' |
||||||
|
|
||||||
|
instance MonadTrans (StateT s) where |
||||||
|
lift m = StateT $ \s -> fmap (flip (,) s) m |
||||||
|
|
||||||
|
instance MonadIO m => MonadIO (StateT s m) where |
||||||
|
liftIO = lift . liftIO |
@ -0,0 +1,9 @@ |
|||||||
|
module WrapItUp where |
||||||
|
|
||||||
|
import Control.Monad.Trans.Maybe |
||||||
|
import Control.Monad.Trans.Except |
||||||
|
import Control.Monad.Trans.Reader |
||||||
|
|
||||||
|
embedded :: MaybeT (ExceptT String (ReaderT () IO)) Int |
||||||
|
embedded = |
||||||
|
MaybeT (ExceptT (ReaderT (const $ return (Right (Just 1))))) |
@ -0,0 +1,44 @@ |
|||||||
|
module WriteTheCode where |
||||||
|
|
||||||
|
import Control.Monad.Trans.Reader |
||||||
|
import Control.Monad.Trans.State |
||||||
|
import Control.Monad.Identity |
||||||
|
|
||||||
|
import Control.Monad.IO.Class |
||||||
|
|
||||||
|
-- 1 |
||||||
|
rDec :: Num a => Reader a a |
||||||
|
rDec = do |
||||||
|
a <- ask |
||||||
|
return $ a - 1 |
||||||
|
|
||||||
|
-- 2 |
||||||
|
rDec' :: Num a => Reader a a |
||||||
|
rDec' = ask >>= return . flip (-) 1 |
||||||
|
|
||||||
|
-- 3 |
||||||
|
rShow :: Show a => ReaderT a Identity String |
||||||
|
rShow = do |
||||||
|
a <- ask |
||||||
|
return $ show a |
||||||
|
|
||||||
|
-- 4 |
||||||
|
rShow' :: Show a => ReaderT a Identity String |
||||||
|
rShow' = ask >>= return . show |
||||||
|
|
||||||
|
-- 5 |
||||||
|
rPrintAndInc :: (Num a, Show a) => ReaderT a IO a |
||||||
|
rPrintAndInc = do |
||||||
|
a <- ask |
||||||
|
liftIO $ putStr "Hi: " |
||||||
|
liftIO $ print a |
||||||
|
return $ a + 1 |
||||||
|
|
||||||
|
-- 6 |
||||||
|
sPrintIncAccum :: (Num a, Show a) => StateT a IO String |
||||||
|
sPrintIncAccum = do |
||||||
|
a <- get |
||||||
|
liftIO $ putStr "Hi: " |
||||||
|
liftIO $ print a |
||||||
|
put (a + 1) |
||||||
|
return $ show a |
Loading…
Reference in new issue