repos / handle-examples.hs.git


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
M README.md
+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.
A backpack-handle/CHANGELOG.md
+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.
A backpack-handle/LICENSE
+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.
A backpack-handle/Main.hs
+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
A backpack-handle/Setup.hs
+2, -0
1@@ -0,0 +1,2 @@
2+import Distribution.Simple
3+main = defaultMain
A backpack-handle/backpack-handle.cabal
+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
A backpack-handle/domain/WeatherProvider.hsig
+14, -0
 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
A backpack-handle/domain/WeatherReporter.hs
+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)
A backpack-handle/impl/SuperWeatherProvider.hs
+16, -0
 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
A backpack-handle/test-impl/TestWeatherProvider.hs
+23, -0
 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
A backpack-handle/test/Test.hs
+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"
A records-handle/CHANGELOG.md
+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.
A records-handle/LICENSE
+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.
A records-handle/Main.hs
+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
A records-handle/Setup.hs
+2, -0
1@@ -0,0 +1,2 @@
2+import Distribution.Simple
3+main = defaultMain
A records-handle/domain/WeatherProvider.hs
+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+  }
A records-handle/domain/WeatherReporter.hs
+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)
A records-handle/impl/SuperWeatherProvider.hs
+12, -0
 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
A records-handle/records-handle.cabal
+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
A records-handle/test-impl/TestWeatherProvider.hs
+17, -0
 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
A records-handle/test/Test.hs
+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"
A simple-handle/CHANGELOG.md
+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.
A simple-handle/LICENSE
+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.
A simple-handle/Main.hs
+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
A simple-handle/Setup.hs
+2, -0
1@@ -0,0 +1,2 @@
2+import Distribution.Simple
3+main = defaultMain
A simple-handle/domain/WeatherProvider.hs
+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
A simple-handle/domain/WeatherReporter.hs
+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
A simple-handle/simple-handle.cabal
+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