- commit
- f582dbf
- parent
- e10bc3c
- author
- Evgenii Akentev
- date
- 2021-01-05 15:47:31 +0400 +04
Add simple, records and backpack versions.
28 files changed,
+433,
-1
+3,
-1
1@@ -1 +1,3 @@
2-# backpack-handle-example
3+# backpack-handle-example
4+
5+The examples of the Handle pattern in simple case, with records, and backpack.
+5,
-0
1@@ -0,0 +1,5 @@
2+# Revision history for backpack-handle-pattern
3+
4+## 0.1.0.0 -- YYYY-mm-dd
5+
6+* First version. Released on an unsuspecting world.
+21,
-0
1@@ -0,0 +1,21 @@
2+MIT License
3+
4+Copyright (c) 2021 Evgenii Akentev
5+
6+Permission is hereby granted, free of charge, to any person obtaining a copy
7+of this software and associated documentation files (the "Software"), to deal
8+in the Software without restriction, including without limitation the rights
9+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+copies of the Software, and to permit persons to whom the Software is
11+furnished to do so, subject to the following conditions:
12+
13+The above copyright notice and this permission notice shall be included in all
14+copies or substantial portions of the Software.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+SOFTWARE.
+13,
-0
1@@ -0,0 +1,13 @@
2+module Main where
3+
4+import qualified SuperWeatherProvider
5+import qualified WeatherProvider
6+import qualified WeatherReporter
7+
8+-- | This is an actual application where we use
9+-- our concrete implementation of `WeatherProvider`.
10+main :: IO ()
11+main = do
12+ let wph = SuperWeatherProvider.new
13+ weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon wph
14+ putStrLn weatherReportInLondon
+2,
-0
1@@ -0,0 +1,2 @@
2+import Distribution.Simple
3+main = defaultMain
+47,
-0
1@@ -0,0 +1,47 @@
2+cabal-version: >=2
3+name: backpack-handle
4+version: 0.1.0.0
5+license-file: LICENSE
6+author: Evgenii Akentev
7+maintainer: i@ak3n.com
8+build-type: Simple
9+extra-source-files: CHANGELOG.md
10+
11+library domain
12+ hs-source-dirs: domain
13+ signatures: WeatherProvider
14+ exposed-modules: WeatherReporter
15+ default-language: Haskell2010
16+ build-depends: base
17+
18+library impl
19+ hs-source-dirs: impl
20+ exposed-modules: SuperWeatherProvider
21+ reexported-modules: SuperWeatherProvider as WeatherProvider
22+ default-language: Haskell2010
23+ build-depends: base
24+
25+library test-impl
26+ hs-source-dirs: test-impl
27+ exposed-modules: TestWeatherProvider
28+ reexported-modules: TestWeatherProvider as WeatherProvider
29+ default-language: Haskell2010
30+ build-depends: base
31+
32+executable backpack-handle-exe
33+ main-is: Main.hs
34+ build-depends: base >=4.13 && <4.14
35+ , impl
36+ , domain
37+ default-language: Haskell2010
38+
39+test-suite spec
40+ type: exitcode-stdio-1.0
41+ hs-source-dirs: test
42+ main-is: Test.hs
43+ default-language: Haskell2010
44+ build-depends: base >= 4.7 && < 5
45+ , QuickCheck
46+ , hspec
47+ , domain
48+ , test-impl
1@@ -0,0 +1,14 @@
2+signature WeatherProvider where
3+
4+data Temperature
5+instance Show Temperature
6+
7+data WeatherData = WeatherData { temperature :: Temperature }
8+
9+type Location = String
10+type Day = String
11+
12+data Handle
13+
14+-- | The interface of `WeatherProvider` with available methods.
15+getWeatherData :: Handle -> Location -> Day -> IO WeatherData
1@@ -0,0 +1,11 @@
2+module WeatherReporter where
3+
4+import WeatherProvider
5+
6+type WeatherReport = String
7+
8+-- | This is domain logic. It uses `WeatherProvider` to get the actual data.
9+getCurrentWeatherReportInLondon :: WeatherProvider.Handle -> IO WeatherReport
10+getCurrentWeatherReportInLondon wph = do
11+ weatherData <- WeatherProvider.getWeatherData wph "London" "now"
12+ return $ "Current temperature in London is " ++ (show $ temperature weatherData)
1@@ -0,0 +1,16 @@
2+module SuperWeatherProvider where
3+
4+type Temperature = Int
5+data WeatherData = WeatherData { temperature :: Temperature }
6+
7+type Location = String
8+type Day = String
9+
10+data Handle = Handle
11+
12+new :: Handle
13+new = Handle
14+
15+-- | This is some concrete implementation `WeatherProvider` interface
16+getWeatherData :: Handle -> Location -> Day -> IO WeatherData
17+getWeatherData _ _ _ = return $ WeatherData 30
1@@ -0,0 +1,23 @@
2+module TestWeatherProvider where
3+
4+type Temperature = Int
5+data WeatherData = WeatherData { temperature :: Temperature }
6+
7+type Location = String
8+type Day = String
9+
10+-- | This is a configuration that allows to setup the provider for tests.
11+data Config = Config
12+ { initTemperature :: Temperature
13+ }
14+
15+data Handle = Handle
16+ { config :: Config
17+ }
18+
19+new :: Config -> Handle
20+new = Handle
21+
22+-- | This is an implementation `WeatherProvider` interface for tests
23+getWeatherData :: Handle -> Location -> Day -> IO WeatherData
24+getWeatherData (Handle conf) _ _ = return $ WeatherData $ initTemperature conf
+22,
-0
1@@ -0,0 +1,22 @@
2+import Test.Hspec
3+
4+import qualified TestWeatherProvider
5+import qualified WeatherProvider
6+import qualified WeatherReporter
7+
8+main :: IO ()
9+main = hspec spec
10+
11+weatherWithTemp :: WeatherProvider.Temperature -> WeatherProvider.Handle
12+weatherWithTemp = TestWeatherProvider.new . TestWeatherProvider.Config
13+
14+spec :: Spec
15+spec = describe "WeatherReporter" $ do
16+ it "weather in London is 0" $ do
17+ weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon $
18+ weatherWithTemp 0
19+ weatherReportInLondon `shouldBe` "Current temperature in London is 0"
20+ it "weather in London is -5" $ do
21+ weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon $
22+ weatherWithTemp (-5)
23+ weatherReportInLondon `shouldBe` "Current temperature in London is -5"
+5,
-0
1@@ -0,0 +1,5 @@
2+# Revision history for backpack-handle-pattern
3+
4+## 0.1.0.0 -- YYYY-mm-dd
5+
6+* First version. Released on an unsuspecting world.
+21,
-0
1@@ -0,0 +1,21 @@
2+MIT License
3+
4+Copyright (c) 2021 Evgenii Akentev
5+
6+Permission is hereby granted, free of charge, to any person obtaining a copy
7+of this software and associated documentation files (the "Software"), to deal
8+in the Software without restriction, including without limitation the rights
9+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+copies of the Software, and to permit persons to whom the Software is
11+furnished to do so, subject to the following conditions:
12+
13+The above copyright notice and this permission notice shall be included in all
14+copies or substantial portions of the Software.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+SOFTWARE.
+13,
-0
1@@ -0,0 +1,13 @@
2+module Main where
3+
4+import qualified SuperWeatherProvider
5+import qualified WeatherProvider
6+import qualified WeatherReporter
7+
8+-- | This is an actual application where we use
9+-- our concrete implementation of `WeatherProvider`.
10+main :: IO ()
11+main = do
12+ let wph = SuperWeatherProvider.new
13+ weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon wph
14+ putStrLn weatherReportInLondon
+2,
-0
1@@ -0,0 +1,2 @@
2+import Distribution.Simple
3+main = defaultMain
+12,
-0
1@@ -0,0 +1,12 @@
2+module WeatherProvider where
3+
4+type Temperature = Int
5+data WeatherData = WeatherData { temperature :: Temperature }
6+
7+type Location = String
8+type Day = String
9+
10+-- | The interface of `WeatherProvider` with available methods.
11+data Handle = Handle
12+ { getWeatherData :: Location -> Day -> IO WeatherData
13+ }
+11,
-0
1@@ -0,0 +1,11 @@
2+module WeatherReporter where
3+
4+import WeatherProvider
5+
6+type WeatherReport = String
7+
8+-- | This is domain logic. It uses `WeatherProvider` to get the actual data.
9+getCurrentWeatherReportInLondon :: WeatherProvider.Handle -> IO WeatherReport
10+getCurrentWeatherReportInLondon wph = do
11+ weatherData <- WeatherProvider.getWeatherData wph "London" "now"
12+ return $ "Current temperature in London is " ++ (show $ temperature weatherData)
1@@ -0,0 +1,12 @@
2+module SuperWeatherProvider where
3+
4+import WeatherProvider
5+
6+new :: Handle
7+new = Handle
8+ { getWeatherData = getSuperWeatherData
9+ }
10+
11+-- | This is some concrete implementation `WeatherProvider` interface
12+getSuperWeatherData :: Location -> Day -> IO WeatherData
13+getSuperWeatherData _ _ = return $ WeatherData 30
+47,
-0
1@@ -0,0 +1,47 @@
2+cabal-version: >=2
3+name: records-handle
4+version: 0.1.0.0
5+license-file: LICENSE
6+author: Evgenii Akentev
7+maintainer: i@ak3n.com
8+build-type: Simple
9+extra-source-files: CHANGELOG.md
10+
11+library domain
12+ hs-source-dirs: domain
13+ exposed-modules: WeatherProvider
14+ , WeatherReporter
15+ default-language: Haskell2010
16+ build-depends: base
17+
18+library impl
19+ hs-source-dirs: impl
20+ exposed-modules: SuperWeatherProvider
21+ default-language: Haskell2010
22+ build-depends: base
23+ , domain
24+
25+library test-impl
26+ hs-source-dirs: test-impl
27+ exposed-modules: TestWeatherProvider
28+ default-language: Haskell2010
29+ build-depends: base
30+ , domain
31+
32+executable records-handle-exe
33+ main-is: Main.hs
34+ build-depends: base >=4.13 && <4.14
35+ , domain
36+ , impl
37+ default-language: Haskell2010
38+
39+test-suite spec
40+ type: exitcode-stdio-1.0
41+ hs-source-dirs: test
42+ main-is: Test.hs
43+ default-language: Haskell2010
44+ build-depends: base >= 4.7 && < 5
45+ , QuickCheck
46+ , hspec
47+ , domain
48+ , test-impl
1@@ -0,0 +1,17 @@
2+module TestWeatherProvider where
3+
4+import WeatherProvider
5+
6+-- | This is a configuration that allows to setup the provider for tests.
7+data Config = Config
8+ { initTemperature :: Temperature
9+ }
10+
11+new :: Config -> Handle
12+new config = Handle
13+ { getWeatherData = getTestWeatherData $ initTemperature config
14+ }
15+
16+-- | This is an implementation `WeatherProvider` interface for tests
17+getTestWeatherData :: Int -> Location -> Day -> IO WeatherData
18+getTestWeatherData temp _ _ = return $ WeatherData temp
+22,
-0
1@@ -0,0 +1,22 @@
2+import Test.Hspec
3+
4+import qualified TestWeatherProvider
5+import qualified WeatherProvider
6+import qualified WeatherReporter
7+
8+main :: IO ()
9+main = hspec spec
10+
11+weatherWithTemp :: WeatherProvider.Temperature -> WeatherProvider.Handle
12+weatherWithTemp = TestWeatherProvider.new . TestWeatherProvider.Config
13+
14+spec :: Spec
15+spec = describe "WeatherReporter" $ do
16+ it "weather in London is 0" $ do
17+ weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon $
18+ weatherWithTemp 0
19+ weatherReportInLondon `shouldBe` "Current temperature in London is 0"
20+ it "weather in London is -5" $ do
21+ weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon $
22+ weatherWithTemp (-5)
23+ weatherReportInLondon `shouldBe` "Current temperature in London is -5"
+5,
-0
1@@ -0,0 +1,5 @@
2+# Revision history for backpack-handle-pattern
3+
4+## 0.1.0.0 -- YYYY-mm-dd
5+
6+* First version. Released on an unsuspecting world.
+21,
-0
1@@ -0,0 +1,21 @@
2+MIT License
3+
4+Copyright (c) 2021 Evgenii Akentev
5+
6+Permission is hereby granted, free of charge, to any person obtaining a copy
7+of this software and associated documentation files (the "Software"), to deal
8+in the Software without restriction, including without limitation the rights
9+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+copies of the Software, and to permit persons to whom the Software is
11+furnished to do so, subject to the following conditions:
12+
13+The above copyright notice and this permission notice shall be included in all
14+copies or substantial portions of the Software.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+SOFTWARE.
+10,
-0
1@@ -0,0 +1,10 @@
2+module Main where
3+
4+import qualified WeatherProvider
5+import qualified WeatherReporter
6+
7+main :: IO ()
8+main = do
9+ let wph = WeatherProvider.new
10+ weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon wph
11+ putStrLn weatherReportInLondon
+2,
-0
1@@ -0,0 +1,2 @@
2+import Distribution.Simple
3+main = defaultMain
+19,
-0
1@@ -0,0 +1,19 @@
2+module WeatherProvider where
3+
4+type Temperature = Int
5+data WeatherData = WeatherData { temperature :: Temperature }
6+
7+type Location = String
8+type Day = String
9+
10+-- | Our Handle is empty, but usually other dependencies are stored here
11+data Handle = Handle
12+
13+-- | Constructor for Handle
14+new :: Handle
15+new = Handle
16+
17+-- | This is some concrete implementation.
18+-- In this example we return a constant value.
19+getWeatherData :: Handle -> Location -> Day -> IO WeatherData
20+getWeatherData _ _ _ = return $ WeatherData 30
+16,
-0
1@@ -0,0 +1,16 @@
2+module WeatherReporter where
3+
4+import WeatherProvider
5+
6+type WeatherReport = String
7+
8+-- | Domain logic. Usually some pure code that might use mtl, free monads, etc.
9+createWeatherReport :: WeatherData -> WeatherReport
10+createWeatherReport (WeatherData temp) =
11+ "The current temperature in London is " ++ (show temp)
12+
13+-- | Domain logic that uses external dependency to get data and process it.
14+getCurrentWeatherReportInLondon :: WeatherProvider.Handle -> IO WeatherReport
15+getCurrentWeatherReportInLondon wph = do
16+ weatherData <- WeatherProvider.getWeatherData wph "London" "now"
17+ return $ createWeatherReport weatherData
+21,
-0
1@@ -0,0 +1,21 @@
2+cabal-version: >=2
3+name: simple-handle
4+version: 0.1.0.0
5+license-file: LICENSE
6+author: Evgenii Akentev
7+maintainer: i@ak3n.com
8+build-type: Simple
9+extra-source-files: CHANGELOG.md
10+
11+library domain
12+ hs-source-dirs: domain
13+ exposed-modules: WeatherProvider
14+ , WeatherReporter
15+ default-language: Haskell2010
16+ build-depends: base
17+
18+executable simple-handle-exe
19+ main-is: Main.hs
20+ build-depends: base >=4.13 && <4.14
21+ , domain
22+ default-language: Haskell2010