parent
44e1748264
commit
9ed8fb2f6d
16 changed files with 449 additions and 0 deletions
@ -0,0 +1,10 @@ |
|||||||
|
# Chapter Excercises |
||||||
|
## A warmup stretch |
||||||
|
see src/ReaderPractise |
||||||
|
|
||||||
|
## Rewriting Shawty |
||||||
|
No idea if this is correct. |
||||||
|
|
||||||
|
see shawty-prime/app/Main.hs |
||||||
|
|
||||||
|
I can create a `ReaderT` from `app` and that works, but I can't do the same with `saveURI` and `getURI` and find how to connect it up with `app`. |
@ -0,0 +1,2 @@ |
|||||||
|
# Short Exercise: Warming Up |
||||||
|
see src/warmingup.hs |
@ -0,0 +1,2 @@ |
|||||||
|
# Excercise: Ask |
||||||
|
see src/ask.hs |
@ -0,0 +1,2 @@ |
|||||||
|
# Exercise: Reading Comprehension |
||||||
|
see src/readingcomp.hs |
@ -0,0 +1,2 @@ |
|||||||
|
# Exercise: Reader Monad |
||||||
|
see src/readingcomp.hs |
@ -0,0 +1,30 @@ |
|||||||
|
Copyright Chris Allen (c) 2015 |
||||||
|
|
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above |
||||||
|
copyright notice, this list of conditions and the following |
||||||
|
disclaimer in the documentation and/or other materials provided |
||||||
|
with the distribution. |
||||||
|
|
||||||
|
* Neither the name of Chris Allen nor the names of other |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,2 @@ |
|||||||
|
import Distribution.Simple |
||||||
|
main = defaultMain |
@ -0,0 +1,116 @@ |
|||||||
|
{-# LANGUAGE OverloadedStrings #-} |
||||||
|
|
||||||
|
module Main where |
||||||
|
|
||||||
|
import Control.Monad (replicateM) |
||||||
|
import Control.Monad.IO.Class (liftIO) |
||||||
|
import qualified Data.ByteString.Char8 as BC |
||||||
|
import Data.Text.Encoding (decodeUtf8, encodeUtf8) |
||||||
|
import qualified Data.Text.Lazy as TL |
||||||
|
import qualified Database.Redis as R |
||||||
|
import Network.URI (URI, parseURI) |
||||||
|
import qualified System.Random as SR |
||||||
|
import Web.Scotty |
||||||
|
|
||||||
|
import Control.Monad.Reader (ReaderT(..), runReaderT, ask, lift) |
||||||
|
|
||||||
|
alphaNum :: String |
||||||
|
alphaNum = ['A'..'Z'] ++ ['0'..'9'] |
||||||
|
|
||||||
|
randomElement :: String -> IO Char |
||||||
|
randomElement xs = do |
||||||
|
let maxIndex :: Int |
||||||
|
maxIndex = length xs - 1 |
||||||
|
-- Right of arrow is IO Int, so randomDigit is Int |
||||||
|
randomDigit <- SR.randomRIO (0, maxIndex) :: IO Int |
||||||
|
return (xs !! randomDigit) |
||||||
|
|
||||||
|
shortyGen :: IO String |
||||||
|
shortyGen = |
||||||
|
replicateM 7 (randomElement alphaNum) |
||||||
|
|
||||||
|
saveURI :: R.Connection |
||||||
|
-> BC.ByteString |
||||||
|
-> BC.ByteString |
||||||
|
-> IO (Either R.Reply R.Status) |
||||||
|
saveURI conn shortURI uri = |
||||||
|
R.runRedis conn $ R.set shortURI uri |
||||||
|
|
||||||
|
-- No idea how to use this now... |
||||||
|
saveURI' :: BC.ByteString |
||||||
|
-> BC.ByteString |
||||||
|
-> ReaderT R.Connection IO (Either R.Reply R.Status) |
||||||
|
saveURI' shortURI uri = do |
||||||
|
conn <- ask |
||||||
|
liftIO $ R.runRedis conn $ R.set shortURI uri |
||||||
|
|
||||||
|
getURI :: R.Connection |
||||||
|
-> BC.ByteString |
||||||
|
-> IO (Either R.Reply (Maybe BC.ByteString)) |
||||||
|
getURI conn shortURI = R.runRedis conn $ R.get shortURI |
||||||
|
|
||||||
|
-- No idea how to use this now... |
||||||
|
getURI' :: BC.ByteString |
||||||
|
-> ReaderT R.Connection IO (Either R.Reply (Maybe BC.ByteString)) |
||||||
|
getURI' shortURI = do |
||||||
|
conn <- ask |
||||||
|
liftIO $ R.runRedis conn $ R.get shortURI |
||||||
|
|
||||||
|
linkShorty :: String -> String |
||||||
|
linkShorty shorty = |
||||||
|
concat [ "<a href=\"" |
||||||
|
, shorty |
||||||
|
, "\">Copy and paste your short URL</a>" |
||||||
|
] |
||||||
|
|
||||||
|
shortyCreated :: Show a => a -> String -> TL.Text |
||||||
|
shortyCreated resp shawty = |
||||||
|
TL.concat [ TL.pack (show resp) |
||||||
|
, " shorty is: ", TL.pack (linkShorty shawty) |
||||||
|
] |
||||||
|
|
||||||
|
shortyAintUri :: TL.Text -> TL.Text |
||||||
|
shortyAintUri uri = |
||||||
|
TL.concat [ uri |
||||||
|
, " wasn't a url, did you forget http://?" |
||||||
|
] |
||||||
|
|
||||||
|
shortyFound :: TL.Text -> TL.Text |
||||||
|
shortyFound tbs = |
||||||
|
TL.concat ["<a href=\"", tbs, "\">", tbs, "</a>"] |
||||||
|
|
||||||
|
app :: ReaderT R.Connection ScottyM () |
||||||
|
app = do |
||||||
|
rConn <- ask |
||||||
|
lift $ do |
||||||
|
get "/" $ do |
||||||
|
uri <- param "uri" |
||||||
|
let parsedUri :: Maybe URI |
||||||
|
parsedUri = parseURI (TL.unpack uri) |
||||||
|
case parsedUri of |
||||||
|
Just _ -> do |
||||||
|
shawty <- liftIO shortyGen |
||||||
|
let shorty = BC.pack shawty |
||||||
|
uri' = encodeUtf8 (TL.toStrict uri) |
||||||
|
resp <- liftIO (saveURI rConn shorty uri') |
||||||
|
html (shortyCreated resp shawty) |
||||||
|
Nothing -> text (shortyAintUri uri) |
||||||
|
get "/:short" $ do |
||||||
|
short <- param "short" |
||||||
|
uri <- liftIO (getURI rConn short) |
||||||
|
case uri of |
||||||
|
Left reply -> text (TL.pack (show reply)) |
||||||
|
Right mbBS -> case mbBS of |
||||||
|
Nothing -> text "uri not found" |
||||||
|
Just bs -> html (shortyFound tbs) |
||||||
|
where tbs :: TL.Text |
||||||
|
tbs = TL.fromStrict (decodeUtf8 bs) |
||||||
|
|
||||||
|
main :: IO () |
||||||
|
main = do |
||||||
|
rConn <- R.connect R.defaultConnectInfo |
||||||
|
scotty 3000 $ runReaderT app rConn |
||||||
|
-- main = do |
||||||
|
-- rConn <- R.connect R.defaultConnectInfo |
||||||
|
-- scotty 3000 (app rConn) |
||||||
|
|
@ -0,0 +1,33 @@ |
|||||||
|
name: shawty |
||||||
|
version: 0.1.0.0 |
||||||
|
synopsis: Initial project template from stack |
||||||
|
description: Please see README.md |
||||||
|
homepage: http://github.com/bitemyapp/shawty#readme |
||||||
|
license: BSD3 |
||||||
|
license-file: LICENSE |
||||||
|
author: Chris Allen |
||||||
|
maintainer: cma@bitemyapp.com |
||||||
|
copyright: 2015, Chris Allen |
||||||
|
category: Web |
||||||
|
build-type: Simple |
||||||
|
cabal-version: >=1.10 |
||||||
|
|
||||||
|
executable shawty |
||||||
|
hs-source-dirs: app |
||||||
|
main-is: Main.hs |
||||||
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N |
||||||
|
build-depends: base |
||||||
|
, bytestring |
||||||
|
, hedis |
||||||
|
, mtl |
||||||
|
, network-uri |
||||||
|
, random |
||||||
|
, scotty |
||||||
|
, semigroups |
||||||
|
, text |
||||||
|
, transformers |
||||||
|
default-language: Haskell2010 |
||||||
|
|
||||||
|
source-repository head |
||||||
|
type: git |
||||||
|
location: https://github.com/bitemyapp/shawty-prime |
@ -0,0 +1,32 @@ |
|||||||
|
# For more information, see: https://github.com/commercialhaskell/stack/blob/release/doc/yaml_configuration.md |
||||||
|
|
||||||
|
# Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2) |
||||||
|
resolver: lts-3.7 |
||||||
|
|
||||||
|
# Local packages, usually specified by relative directory name |
||||||
|
packages: |
||||||
|
- '.' |
||||||
|
|
||||||
|
# Packages to be pulled from upstream that are not in the resolver (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: >= 0.1.4.0 |
||||||
|
|
||||||
|
# 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] |
@ -0,0 +1,57 @@ |
|||||||
|
module ReaderPractice where |
||||||
|
|
||||||
|
import Control.Applicative |
||||||
|
import Data.Maybe |
||||||
|
|
||||||
|
x :: [Integer] |
||||||
|
x = [1, 2, 3] |
||||||
|
y :: [Integer] |
||||||
|
y = [4, 5, 6] |
||||||
|
z :: [Integer] |
||||||
|
z = [7, 8, 9] |
||||||
|
|
||||||
|
xs :: Maybe Integer |
||||||
|
xs = lookup 3 $ zip x y |
||||||
|
|
||||||
|
ys :: Maybe Integer |
||||||
|
ys = lookup 6 $ zip y z |
||||||
|
|
||||||
|
zs :: Maybe Integer |
||||||
|
zs = lookup 4 $ zip x y |
||||||
|
|
||||||
|
z' :: Integer -> Maybe Integer |
||||||
|
z' n = lookup n $ zip x z |
||||||
|
|
||||||
|
x1 :: Maybe (Integer, Integer) |
||||||
|
x1 = liftA2 (,) xs ys |
||||||
|
|
||||||
|
x2 :: Maybe (Integer, Integer) |
||||||
|
x2 = liftA2 (,) ys zs |
||||||
|
|
||||||
|
x3 :: Integer -> (Maybe Integer, Maybe Integer) |
||||||
|
x3 = liftA2 (,) z' z' |
||||||
|
|
||||||
|
summed :: Num c => (c, c) -> c |
||||||
|
summed = uncurry (+) |
||||||
|
|
||||||
|
bolt :: Integer -> Bool |
||||||
|
bolt = liftA2 (&&) (>3) (<8) |
||||||
|
|
||||||
|
sequA :: Integral a => a -> [Bool] |
||||||
|
sequA m = sequenceA [(>3), (<8), even] m |
||||||
|
|
||||||
|
s' :: Maybe Integer |
||||||
|
s' = summed <$> ((,) <$> xs <*> ys) |
||||||
|
|
||||||
|
main :: IO () |
||||||
|
main = do |
||||||
|
-- print $ sequenceA [Just (3 :: Integer), Just 2, Just 1] |
||||||
|
-- print $ sequenceA [x, y] |
||||||
|
-- print $ sequenceA [xs, ys] |
||||||
|
-- print $ summed <$> ((,) <$> xs <*> ys) |
||||||
|
-- print $ fmap summed ((,) <$> xs <*> zs) |
||||||
|
-- print $ bolt 7 |
||||||
|
-- print $ fmap bolt z |
||||||
|
print $ and $ sequA (7 :: Integer) |
||||||
|
print $ sequA $ fromMaybe 0 s' |
||||||
|
print $ bolt $ fromMaybe 0 ys |
@ -0,0 +1,6 @@ |
|||||||
|
module Ask where |
||||||
|
|
||||||
|
newtype Reader r a = Reader { runReader :: r -> a } |
||||||
|
|
||||||
|
ask :: Reader a a |
||||||
|
ask = Reader id |
@ -0,0 +1,27 @@ |
|||||||
|
module Follow where |
||||||
|
|
||||||
|
import Control.Applicative |
||||||
|
|
||||||
|
boop :: Num a => a -> a |
||||||
|
boop = (*2) |
||||||
|
|
||||||
|
doop :: Num a => a -> a |
||||||
|
doop = (+10) |
||||||
|
|
||||||
|
bip :: Num a => a -> a |
||||||
|
bip = boop . doop |
||||||
|
|
||||||
|
bloop :: Num a => a -> a |
||||||
|
bloop = fmap boop doop |
||||||
|
|
||||||
|
bbop :: Num a => a -> a |
||||||
|
bbop = (+) <$> boop <*> doop |
||||||
|
|
||||||
|
duwop :: Num a => a -> a |
||||||
|
duwop = liftA2 (+) boop doop |
||||||
|
|
||||||
|
boopDoop :: Num a => a -> a |
||||||
|
boopDoop = do |
||||||
|
a <- boop |
||||||
|
b <- doop |
||||||
|
return (a + b) |
@ -0,0 +1,26 @@ |
|||||||
|
module MonadFunc where |
||||||
|
|
||||||
|
foo :: (Functor f, Num a) => f a -> f a |
||||||
|
foo r = fmap (+1) r |
||||||
|
|
||||||
|
bar :: Foldable f => t -> f a -> (t, Int) |
||||||
|
bar r t = (r, length t) |
||||||
|
|
||||||
|
froot :: Num a => [a] -> ([a], Int) |
||||||
|
froot r = (map (+1) r, length r) |
||||||
|
|
||||||
|
barOne :: Foldable t => t a -> (t a, Int) |
||||||
|
barOne r = (r, length r) |
||||||
|
|
||||||
|
barPlus :: (Foldable t, Functor t, Num a) => t a -> (t a, Int) |
||||||
|
barPlus r = (foo r, length r) |
||||||
|
|
||||||
|
frooty :: Num a => [a] -> ([a], Int) |
||||||
|
frooty r = bar (foo r) r |
||||||
|
|
||||||
|
frooty' :: Num a => [a] -> ([a], Int) |
||||||
|
frooty' = \r -> bar (foo r) r |
||||||
|
|
||||||
|
-- fooBind :: (t -> t1) -> (t1 -> t -> t2) -> t -> t2 |
||||||
|
fooBind :: (r -> a) -> (a -> r -> b) -> r -> b |
||||||
|
fooBind m k = \r -> k (m r) r |
@ -0,0 +1,75 @@ |
|||||||
|
{-# LANGUAGE InstanceSigs #-} |
||||||
|
module ReadingComp where |
||||||
|
|
||||||
|
import Control.Applicative (liftA2) |
||||||
|
|
||||||
|
newtype Reader r a = Reader { runReader :: r -> a } |
||||||
|
|
||||||
|
instance Functor (Reader r) where |
||||||
|
fmap f (Reader ra) = Reader $ f . ra |
||||||
|
|
||||||
|
-- 1 |
||||||
|
myLiftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c |
||||||
|
myLiftA2 f a b = f <$> a <*> b |
||||||
|
|
||||||
|
-- 2 |
||||||
|
asks :: (r -> a) -> Reader r a |
||||||
|
asks = Reader |
||||||
|
|
||||||
|
-- 3 |
||||||
|
instance Applicative (Reader r) where |
||||||
|
pure :: a -> Reader r a |
||||||
|
pure a = Reader $ const a |
||||||
|
|
||||||
|
(<*>) :: Reader r (a -> b) -> Reader r a -> Reader r b |
||||||
|
(Reader rab) <*> (Reader ra) = Reader $ \r -> rab r $ ra r |
||||||
|
|
||||||
|
instance Monad (Reader r) where |
||||||
|
return = pure |
||||||
|
|
||||||
|
(>>=) :: Reader r a -> (a -> Reader r b) -> Reader r b |
||||||
|
(Reader ra) >>= aRb = Reader $ \r -> (runReader (aRb (ra r))) r |
||||||
|
|
||||||
|
newtype HumanName = HumanName String deriving (Eq, Show) |
||||||
|
newtype DogName = DogName String deriving (Eq, Show) |
||||||
|
newtype Address = Address String deriving (Eq, Show) |
||||||
|
|
||||||
|
data Person = Person { humanName :: HumanName |
||||||
|
, dogName :: DogName |
||||||
|
, address :: Address |
||||||
|
} deriving (Eq, Show) |
||||||
|
|
||||||
|
data Dog = Dog { dogsName :: DogName |
||||||
|
, dogsAddress :: Address |
||||||
|
} deriving (Eq, Show) |
||||||
|
|
||||||
|
pers :: Person |
||||||
|
pers = Person (HumanName "Big Bird") |
||||||
|
(DogName "Barkley") |
||||||
|
(Address "Sesame Street") |
||||||
|
|
||||||
|
mario :: Person |
||||||
|
mario = Person (HumanName "Mario") |
||||||
|
(DogName "Yoshi") |
||||||
|
(Address "Mushroom Kingdom") |
||||||
|
|
||||||
|
getDog :: Person -> Dog |
||||||
|
getDog p = Dog (dogName p) (address p) |
||||||
|
|
||||||
|
getDogR :: Person -> Dog |
||||||
|
getDogR = Dog <$> dogName <*> address |
||||||
|
|
||||||
|
getDogR' :: Person -> Dog |
||||||
|
getDogR' = liftA2 Dog dogName address |
||||||
|
|
||||||
|
getDogRM :: Person -> Dog |
||||||
|
getDogRM = do |
||||||
|
name <- dogName |
||||||
|
addy <- address |
||||||
|
return $ Dog name addy |
||||||
|
|
||||||
|
getDogRM' :: Reader Person Dog |
||||||
|
getDogRM' = do |
||||||
|
name <- Reader dogName |
||||||
|
addy <- Reader address |
||||||
|
return $ Dog name addy |
@ -0,0 +1,27 @@ |
|||||||
|
module WarmingUp where |
||||||
|
|
||||||
|
import Data.Char |
||||||
|
|
||||||
|
cap :: [Char] -> [Char] |
||||||
|
cap = map toUpper |
||||||
|
|
||||||
|
rev :: [Char] -> [Char] |
||||||
|
rev = reverse |
||||||
|
|
||||||
|
composed :: [Char] -> [Char] |
||||||
|
composed = rev . cap |
||||||
|
|
||||||
|
fmapped :: [Char] -> [Char] |
||||||
|
fmapped = fmap cap rev |
||||||
|
|
||||||
|
tupled :: [Char] -> ([Char],[Char]) |
||||||
|
tupled = (,) <$> cap <*> rev |
||||||
|
|
||||||
|
tupled' :: [Char] -> ([Char], [Char]) |
||||||
|
tupled' = do |
||||||
|
a <- cap |
||||||
|
b <- rev |
||||||
|
return (a, b) |
||||||
|
|
||||||
|
tupled'' :: [Char] -> ([Char],[Char]) |
||||||
|
tupled'' = cap >>= \a -> rev >>= \b -> return (a,b) |
Loading…
Reference in new issue