├── .gitignore ├── LICENSE ├── Monads.cabal ├── README.md ├── Setup.hs ├── src ├── ApplicativesComplete.hs ├── Functors.hs ├── FunctorsComplete.hs ├── LawsComplete.hs ├── MonadsBasic.hs ├── MonadsBasicComplete.hs ├── ReaderWriter.hs ├── ReaderWriterComplete.hs ├── State.hs ├── StateComplete.hs ├── Transformers.hs └── TransformersComplete.hs ├── stack.yaml └── stack.yaml.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | *~ 3 | *.swp 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Author name here (c) 2021 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Author name here nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Monads.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 1.12 2 | 3 | -- This file has been generated from package.yaml by hpack version 0.31.2. 4 | -- 5 | -- see: https://github.com/sol/hpack 6 | -- 7 | -- hash: ba049f250b863ad5c7abc745a73c1adfc336f3d1a3f39f43a7d22beedcd8e9fd 8 | 9 | name: Monads 10 | version: 0.1.0.0 11 | description: Please see the README on GitHub at 12 | homepage: https://github.com/MondayMorningHaskell/Monads#readme 13 | bug-reports: https://github.com/MondayMorningHaskell/Monads/issues 14 | author: James Bowen 15 | maintainer: james@mondaymorninghaskell.me 16 | copyright: 2021 Monday Morning Haskell 17 | license: BSD3 18 | license-file: LICENSE 19 | build-type: Simple 20 | extra-source-files: 21 | README.md 22 | 23 | source-repository head 24 | type: git 25 | location: https://github.com/MondayMorningHaskell/Monads 26 | 27 | library 28 | exposed-modules: 29 | Functors 30 | FunctorsComplete 31 | ApplicativesComplete 32 | MonadsBasic 33 | MonadsBasicComplete 34 | ReaderWriter 35 | ReaderWriterComplete 36 | State 37 | StateComplete 38 | Transformers 39 | TransformersComplete 40 | LawsComplete 41 | other-modules: 42 | Paths_Monads 43 | hs-source-dirs: 44 | src 45 | build-depends: 46 | base >=4.7 && <5 47 | , array 48 | , containers 49 | , mtl 50 | , QuickCheck 51 | , random 52 | , transformers 53 | default-language: Haskell2010 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Monads (and other Functional Structures) 2 | 3 | This repository is meant to accompany the [Monads Series](https://www.mmhaskell.com/monads) on the Monday Morning Haskell blog. Each article in the series has one of two corresponding modules in this repository. Any module with "Complete" in its name is meant mostly as a reference. You can read the code along with the article and see the finished product. The other modules (for example [Functors](https://github.com/MondayMorningHaskell/Monads/blob/master/src/Functors.hs) and [MonadsBasic](https://github.com/MondayMorningHaskell/Monads/blob/master/src/MonadsBasic.hs)) also follow the same code examples, but some parts are left `undefined` with a "TODO" marker, so you can write the code in for yourself! 4 | 5 | ## Repository Instructions 6 | 7 | Start by building the code: 8 | 9 | ``` 10 | stack build 11 | ``` 12 | 13 | Then, you can open up the exercise in one window and bring up GHCI in another to follow along. There's some overlap in variable names, so you should start your GHCI session by clearing the module list: 14 | 15 | ``` 16 | >> stack ghci 17 | >> :l 18 | ``` 19 | 20 | Then you can load the module you're working with, and try out the different expressions! 21 | 22 | ``` 23 | >> :l ReaderWriter 24 | >> acc1 "Hello" 25 | (2, "lloHH") 26 | >> acc1' 27 | (undefined) 28 | ``` 29 | 30 | The `acc1'` function is undefined. So you can then modify it and use `:r` to reload the module! 31 | 32 | ``` 33 | >> :r 34 | >> acc1' "Hello" 35 | ("lloHH", 2) 36 | ``` 37 | 38 | ## Full Module List 39 | 40 | 1. [Part 1](https://mmhaskell.com/monads/functors): [Functors](https://github.com/MondayMorningHaskell/Monads/blob/master/src/Functors.hs), [FunctorsComplete](https://github.com/MondayMorningHaskell/Monads/blob/master/src/FunctorsComplete.hs) 41 | 2. [Part 2](https://mmhaskell.com/monads/applicatives): [ApplicativesComplete](https://github.com/MondayMorningHaskell/Monads/blob/master/src/ApplicativesComplete.hs) (All coding can be done in GHCI) 42 | 3. [Part 3](https://mmhaskell.com/monads/tutorial): [MonadsBasic](https://github.com/MondayMorningHaskell/Monads/blob/master/src/MonadsBasic.hs), [MonadsBasicComplete](https://github.com/MondayMorningHaskell/Monads/blob/master/src/MonadsBasicComplete.hs) 43 | 4. [Part 4](https://mmhaskell.com/monads/reader-writer): [ReaderWriter](https://github.com/MondayMorningHaskell/Monads/blob/master/src/ReaderWriter.hs), [ReaderWriterComplete](https://github.com/MondayMorningHaskell/Monads/blob/master/src/ReaderWriterComplete.hs) 44 | 5. [Part 5](https://mmhaskell.com/monads/state): [State](https://github.com/MondayMorningHaskell/Monads/blob/master/src/State.hs), [StateComplete](https://github.com/MondayMorningHaskell/Monads/blob/master/src/StateComplete.hs) 45 | 6. [Part 6](https://mmhaskell.com/monads/transformers): [Transformers](https://github.com/MondayMorningHaskell/Monads/blob/master/src/Transformers.hs), [TransformersComplete](https://github.com/MondayMorningHaskell/Monads/blob/master/src/TransformersComplete.hs) 46 | 7. [Part 7](https://mmhaskell.com/monads/laws): [LawsComplete](https://github.com/MondayMorningHaskell/Monads/blob/master/src/LawsComplete.hs) (One small coding addition you can make) 47 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /src/ApplicativesComplete.hs: -------------------------------------------------------------------------------- 1 | module ApplicativesComplete where 2 | 3 | -- For part 2, you can work entirely within GHCI 4 | -- This module just contains some references to 5 | -- the Applicative definitions mentioned in the article. 6 | 7 | {- Applicative Instances 8 | 9 | class Applicative f where 10 | pure :: a -> f a 11 | (<*>) :: f (a -> b) -> f a -> f b 12 | 13 | instance Applicative Maybe where 14 | pure = Just 15 | (<*>) Nothing _ = Nothing 16 | (<*>) _ Nothing = Nothing 17 | (<*>) (Just f) (Just x) = Just (f x) 18 | 19 | instance Applicative [] where 20 | pure a = [a] 21 | fs <*> xs = [f x | f <- fs, x <- xs] 22 | -} 23 | -------------------------------------------------------------------------------- /src/Functors.hs: -------------------------------------------------------------------------------- 1 | module Functors where 2 | 3 | import Data.Maybe (mapMaybe) 4 | import qualified Data.Map as M 5 | 6 | -- Motivating Examples! 7 | 8 | -- Simple String conversion. It might fail, so it returns Maybe 9 | tupleFromInputString :: String -> Maybe (String, String, Int) 10 | tupleFromInputString input = if length stringComponents /= 3 11 | then Nothing 12 | else Just (stringComponents !! 0, stringComponents !! 1, age) 13 | where 14 | stringComponents = words input 15 | age = read (stringComponents !! 2) :: Int 16 | 17 | -- An alternative to using a tuple (String, String, Int) 18 | data Person = Person 19 | { firstName :: String 20 | , lastName :: String 21 | , age :: Int 22 | } 23 | 24 | personFromTuple :: (String, String, Int) -> Person 25 | personFromTuple (fName, lName, age) = Person fName lName age 26 | 27 | -- Converting between the two formats 28 | convertTuple :: Maybe (String, String, Int) -> Maybe Person 29 | convertTuple Nothing = Nothing 30 | convertTuple (Just t) = Just (personFromTuple t) 31 | 32 | -- Could not use `convertTuple` with the results of this function! 33 | -- Would have to write a new function of type [(String, String, Int)] -> [Person] 34 | listFromInputString :: String -> [(String, String, Int)] 35 | listFromInputString contents = mapMaybe tupleFromInputString (lines contents) 36 | 37 | {- Functor Definitions: 38 | 39 | class Functor f where 40 | fmap :: (a -> b) -> f a -> f b 41 | 42 | instance Functor [] where 43 | fmap = map 44 | 45 | instance Functor Maybe where 46 | fmap _ Nothing = Nothing 47 | fmap f (Just a) = Just (f a) 48 | 49 | instance Functor (Either a) where 50 | fmap _ (Left x) = Left x 51 | fmap f (Right y) = Right (f y) 52 | -} 53 | 54 | -- TODO: This function needs a type signature! 55 | -- Make it as general as possible! 56 | -- convertTupleFunctor = fmap personFromTuple 57 | 58 | -- Making our own Functor 59 | 60 | data GovDirectory a = GovDirectory { 61 | mayor :: a, 62 | interimMayor :: Maybe a, 63 | cabinet :: M.Map String a, 64 | councilMembers :: [a] 65 | } 66 | 67 | instance Functor GovDirectory where 68 | -- TODO: Write out this functor instance! 69 | fmap f oldDirectory = undefined 70 | 71 | oldDirectory :: GovDirectory (String, String, Int) 72 | oldDirectory = GovDirectory 73 | ("John", "Doe", 46) 74 | Nothing 75 | (M.fromList 76 | [ ("Treasurer", ("Timothy", "Houston", 51)) 77 | , ("Historian", ("Bill", "Jefferson", 42)) 78 | , ("Sheriff", ("Susan", "Harrison", 49)) 79 | ]) 80 | ([("Sharon", "Stevens", 38), ("Christine", "Washington", 47)]) 81 | 82 | -- TODO: How can we do this in general terms, since we have 83 | -- a Functor instance? 84 | newDirectory :: GovDirectory Person 85 | newDirectory = undefined 86 | -------------------------------------------------------------------------------- /src/FunctorsComplete.hs: -------------------------------------------------------------------------------- 1 | module FunctorsComplete where 2 | 3 | import Data.Maybe (mapMaybe) 4 | import qualified Data.Map as M 5 | 6 | -- Motivating Examples! 7 | 8 | -- Simple String conversion. It might fail, so it returns Maybe 9 | tupleFromInputString :: String -> Maybe (String, String, Int) 10 | tupleFromInputString input = if length stringComponents /= 3 11 | then Nothing 12 | else Just (stringComponents !! 0, stringComponents !! 1, age) 13 | where 14 | stringComponents = words input 15 | age = read (stringComponents !! 2) :: Int 16 | 17 | -- An alternative to using a tuple (String, String, Int) 18 | data Person = Person 19 | { firstName :: String 20 | , lastName :: String 21 | , age :: Int 22 | } 23 | 24 | personFromTuple :: (String, String, Int) -> Person 25 | personFromTuple (fName, lName, age) = Person fName lName age 26 | 27 | -- Converting between the two formats 28 | convertTuple :: Maybe (String, String, Int) -> Maybe Person 29 | convertTuple Nothing = Nothing 30 | convertTuple (Just t) = Just (personFromTuple t) 31 | 32 | -- Could not use `convertTuple` with the results of this function! 33 | -- Would have to write a new function of type [(String, String, Int)] -> [Person] 34 | listFromInputString :: String -> [(String, String, Int)] 35 | listFromInputString contents = mapMaybe tupleFromInputString (lines contents) 36 | 37 | {- Functor Definitions: 38 | 39 | class Functor f where 40 | fmap :: (a -> b) -> f a -> f b 41 | 42 | instance Functor [] where 43 | fmap = map 44 | 45 | instance Functor Maybe where 46 | fmap _ Nothing = Nothing 47 | fmap f (Just a) = Just (f a) 48 | 49 | instance Functor (Either a) where 50 | fmap _ (Left x) = Left x 51 | fmap f (Right y) = Right (f y) 52 | -} 53 | 54 | -- Now this example will work for either Maybe (String, String, Int) or [(String, String, Int)] 55 | convertTupleFunctor :: Functor f => f (String, String, Int) -> f Person 56 | convertTupleFunctor = fmap personFromTuple 57 | 58 | -- Making our own Functor 59 | 60 | data GovDirectory a = GovDirectory { 61 | mayor :: a, 62 | interimMayor :: Maybe a, 63 | cabinet :: M.Map String a, 64 | councilMembers :: [a] 65 | } 66 | 67 | instance Functor GovDirectory where 68 | fmap f oldDirectory = GovDirectory { 69 | mayor = f (mayor oldDirectory), 70 | interimMayor = fmap f (interimMayor oldDirectory), 71 | cabinet = fmap f (cabinet oldDirectory), 72 | councilMembers = fmap f (councilMembers oldDirectory) 73 | -- Could also use <$> as an operator for `fmap` 74 | -- interimMayor = f <$> interimMayor oldDirectory, 75 | -- cabinet = f <$> cabinet oldDirectory, 76 | -- councilMembers = f <$> councilMembers oldDirectory 77 | } 78 | 79 | oldDirectory :: GovDirectory (String, String, Int) 80 | oldDirectory = GovDirectory 81 | ("John", "Doe", 46) 82 | Nothing 83 | (M.fromList 84 | [ ("Treasurer", ("Timothy", "Houston", 51)) 85 | , ("Historian", ("Bill", "Jefferson", 42)) 86 | , ("Sheriff", ("Susan", "Harrison", 49)) 87 | ]) 88 | ([("Sharon", "Stevens", 38), ("Christine", "Washington", 47)]) 89 | 90 | newDirectory :: GovDirectory Person 91 | newDirectory = personFromTuple <$> oldDirectory 92 | -------------------------------------------------------------------------------- /src/LawsComplete.hs: -------------------------------------------------------------------------------- 1 | module LawsComplete where 2 | 3 | import qualified Data.Map as M 4 | import Test.QuickCheck 5 | 6 | {- Functor Laws 7 | 8 | class Functor f where 9 | fmap :: (a -> b) -> f a -> f b 10 | 11 | -- Identity Law 12 | fmap id = id 13 | 14 | -- Composition Law 15 | fmap (g . f) = fmap g . fmap f 16 | 17 | -} 18 | 19 | {- Applicative Laws 20 | 21 | -- Identity Law 22 | pure id <*> v = v 23 | 24 | -- Homomorphism Law 25 | pure f <*> pure x = pure (f x) 26 | 27 | -- Interchange Law 28 | u <*> pure y = pure ($ y) <*> u 29 | 30 | -- Composition Law 31 | pure (.) <*> u <*> v <*> w = u <*> (v <*> w) 32 | 33 | -} 34 | 35 | {- Monad Laws 36 | 37 | -- Identity Laws 38 | return a >>= f = f 39 | m >>= return = m 40 | 41 | -- Composition Law 42 | (m >>= f) >>= g = m >>= (\x -> f x >>= g) 43 | 44 | -} 45 | 46 | data GovDirectory a = GovDirectory 47 | { mayor :: a 48 | , interimMayor :: Maybe a 49 | , cabinet :: M.Map String a 50 | , councilMembers :: [a] 51 | } deriving (Show, Eq) 52 | 53 | -- An invalid functor instance! 54 | instance Functor GovDirectory where 55 | fmap f oldDirectory = GovDirectory { 56 | mayor = f (mayor oldDirectory), 57 | interimMayor = Nothing, -- This isn't right! 58 | cabinet = f <$> cabinet oldDirectory, 59 | councilMembers = f <$> councilMembers oldDirectory 60 | } 61 | 62 | instance Arbitrary a => Arbitrary (GovDirectory a) where 63 | arbitrary = do 64 | m <- arbitrary 65 | im <- arbitrary 66 | cab <- arbitrary 67 | cm <- arbitrary 68 | return $ GovDirectory 69 | { mayor = m 70 | , interimMayor = im 71 | , cabinet = cab 72 | , councilMembers = cm 73 | } 74 | 75 | -- TODO: Fix the functor instance and run again! 76 | main :: IO () 77 | main = quickCheck govDirectoryFunctorCheck 78 | 79 | govDirectoryFunctorCheck :: GovDirectory String -> Bool 80 | govDirectoryFunctorCheck gd = fmap id gd == gd 81 | -------------------------------------------------------------------------------- /src/MonadsBasic.hs: -------------------------------------------------------------------------------- 1 | module MonadsBasic where 2 | 3 | import Data.Char 4 | 5 | {- Monad instances 6 | 7 | class Monad m where 8 | return :: a -> m a 9 | (>>=) :: m a -> (a -> m b) -> m b 10 | 11 | instance Monad Maybe where 12 | return = Just 13 | Nothing >>= _ = Nothing 14 | Just a >>= f = f a 15 | 16 | instance Monad (Either a) where 17 | return r = Right r 18 | (Left l) >>= _ = Left l 19 | (Right r) >>= f = f r 20 | -} 21 | 22 | maybeFunc1 :: String -> Maybe Int 23 | maybeFunc1 "" = Nothing 24 | maybeFunc1 str = Just $ length str 25 | 26 | maybeFunc2 :: Int -> Maybe Float 27 | maybeFunc2 i = if i `mod` 2 == 0 28 | then Nothing 29 | else Just ((fromIntegral i) * 3.14159) 30 | 31 | maybeFunc3 :: Float -> Maybe [Int] 32 | maybeFunc3 f = if f > 15.0 33 | then Nothing 34 | else Just [floor f, ceiling f] 35 | 36 | -- Evaluating each input leads to the "triangle" anti-pattern 37 | runMaybeFuncs :: String -> Maybe [Int] 38 | runMaybeFuncs input = case maybeFunc1 input of 39 | Nothing -> Nothing 40 | Just i -> case maybeFunc2 i of 41 | Nothing -> Nothing 42 | Just f -> maybeFunc3 f 43 | -- Imagine it keeps going... 44 | -- case maybeFunc3 of 45 | -- Nothing -> ... 46 | -- Just y -> ... 47 | 48 | -- TODO: Write the above function in one line using the >>= operator! 49 | runMaybeFuncsBind :: String -> Maybe [Int] 50 | runMaybeFuncsBind input = undefined 51 | 52 | -- TODO: Write the above function with "do" notation! 53 | runMaybeFuncsDo :: String -> Maybe [Int] 54 | runMaybeFuncsDo input = undefined 55 | 56 | -- TODO: How do we add "2" to the result of the first function? 57 | -- First try using "do" notation, then try using (>>=) 58 | runMaybeFuncsDo2 :: String -> Maybe [Int] 59 | runMaybeFuncsDo2 input = undefined 60 | 61 | runMaybeFuncsBind2 :: String -> Maybe [Int] 62 | runMaybeFuncsBind2 input = undefined 63 | 64 | -- Using the Either monad 65 | eitherFunc1 :: String -> Either String Int 66 | eitherFunc1 "" = Left "String cannot be empty!" 67 | eitherFunc1 str = Right $ length str 68 | 69 | eitherFunc2 :: Int -> Either String Float 70 | eitherFunc2 i = if i `mod` 2 == 0 71 | then Left "Length cannot be even!" 72 | else Right ((fromIntegral i) * 3.14159) 73 | 74 | eitherFunc3 :: Float -> Either String [Int] 75 | eitherFunc3 f = if f > 15.0 76 | then Left "Float is too large!" 77 | else Right [floor f, ceiling f] 78 | 79 | -- TODO: Call the 3 functions above! 80 | -- Use do-notation or (>>=) 81 | runEitherFuncs :: String -> Either String [Int] 82 | runEitherFuncs input = undefined 83 | 84 | -- Using a different error type is a **different monad**! 85 | data CustomError = CustomError 86 | 87 | eitherFuncCustom :: Either CustomError Float 88 | eitherFuncCustom = undefined 89 | 90 | -- TODO: use the IO monad to write an "echo" program 91 | -- 92 | -- Hint: Use these functions: 93 | -- getLine :: IO String 94 | -- print :: String -> IO () 95 | main :: IO () 96 | main = undefined 97 | -------------------------------------------------------------------------------- /src/MonadsBasicComplete.hs: -------------------------------------------------------------------------------- 1 | module MonadsBasicComplete where 2 | 3 | import Data.Char 4 | 5 | {- Monad instances 6 | 7 | class Monad m where 8 | return :: a -> m a 9 | (>>=) :: m a -> (a -> m b) -> m b 10 | 11 | instance Monad Maybe where 12 | return = Just 13 | Nothing >>= _ = Nothing 14 | Just a >>= f = f a 15 | 16 | instance Monad (Either a) where 17 | return r = Right r 18 | (Left l) >>= _ = Left l 19 | (Right r) >>= f = f r 20 | -} 21 | 22 | maybeFunc1 :: String -> Maybe Int 23 | maybeFunc1 "" = Nothing 24 | maybeFunc1 str = Just $ length str 25 | 26 | maybeFunc2 :: Int -> Maybe Float 27 | maybeFunc2 i = if i `mod` 2 == 0 28 | then Nothing 29 | else Just ((fromIntegral i) * 3.14159) 30 | 31 | maybeFunc3 :: Float -> Maybe [Int] 32 | maybeFunc3 f = if f > 15.0 33 | then Nothing 34 | else Just [floor f, ceiling f] 35 | 36 | runMaybeFuncs :: String -> Maybe [Int] 37 | runMaybeFuncs input = case maybeFunc1 input of 38 | Nothing -> Nothing 39 | Just i -> case maybeFunc2 i of 40 | Nothing -> Nothing 41 | Just f -> maybeFunc3 f 42 | 43 | -- No "triangle pattern!" 44 | runMaybeFuncsBind :: String -> Maybe [Int] 45 | runMaybeFuncsBind input = maybeFunc1 input >>= maybeFunc2 >>= maybeFunc3 46 | 47 | -- Almost like we're writing procedural code! 48 | runMaybeFuncsDo :: String -> Maybe [Int] 49 | runMaybeFuncsDo input = do 50 | i <- maybeFunc1 input 51 | f <- maybeFunc2 i 52 | maybeFunc3 f 53 | 54 | -- Adding 2 to the first result 55 | -- Easy with do-notation! 56 | runMaybeFuncsDo2 :: String -> Maybe [Int] 57 | runMaybeFuncsDo2 input = do 58 | i <- maybeFunc1 input 59 | f <- maybeFunc2 (i + 2) 60 | maybeFunc3 f 61 | 62 | -- Not so nice 63 | runMaybeFuncsBind2 :: String -> Maybe [Int] 64 | runMaybeFuncsBind2 input = maybeFunc1 input 65 | >>= (\i -> maybeFunc2 (i + 2)) 66 | >>= maybeFunc3 67 | 68 | -- Using the Either monad 69 | eitherFunc1 :: String -> Either String Int 70 | eitherFunc1 "" = Left "String cannot be empty!" 71 | eitherFunc1 str = Right $ length str 72 | 73 | eitherFunc2 :: Int -> Either String Float 74 | eitherFunc2 i = if i `mod` 2 == 0 75 | then Left "Length cannot be even!" 76 | else Right ((fromIntegral i) * 3.14159) 77 | 78 | eitherFunc3 :: Float -> Either String [Int] 79 | eitherFunc3 f = if f > 15.0 80 | then Left "Float is too large!" 81 | else Right [floor f, ceiling f] 82 | 83 | runEitherFuncs :: String -> Either String [Int] 84 | runEitherFuncs input = do 85 | i <- eitherFunc1 input 86 | f <- eitherFunc2 i 87 | eitherFunc3 f 88 | 89 | -- Using a different error type is a **different monad**! 90 | data CustomError = CustomError 91 | 92 | eitherFuncCustom :: Either CustomError Float 93 | eitherFuncCustom = undefined 94 | 95 | -- Using the IO monad to write an "echo" program 96 | main :: IO () 97 | main = do 98 | -- getLine :: IO String 99 | input <- getLine 100 | let uppercased = map Data.Char.toUpper input 101 | -- print :: String -> IO () 102 | print uppercased 103 | -------------------------------------------------------------------------------- /src/ReaderWriter.hs: -------------------------------------------------------------------------------- 1 | module ReaderWriter where 2 | 3 | import Control.Monad.Reader 4 | import Control.Monad.Writer 5 | import Data.Maybe (fromMaybe) 6 | import System.Environment (lookupEnv) 7 | 8 | main1 :: IO () 9 | main1 = do 10 | env <- loadEnv 11 | let str = func1 env 12 | print str 13 | 14 | data Environment = Environment 15 | { param1 :: String 16 | , param2 :: String 17 | , param3 :: String 18 | } 19 | 20 | loadEnv :: IO Environment 21 | loadEnv = do 22 | p1 <- lookupEnv "param1" 23 | p2 <- lookupEnv "param2" 24 | p3 <- lookupEnv "param3" 25 | return $ Environment 26 | (fromMaybe "param1" p1) 27 | (fromMaybe "parameter2" p2) 28 | (fromMaybe "p3" p3) 29 | 30 | -- These functions all need to have the Environment, 31 | -- even though only func3 uses it! 32 | func1 :: Environment -> String 33 | func1 env = "Result: " ++ (show (func2 env)) 34 | 35 | func2 :: Environment -> Int 36 | func2 env = 2 + floor (func3 env) 37 | 38 | func3 :: Environment -> Float 39 | func3 env = (fromIntegral $ l1 + l2 + l3) * 2.1 40 | where 41 | l1 = length (param1 env) 42 | l2 = length (param2 env) * 2 43 | l3 = length (param3 env) * 3 44 | 45 | -- TODO: Re-write these functions to use the Reader monad! 46 | main2 :: IO () 47 | main2 = do 48 | env <- loadEnv 49 | let str = runReader func1' env 50 | print str 51 | 52 | -- func1 and func2 shouldn't need the extra parameter anymore 53 | func1' :: Reader Environment String 54 | func1' = undefined 55 | 56 | func2' :: Reader Environment Int 57 | func2' = undefined 58 | 59 | -- Accumulation Functions 60 | 61 | -- Calls acc2 if even length, acc3 and acc4 if odd 62 | acc1 :: String -> (Int, String) 63 | acc1 input = if length input `mod` 2 == 0 64 | then acc2 (0, input) 65 | else (i1 + i2, str1 ++ str2) 66 | where 67 | (i1, str1) = acc3 (0, tail input) 68 | (i2, str2) = acc4 (0, take 1 input) 69 | 70 | -- Calls acc4 on truncated version 71 | acc2 :: (Int, String) -> (Int, String) 72 | acc2 (prev, input) = if (length input) > 10 73 | then acc4 (prev + 1, take 9 input) 74 | else (10, input) 75 | 76 | -- Calls acc2 on expanded version if a multiple of 3 77 | acc3 :: (Int, String) -> (Int, String) 78 | acc3 (prev, input) = if (length input) `mod` 3 == 0 79 | then (prev + f2resI + 3, f2resStr) 80 | else (prev + 1, tail input) 81 | where 82 | (f2resI, f2resStr) = acc2 (prev, input ++ "ab") 83 | 84 | acc4 :: (Int, String) -> (Int, String) 85 | acc4 (prev, input) = if (length input) < 10 86 | then (prev + length input, input ++ input) 87 | else (prev + 5, take 5 input) 88 | 89 | {- 90 | class Semigroup a where 91 | -- Also known as `mappend` 92 | (<>) :: a -> a -> a 93 | 94 | class (Semigroup a) => Monoid a where 95 | mempty :: a 96 | -} 97 | 98 | instance Semigroup Int where 99 | a <> b = a + b 100 | 101 | instance Monoid Int where 102 | mempty = 0 103 | 104 | -- TODO: Re-write these functions to use the Writer monad with the Int instance above! 105 | acc1' :: String -> (String, Int) 106 | acc1' input = undefined 107 | 108 | acc2' :: String -> Writer Int String 109 | acc2' input = undefined 110 | 111 | acc3' :: String -> Writer Int String 112 | acc3' input = undefined 113 | 114 | acc4' :: String -> Writer Int String 115 | acc4' input = undefined 116 | -------------------------------------------------------------------------------- /src/ReaderWriterComplete.hs: -------------------------------------------------------------------------------- 1 | module ReaderWriterComplete where 2 | 3 | import Control.Monad.Reader 4 | import Control.Monad.Writer 5 | import Data.Maybe (fromMaybe) 6 | import System.Environment (lookupEnv) 7 | 8 | main1 :: IO () 9 | main1 = do 10 | env <- loadEnv 11 | let str = func1 env 12 | print str 13 | 14 | data Environment = Environment 15 | { param1 :: String 16 | , param2 :: String 17 | , param3 :: String 18 | } 19 | 20 | loadEnv :: IO Environment 21 | loadEnv = do 22 | p1 <- lookupEnv "param1" 23 | p2 <- lookupEnv "param2" 24 | p3 <- lookupEnv "param3" 25 | return $ Environment 26 | (fromMaybe "param1" p1) 27 | (fromMaybe "parameter2" p2) 28 | (fromMaybe "p3" p3) 29 | 30 | -- These functions all need to have the Environment, 31 | -- even though only func3 uses it! 32 | func1 :: Environment -> String 33 | func1 env = "Result: " ++ (show (func2 env)) 34 | 35 | func2 :: Environment -> Int 36 | func2 env = 2 + floor (func3 env) 37 | 38 | func3 :: Environment -> Float 39 | func3 env = (fromIntegral $ l1 + l2 + l3) * 2.1 40 | where 41 | l1 = length (param1 env) 42 | l2 = length (param2 env) * 2 43 | l3 = length (param3 env) * 3 44 | 45 | -- Now we use the Reader monad! 46 | main2 :: IO () 47 | main2 = do 48 | env <- loadEnv 49 | let str = runReader func1' env 50 | print str 51 | 52 | -- func1 and func2 don't need the extra parameter anymore 53 | func1' :: Reader Environment String 54 | func1' = do 55 | res <- func2' 56 | return ("Result: " ++ show res) 57 | 58 | func2' :: Reader Environment Int 59 | func2' = do 60 | env <- ask 61 | let res3 = func3 env 62 | return (2 + floor res3) 63 | 64 | -- Accumulation Functions 65 | 66 | -- Calls acc2 if even length, acc3 and acc4 if odd 67 | acc1 :: String -> (Int, String) 68 | acc1 input = if length input `mod` 2 == 0 69 | then acc2 (0, input) 70 | else (i1 + i2, str1 ++ str2) 71 | where 72 | (i1, str1) = acc3 (0, tail input) 73 | (i2, str2) = acc4 (0, take 1 input) 74 | 75 | -- Calls acc4 on truncated version 76 | acc2 :: (Int, String) -> (Int, String) 77 | acc2 (prev, input) = if (length input) > 10 78 | then acc4 (prev + 1, take 9 input) 79 | else (10, input) 80 | 81 | -- Calls acc2 on expanded version if a multiple of 3 82 | acc3 :: (Int, String) -> (Int, String) 83 | acc3 (prev, input) = if (length input) `mod` 3 == 0 84 | then (prev + f2resI + 3, f2resStr) 85 | else (prev + 1, tail input) 86 | where 87 | (f2resI, f2resStr) = acc2 (prev, input ++ "ab") 88 | 89 | acc4 :: (Int, String) -> (Int, String) 90 | acc4 (prev, input) = if (length input) < 10 91 | then (prev + length input, input ++ input) 92 | else (prev + 5, take 5 input) 93 | 94 | {- 95 | class Semigroup a where 96 | -- Also known as `mappend` 97 | (<>) :: a -> a -> a 98 | 99 | class (Semigroup a) => Monoid a where 100 | mempty :: a 101 | -} 102 | 103 | instance Semigroup Int where 104 | a <> b = a + b 105 | 106 | instance Monoid Int where 107 | mempty = 0 108 | 109 | acc1' :: String -> (String, Int) 110 | acc1' input = if length input `mod` 2 == 0 111 | then runWriter (acc2' input) 112 | else runWriter $ do 113 | str1 <- acc3' (tail input) 114 | str2 <- acc4' (take 1 input) 115 | return (str1 ++ str2) 116 | 117 | acc2' :: String -> Writer Int String 118 | acc2' input = if (length input) > 10 119 | then do 120 | tell 1 121 | acc4' (take 9 input) 122 | else do 123 | tell 10 124 | return input 125 | 126 | acc3' :: String -> Writer Int String 127 | acc3' input = if (length input) `mod` 3 == 0 128 | then do 129 | tell 3 130 | acc2' (input ++ "ab") 131 | else do 132 | tell 1 133 | return $ tail input 134 | 135 | acc4' :: String -> Writer Int String 136 | acc4' input = if (length input) < 10 137 | then do 138 | tell (length input) 139 | return (input ++ input) 140 | else do 141 | tell 5 142 | return (take 5 input) 143 | -------------------------------------------------------------------------------- /src/State.hs: -------------------------------------------------------------------------------- 1 | module State where 2 | 3 | import Control.Monad.State 4 | import qualified Data.Array as A 5 | import qualified Data.Ix as I 6 | import System.Random (StdGen, randomR, newStdGen) 7 | 8 | {- State Monad reference 9 | 10 | get :: State s s 11 | put :: s -> State s () 12 | runState :: State s a -> s -> (a, s) 13 | 14 | -} 15 | 16 | data Player = XPlayer | OPlayer 17 | 18 | data TileState = Empty | HasX | HasO 19 | deriving (Show, Eq) 20 | 21 | type TileIndex = (Int, Int) 22 | 23 | boardIndices :: [TileIndex] 24 | boardIndices = I.range ((0, 0), (2,2)) 25 | 26 | data GameState = GameState 27 | { board :: A.Array TileIndex TileState 28 | , currentPlayer :: Player 29 | , generator :: StdGen 30 | } 31 | 32 | initialGameState :: StdGen -> GameState 33 | initialGameState gen = GameState 34 | (A.array (head boardIndices, last boardIndices) [(i, Empty) | i <- boardIndices]) 35 | XPlayer 36 | gen 37 | 38 | nextPlayer :: Player -> Player 39 | nextPlayer XPlayer = OPlayer 40 | nextPlayer OPlayer = XPlayer 41 | 42 | tileForPlayer :: Player -> TileState 43 | tileForPlayer XPlayer = HasX 44 | tileForPlayer OPlayer = HasO 45 | 46 | -- TODO: Fill in these stateful functions! 47 | 48 | -- Select a random move from among the Empty tiles 49 | -- You can't use the IO monad, so you have to make use of the 50 | -- stateful generator in the GameState! 51 | chooseRandomMove :: State GameState TileIndex 52 | chooseRandomMove = undefined 53 | 54 | -- Given a selected tile, mark it for the "current" player! 55 | applyMove :: TileIndex -> State GameState () 56 | applyMove i = undefined 57 | 58 | -- The game is done when there are no more Empty tiles! 59 | isGameDone :: State GameState Bool 60 | isGameDone = undefined 61 | 62 | -- Combine your functions together for a function to complete a single turn! 63 | resolveTurn :: State GameState Bool 64 | resolveTurn = undefined 65 | 66 | -- As an extra challenge, you can try to complete the game implementation! 67 | main :: IO () 68 | main = undefined 69 | -------------------------------------------------------------------------------- /src/StateComplete.hs: -------------------------------------------------------------------------------- 1 | module StateComplete where 2 | 3 | import Control.Monad.State 4 | import qualified Data.Array as A 5 | import qualified Data.Ix as I 6 | import System.Random (StdGen, randomR, newStdGen) 7 | 8 | {- State Monad reference 9 | 10 | get :: State s s 11 | put :: s -> State s () 12 | runState :: State s a -> s -> (a, s) 13 | 14 | -} 15 | 16 | data Player = XPlayer | OPlayer 17 | 18 | data TileState = Empty | HasX | HasO 19 | deriving (Show, Eq) 20 | 21 | type TileIndex = (Int, Int) 22 | 23 | boardIndices :: [TileIndex] 24 | boardIndices = I.range ((0, 0), (2,2)) 25 | 26 | data GameState = GameState 27 | { board :: A.Array TileIndex TileState 28 | , currentPlayer :: Player 29 | , generator :: StdGen 30 | } 31 | 32 | initialGameState :: StdGen -> GameState 33 | initialGameState gen = GameState 34 | (A.array (head boardIndices, last boardIndices) [(i, Empty) | i <- boardIndices]) 35 | XPlayer 36 | gen 37 | 38 | nextPlayer :: Player -> Player 39 | nextPlayer XPlayer = OPlayer 40 | nextPlayer OPlayer = XPlayer 41 | 42 | tileForPlayer :: Player -> TileState 43 | tileForPlayer XPlayer = HasX 44 | tileForPlayer OPlayer = HasO 45 | 46 | chooseRandomMove :: State GameState TileIndex 47 | chooseRandomMove = do 48 | game <- get 49 | let openSpots = [ fst pair | pair <- A.assocs (board game), snd pair == Empty] 50 | let gen = generator game 51 | let (i, gen') = randomR (0, length openSpots - 1) gen 52 | put $ game { generator = gen' } 53 | return $ openSpots !! i 54 | 55 | applyMove :: TileIndex -> State GameState () 56 | applyMove i = do 57 | game <- get 58 | let p = currentPlayer game 59 | let newBoard = board game A.// [(i, tileForPlayer p)] 60 | put $ game { currentPlayer = nextPlayer p, board = newBoard } 61 | 62 | isGameDone :: State GameState Bool 63 | isGameDone = do 64 | game <- get 65 | let openSpots = [ fst pair | pair <- A.assocs (board game), snd pair == Empty] 66 | return $ length openSpots == 0 67 | 68 | resolveTurn :: State GameState Bool 69 | resolveTurn = do 70 | i <- chooseRandomMove 71 | applyMove i 72 | isGameDone 73 | -------------------------------------------------------------------------------- /src/Transformers.hs: -------------------------------------------------------------------------------- 1 | module Transformers where 2 | 3 | import Control.Monad.IO.Class 4 | import Control.Monad.Trans.Class (lift) 5 | import Control.Monad.Trans.Reader 6 | import Control.Monad.Trans.Maybe 7 | import Data.Char (isUpper, isLower) 8 | 9 | main1 :: IO () 10 | main1 = do 11 | maybeUserName <- readUserName 12 | case maybeUserName of 13 | Nothing -> print "Invalid user name!" 14 | Just (uName) -> do 15 | maybeEmail <- readEmail 16 | case maybeEmail of 17 | Nothing -> print "Invalid email!" 18 | Just (email) -> do 19 | maybePassword <- readPassword 20 | case maybePassword of 21 | Nothing -> print "Invalid Password" 22 | Just password -> login uName email password 23 | 24 | readUserName :: IO (Maybe String) 25 | readUserName = do 26 | putStrLn "Please enter your username!" 27 | str <- getLine 28 | if length str > 5 29 | then return $ Just str 30 | else return Nothing 31 | 32 | readEmail :: IO (Maybe String) 33 | readEmail = do 34 | putStrLn "Please enter your email!" 35 | str <- getLine 36 | if '@' `elem` str && '.' `elem` str 37 | then return $ Just str 38 | else return Nothing 39 | 40 | readPassword :: IO (Maybe String) 41 | readPassword = do 42 | putStrLn "Please enter your Password!" 43 | str <- getLine 44 | if length str < 8 || null (filter isUpper str) || null (filter isLower str) 45 | then return Nothing 46 | else return $ Just str 47 | 48 | login :: String -> String -> String -> IO () 49 | login username email password = putStrLn $ "Now logged in as: " ++ username 50 | 51 | {- MaybeT Reference 52 | 53 | newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) } 54 | 55 | instance (Monad m) => Monad (MaybeT m) where 56 | return = lift . return 57 | x >>= f = MaybeT $ do 58 | v <- runMaybeT x 59 | case v of 60 | Nothing -> return Nothing 61 | Just y -> runMaybeT (f y) 62 | 63 | -} 64 | 65 | -- TODO: Fill in these functions to work with "MaybeT"! 66 | readUserName' :: MaybeT IO String 67 | readUserName' = undefined 68 | 69 | readEmail' :: MaybeT IO String 70 | readEmail' = undefined 71 | 72 | readPassword' :: MaybeT IO String 73 | readPassword' = undefined 74 | 75 | main2 :: IO () 76 | main2 = undefined 77 | 78 | -- Lifting 79 | 80 | type Env = (Maybe String, Maybe String, Maybe String) 81 | 82 | -- TODO: 83 | -- Try retrieving the username from the first element of the Env 84 | -- If it is Nothing, then reading the input! 85 | -- You'll have to use a couple "lift"s! 86 | readUserName'' :: MaybeT (ReaderT Env IO) String 87 | readUserName'' = undefined 88 | 89 | -- These functions aren't necessary for the above example ^^ 90 | type TripleMonad a = MaybeT (ReaderT Env IO) a 91 | 92 | performReader :: ReaderT Env IO a -> TripleMonad a 93 | performReader = lift 94 | 95 | performIO :: IO a -> TripleMonad a 96 | performIO = lift . lift 97 | 98 | {- MonadTrans and MonadIO Reference 99 | 100 | class MonadTrans t where 101 | lift :: (Monad m) => m a -> t m a 102 | 103 | class (Monad m) => MonadIO m where 104 | liftIO :: IO a -> m a 105 | 106 | -} 107 | 108 | debugFunc :: (MonadIO m) => String -> m () 109 | debugFunc input = liftIO $ putStrLn ("Successfully produced input: " ++ input) 110 | 111 | -- TODO: Re-write main2, but run 'debugFunc' each time you get a portion of 112 | -- the user's input! 113 | main3 :: IO () 114 | main3 = undefined 115 | -------------------------------------------------------------------------------- /src/TransformersComplete.hs: -------------------------------------------------------------------------------- 1 | module TransformersComplete where 2 | 3 | import Control.Monad.IO.Class 4 | import Control.Monad.Trans.Class (lift) 5 | import Control.Monad.Trans.Reader 6 | import Control.Monad.Trans.Maybe 7 | import Data.Char (isUpper, isLower) 8 | 9 | main1 :: IO () 10 | main1 = do 11 | maybeUserName <- readUserName 12 | case maybeUserName of 13 | Nothing -> print "Invalid user name!" 14 | Just (uName) -> do 15 | maybeEmail <- readEmail 16 | case maybeEmail of 17 | Nothing -> print "Invalid email!" 18 | Just (email) -> do 19 | maybePassword <- readPassword 20 | case maybePassword of 21 | Nothing -> print "Invalid Password" 22 | Just password -> login uName email password 23 | 24 | readUserName :: IO (Maybe String) 25 | readUserName = do 26 | putStrLn "Please enter your username!" 27 | str <- getLine 28 | if length str > 5 29 | then return $ Just str 30 | else return Nothing 31 | 32 | readEmail :: IO (Maybe String) 33 | readEmail = do 34 | putStrLn "Please enter your email!" 35 | str <- getLine 36 | if '@' `elem` str && '.' `elem` str 37 | then return $ Just str 38 | else return Nothing 39 | 40 | readPassword :: IO (Maybe String) 41 | readPassword = do 42 | putStrLn "Please enter your Password!" 43 | str <- getLine 44 | if length str < 8 || null (filter isUpper str) || null (filter isLower str) 45 | then return Nothing 46 | else return $ Just str 47 | 48 | login :: String -> String -> String -> IO () 49 | login username email password = putStrLn $ "Now logged in as: " ++ username 50 | 51 | {- MaybeT Reference 52 | 53 | newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) } 54 | 55 | instance (Monad m) => Monad (MaybeT m) where 56 | return = lift . return 57 | x >>= f = MaybeT $ do 58 | v <- runMaybeT x 59 | case v of 60 | Nothing -> return Nothing 61 | Just y -> runMaybeT (f y) 62 | 63 | -} 64 | 65 | readUserName' :: MaybeT IO String 66 | readUserName' = MaybeT $ do 67 | putStrLn "Please enter your Username!" 68 | str <- getLine 69 | if length str > 5 70 | then return $ Just str 71 | else return Nothing 72 | 73 | readEmail' :: MaybeT IO String 74 | readEmail' = MaybeT $ do 75 | putStrLn "Please enter your Email!" 76 | str <- getLine 77 | if length str > 5 78 | then return $ Just str 79 | else return Nothing 80 | 81 | readPassword' :: MaybeT IO String 82 | readPassword' = MaybeT $ do 83 | putStrLn "Please enter your Password!" 84 | str <- getLine 85 | if length str < 8 || null (filter isUpper str) || null (filter isLower str) 86 | then return Nothing 87 | else return $ Just str 88 | 89 | main2 :: IO () 90 | main2 = do 91 | maybeCreds <- runMaybeT $ do 92 | usr <- readUserName' 93 | email <- readEmail' 94 | pass <- readPassword' 95 | return (usr, email, pass) 96 | case maybeCreds of 97 | Nothing -> print "Couldn't login!" 98 | Just (u, e, p) -> login u e p 99 | 100 | -- Lifting 101 | 102 | type Env = (Maybe String, Maybe String, Maybe String) 103 | 104 | readUserName'' :: MaybeT (ReaderT Env IO) String 105 | readUserName'' = MaybeT $ do 106 | (maybeOldUser, _, _) <- ask 107 | case maybeOldUser of 108 | Just str -> return $ Just str 109 | Nothing -> do 110 | -- lift allows normal IO functions from inside ReaderT Env IO! 111 | lift $ putStrLn "Please enter your Username!" 112 | input <- lift getLine 113 | if length input > 5 114 | then return (Just input) 115 | else return Nothing 116 | 117 | type TripleMonad a = MaybeT (ReaderT Env IO) a 118 | 119 | performReader :: ReaderT Env IO a -> TripleMonad a 120 | performReader = lift 121 | 122 | performIO :: IO a -> TripleMonad a 123 | performIO = lift . lift 124 | 125 | {- MonadTrans and MonadIO Reference 126 | 127 | class MonadTrans t where 128 | lift :: (Monad m) => m a -> t m a 129 | 130 | class (Monad m) => MonadIO m where 131 | liftIO :: IO a -> m a 132 | 133 | -} 134 | 135 | debugFunc :: (MonadIO m) => String -> m () 136 | debugFunc input = liftIO $ putStrLn ("Successfully produced input: " ++ input) 137 | 138 | main3 :: IO () 139 | main3 = do 140 | maybeCreds <- runMaybeT $ do 141 | usr <- readUserName' 142 | debugFunc usr 143 | email <- readEmail' 144 | debugFunc email 145 | pass <- readPassword' 146 | debugFunc pass 147 | return (usr, email, pass) 148 | case maybeCreds of 149 | Nothing -> print "Couldn't login!" 150 | Just (u, e, p) -> login u e p 151 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # 15 | # The location of a snapshot can be provided as a file or url. Stack assumes 16 | # a snapshot provided as a file might change, whereas a url resource does not. 17 | # 18 | # resolver: ./custom-snapshot.yaml 19 | # resolver: https://example.com/snapshots/2018-01-01.yaml 20 | resolver: lts-16.27 21 | 22 | # User packages to be built. 23 | # Various formats can be used as shown in the example below. 24 | # 25 | # packages: 26 | # - some-directory 27 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 28 | # subdirs: 29 | # - auto-update 30 | # - wai 31 | packages: 32 | - . 33 | # Dependency packages to be pulled from upstream that are not in the resolver. 34 | # These entries can reference officially published versions as well as 35 | # forks / in-progress versions pinned to a git hash. For example: 36 | # 37 | # extra-deps: 38 | # - acme-missiles-0.3 39 | # - git: https://github.com/commercialhaskell/stack.git 40 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 41 | # 42 | # extra-deps: [] 43 | 44 | # Override default flag values for local packages and extra-deps 45 | # flags: {} 46 | 47 | # Extra package databases containing global packages 48 | # extra-package-dbs: [] 49 | 50 | # Control whether we use the GHC we find on the path 51 | # system-ghc: true 52 | # 53 | # Require a specific version of stack, using version ranges 54 | # require-stack-version: -any # Default 55 | # require-stack-version: ">=2.1" 56 | # 57 | # Override the architecture used by stack, especially useful on Windows 58 | # arch: i386 59 | # arch: x86_64 60 | # 61 | # Extra directories used by stack for building 62 | # extra-include-dirs: [/path/to/dir] 63 | # extra-lib-dirs: [/path/to/dir] 64 | # 65 | # Allow a newer minor version of GHC than the snapshot specifies 66 | # compiler-check: newer-minor 67 | -------------------------------------------------------------------------------- /stack.yaml.lock: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by Stack. 2 | # You should not edit this file by hand. 3 | # For more information, please see the documentation at: 4 | # https://docs.haskellstack.org/en/stable/lock_files 5 | 6 | packages: [] 7 | snapshots: 8 | - completed: 9 | size: 533252 10 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/27.yaml 11 | sha256: c2aaae52beeacf6a5727c1010f50e89d03869abfab6d2c2658ade9da8ed50c73 12 | original: lts-16.27 13 | --------------------------------------------------------------------------------