Add simple, records and backpack versions.
authorEvgenii Akentev <i@ak3n.com>
Tue, 5 Jan 2021 11:47:31 +0000 (16:47 +0500)
committerEvgenii Akentev <i@ak3n.com>
Tue, 5 Jan 2021 11:47:31 +0000 (16:47 +0500)
28 files changed:
README.md
backpack-handle/CHANGELOG.md [new file with mode: 0644]
backpack-handle/LICENSE [new file with mode: 0644]
backpack-handle/Main.hs [new file with mode: 0644]
backpack-handle/Setup.hs [new file with mode: 0644]
backpack-handle/backpack-handle.cabal [new file with mode: 0644]
backpack-handle/domain/WeatherProvider.hsig [new file with mode: 0644]
backpack-handle/domain/WeatherReporter.hs [new file with mode: 0644]
backpack-handle/impl/SuperWeatherProvider.hs [new file with mode: 0644]
backpack-handle/test-impl/TestWeatherProvider.hs [new file with mode: 0644]
backpack-handle/test/Test.hs [new file with mode: 0644]
records-handle/CHANGELOG.md [new file with mode: 0644]
records-handle/LICENSE [new file with mode: 0644]
records-handle/Main.hs [new file with mode: 0644]
records-handle/Setup.hs [new file with mode: 0644]
records-handle/domain/WeatherProvider.hs [new file with mode: 0644]
records-handle/domain/WeatherReporter.hs [new file with mode: 0644]
records-handle/impl/SuperWeatherProvider.hs [new file with mode: 0644]
records-handle/records-handle.cabal [new file with mode: 0644]
records-handle/test-impl/TestWeatherProvider.hs [new file with mode: 0644]
records-handle/test/Test.hs [new file with mode: 0644]
simple-handle/CHANGELOG.md [new file with mode: 0644]
simple-handle/LICENSE [new file with mode: 0644]
simple-handle/Main.hs [new file with mode: 0644]
simple-handle/Setup.hs [new file with mode: 0644]
simple-handle/domain/WeatherProvider.hs [new file with mode: 0644]
simple-handle/domain/WeatherReporter.hs [new file with mode: 0644]
simple-handle/simple-handle.cabal [new file with mode: 0644]

index 7632ba3cf80d6aa48c1fbf075d174c39a463f526..8b12c7ca3c686373db5d3d5552209645391956f6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
-# backpack-handle-example
\ No newline at end of file
+# backpack-handle-example
+
+The examples of the Handle pattern in simple case, with records, and backpack.
diff --git a/backpack-handle/CHANGELOG.md b/backpack-handle/CHANGELOG.md
new file mode 100644 (file)
index 0000000..806c22c
--- /dev/null
@@ -0,0 +1,5 @@
+# Revision history for backpack-handle-pattern
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/backpack-handle/LICENSE b/backpack-handle/LICENSE
new file mode 100644 (file)
index 0000000..9eea539
--- /dev/null
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Evgenii Akentev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/backpack-handle/Main.hs b/backpack-handle/Main.hs
new file mode 100644 (file)
index 0000000..07cd3de
--- /dev/null
@@ -0,0 +1,13 @@
+module Main where
+
+import qualified SuperWeatherProvider
+import qualified WeatherProvider
+import qualified WeatherReporter
+
+-- | This is an actual application where we use
+-- our concrete implementation of `WeatherProvider`.
+main :: IO ()
+main = do
+  let wph = SuperWeatherProvider.new
+  weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon wph
+  putStrLn weatherReportInLondon
diff --git a/backpack-handle/Setup.hs b/backpack-handle/Setup.hs
new file mode 100644 (file)
index 0000000..9a994af
--- /dev/null
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/backpack-handle/backpack-handle.cabal b/backpack-handle/backpack-handle.cabal
new file mode 100644 (file)
index 0000000..c427729
--- /dev/null
@@ -0,0 +1,47 @@
+cabal-version:       >=2
+name:                backpack-handle
+version:             0.1.0.0
+license-file:        LICENSE
+author:              Evgenii Akentev
+maintainer:          i@ak3n.com
+build-type:          Simple
+extra-source-files:  CHANGELOG.md
+
+library domain
+  hs-source-dirs: domain
+  signatures:      WeatherProvider
+  exposed-modules: WeatherReporter
+  default-language: Haskell2010
+  build-depends:    base
+
+library impl
+  hs-source-dirs: impl
+  exposed-modules: SuperWeatherProvider
+  reexported-modules: SuperWeatherProvider as WeatherProvider
+  default-language: Haskell2010
+  build-depends:    base
+
+library test-impl
+  hs-source-dirs: test-impl
+  exposed-modules: TestWeatherProvider
+  reexported-modules: TestWeatherProvider as WeatherProvider
+  default-language: Haskell2010
+  build-depends:    base
+
+executable backpack-handle-exe
+  main-is:             Main.hs
+  build-depends:       base >=4.13 && <4.14
+                     , impl
+                     , domain
+  default-language:    Haskell2010
+
+test-suite spec
+  type:             exitcode-stdio-1.0
+  hs-source-dirs:   test
+  main-is:          Test.hs
+  default-language:   Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , QuickCheck
+                     , hspec
+                     , domain
+                     , test-impl
diff --git a/backpack-handle/domain/WeatherProvider.hsig b/backpack-handle/domain/WeatherProvider.hsig
new file mode 100644 (file)
index 0000000..56de95a
--- /dev/null
@@ -0,0 +1,14 @@
+signature WeatherProvider where
+
+data Temperature
+instance Show Temperature
+
+data WeatherData = WeatherData { temperature :: Temperature }
+
+type Location = String
+type Day = String
+
+data Handle
+
+-- | The interface of `WeatherProvider` with available methods.
+getWeatherData :: Handle -> Location -> Day -> IO WeatherData
diff --git a/backpack-handle/domain/WeatherReporter.hs b/backpack-handle/domain/WeatherReporter.hs
new file mode 100644 (file)
index 0000000..e096750
--- /dev/null
@@ -0,0 +1,11 @@
+module WeatherReporter where
+
+import WeatherProvider
+
+type WeatherReport = String
+
+-- | This is domain logic. It uses `WeatherProvider` to get the actual data.
+getCurrentWeatherReportInLondon :: WeatherProvider.Handle -> IO WeatherReport
+getCurrentWeatherReportInLondon wph = do
+  weatherData <- WeatherProvider.getWeatherData wph "London" "now"
+  return $ "Current temperature in London is " ++ (show $ temperature weatherData)
\ No newline at end of file
diff --git a/backpack-handle/impl/SuperWeatherProvider.hs b/backpack-handle/impl/SuperWeatherProvider.hs
new file mode 100644 (file)
index 0000000..1a29069
--- /dev/null
@@ -0,0 +1,16 @@
+module SuperWeatherProvider where
+
+type Temperature = Int
+data WeatherData = WeatherData { temperature :: Temperature }
+
+type Location = String
+type Day = String
+
+data Handle = Handle
+
+new :: Handle
+new = Handle
+
+-- | This is some concrete implementation `WeatherProvider` interface
+getWeatherData :: Handle -> Location -> Day -> IO WeatherData
+getWeatherData _ _ _ = return $ WeatherData 30
diff --git a/backpack-handle/test-impl/TestWeatherProvider.hs b/backpack-handle/test-impl/TestWeatherProvider.hs
new file mode 100644 (file)
index 0000000..900b102
--- /dev/null
@@ -0,0 +1,23 @@
+module TestWeatherProvider where
+
+type Temperature = Int
+data WeatherData = WeatherData { temperature :: Temperature }
+
+type Location = String
+type Day = String
+
+-- | This is a configuration that allows to setup the provider for tests.
+data Config = Config
+  { initTemperature :: Temperature
+  }
+
+data Handle = Handle
+  { config :: Config
+  }
+
+new :: Config -> Handle
+new = Handle
+
+-- | This is an implementation `WeatherProvider` interface for tests
+getWeatherData :: Handle -> Location -> Day -> IO WeatherData
+getWeatherData (Handle conf) _ _ = return $ WeatherData $ initTemperature conf
diff --git a/backpack-handle/test/Test.hs b/backpack-handle/test/Test.hs
new file mode 100644 (file)
index 0000000..18dd2e5
--- /dev/null
@@ -0,0 +1,22 @@
+import Test.Hspec
+
+import qualified TestWeatherProvider
+import qualified WeatherProvider
+import qualified WeatherReporter
+
+main :: IO ()
+main = hspec spec
+
+weatherWithTemp :: WeatherProvider.Temperature -> WeatherProvider.Handle
+weatherWithTemp = TestWeatherProvider.new . TestWeatherProvider.Config 
+
+spec :: Spec
+spec = describe "WeatherReporter" $ do
+  it "weather in London is 0" $ do
+    weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon $
+      weatherWithTemp 0
+    weatherReportInLondon `shouldBe` "Current temperature in London is 0"
+  it "weather in London is -5" $ do
+    weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon $
+      weatherWithTemp (-5)
+    weatherReportInLondon `shouldBe` "Current temperature in London is -5"
diff --git a/records-handle/CHANGELOG.md b/records-handle/CHANGELOG.md
new file mode 100644 (file)
index 0000000..806c22c
--- /dev/null
@@ -0,0 +1,5 @@
+# Revision history for backpack-handle-pattern
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/records-handle/LICENSE b/records-handle/LICENSE
new file mode 100644 (file)
index 0000000..9eea539
--- /dev/null
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Evgenii Akentev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/records-handle/Main.hs b/records-handle/Main.hs
new file mode 100644 (file)
index 0000000..07cd3de
--- /dev/null
@@ -0,0 +1,13 @@
+module Main where
+
+import qualified SuperWeatherProvider
+import qualified WeatherProvider
+import qualified WeatherReporter
+
+-- | This is an actual application where we use
+-- our concrete implementation of `WeatherProvider`.
+main :: IO ()
+main = do
+  let wph = SuperWeatherProvider.new
+  weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon wph
+  putStrLn weatherReportInLondon
diff --git a/records-handle/Setup.hs b/records-handle/Setup.hs
new file mode 100644 (file)
index 0000000..9a994af
--- /dev/null
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/records-handle/domain/WeatherProvider.hs b/records-handle/domain/WeatherProvider.hs
new file mode 100644 (file)
index 0000000..9375c4d
--- /dev/null
@@ -0,0 +1,12 @@
+module WeatherProvider where
+
+type Temperature = Int
+data WeatherData = WeatherData { temperature :: Temperature }
+
+type Location = String
+type Day = String
+
+-- | The interface of `WeatherProvider` with available methods.
+data Handle = Handle
+  { getWeatherData :: Location -> Day -> IO WeatherData
+  }
diff --git a/records-handle/domain/WeatherReporter.hs b/records-handle/domain/WeatherReporter.hs
new file mode 100644 (file)
index 0000000..e096750
--- /dev/null
@@ -0,0 +1,11 @@
+module WeatherReporter where
+
+import WeatherProvider
+
+type WeatherReport = String
+
+-- | This is domain logic. It uses `WeatherProvider` to get the actual data.
+getCurrentWeatherReportInLondon :: WeatherProvider.Handle -> IO WeatherReport
+getCurrentWeatherReportInLondon wph = do
+  weatherData <- WeatherProvider.getWeatherData wph "London" "now"
+  return $ "Current temperature in London is " ++ (show $ temperature weatherData)
\ No newline at end of file
diff --git a/records-handle/impl/SuperWeatherProvider.hs b/records-handle/impl/SuperWeatherProvider.hs
new file mode 100644 (file)
index 0000000..9eef94a
--- /dev/null
@@ -0,0 +1,12 @@
+module SuperWeatherProvider where
+
+import WeatherProvider
+
+new :: Handle
+new = Handle
+  { getWeatherData = getSuperWeatherData
+  }
+
+-- | This is some concrete implementation `WeatherProvider` interface
+getSuperWeatherData :: Location -> Day -> IO WeatherData
+getSuperWeatherData _ _ = return $ WeatherData 30
\ No newline at end of file
diff --git a/records-handle/records-handle.cabal b/records-handle/records-handle.cabal
new file mode 100644 (file)
index 0000000..ac1318e
--- /dev/null
@@ -0,0 +1,47 @@
+cabal-version:       >=2
+name:                records-handle
+version:             0.1.0.0
+license-file:        LICENSE
+author:              Evgenii Akentev
+maintainer:          i@ak3n.com
+build-type:          Simple
+extra-source-files:  CHANGELOG.md
+
+library domain
+  hs-source-dirs: domain
+  exposed-modules: WeatherProvider
+                 , WeatherReporter
+  default-language: Haskell2010
+  build-depends:    base
+
+library impl
+  hs-source-dirs: impl
+  exposed-modules: SuperWeatherProvider
+  default-language: Haskell2010
+  build-depends:    base
+                  , domain
+
+library test-impl
+  hs-source-dirs: test-impl
+  exposed-modules: TestWeatherProvider
+  default-language: Haskell2010
+  build-depends:    base
+                  , domain
+
+executable records-handle-exe
+  main-is:             Main.hs
+  build-depends:       base >=4.13 && <4.14
+                     , domain
+                     , impl
+  default-language:    Haskell2010
+
+test-suite spec
+  type:             exitcode-stdio-1.0
+  hs-source-dirs:   test
+  main-is:          Test.hs
+  default-language:   Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , QuickCheck
+                     , hspec
+                     , domain
+                     , test-impl
diff --git a/records-handle/test-impl/TestWeatherProvider.hs b/records-handle/test-impl/TestWeatherProvider.hs
new file mode 100644 (file)
index 0000000..172dea4
--- /dev/null
@@ -0,0 +1,17 @@
+module TestWeatherProvider where
+
+import WeatherProvider
+
+-- | This is a configuration that allows to setup the provider for tests.
+data Config = Config
+  { initTemperature :: Temperature
+  }
+
+new :: Config -> Handle
+new config = Handle
+  { getWeatherData = getTestWeatherData $ initTemperature config
+  }
+
+-- | This is an implementation `WeatherProvider` interface for tests
+getTestWeatherData :: Int -> Location -> Day -> IO WeatherData
+getTestWeatherData temp _ _ = return $ WeatherData temp
diff --git a/records-handle/test/Test.hs b/records-handle/test/Test.hs
new file mode 100644 (file)
index 0000000..18dd2e5
--- /dev/null
@@ -0,0 +1,22 @@
+import Test.Hspec
+
+import qualified TestWeatherProvider
+import qualified WeatherProvider
+import qualified WeatherReporter
+
+main :: IO ()
+main = hspec spec
+
+weatherWithTemp :: WeatherProvider.Temperature -> WeatherProvider.Handle
+weatherWithTemp = TestWeatherProvider.new . TestWeatherProvider.Config 
+
+spec :: Spec
+spec = describe "WeatherReporter" $ do
+  it "weather in London is 0" $ do
+    weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon $
+      weatherWithTemp 0
+    weatherReportInLondon `shouldBe` "Current temperature in London is 0"
+  it "weather in London is -5" $ do
+    weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon $
+      weatherWithTemp (-5)
+    weatherReportInLondon `shouldBe` "Current temperature in London is -5"
diff --git a/simple-handle/CHANGELOG.md b/simple-handle/CHANGELOG.md
new file mode 100644 (file)
index 0000000..806c22c
--- /dev/null
@@ -0,0 +1,5 @@
+# Revision history for backpack-handle-pattern
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/simple-handle/LICENSE b/simple-handle/LICENSE
new file mode 100644 (file)
index 0000000..9eea539
--- /dev/null
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Evgenii Akentev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/simple-handle/Main.hs b/simple-handle/Main.hs
new file mode 100644 (file)
index 0000000..e1d6702
--- /dev/null
@@ -0,0 +1,10 @@
+module Main where
+
+import qualified WeatherProvider
+import qualified WeatherReporter
+
+main :: IO ()
+main = do
+  let wph = WeatherProvider.new
+  weatherReportInLondon <- WeatherReporter.getCurrentWeatherReportInLondon wph
+  putStrLn weatherReportInLondon
diff --git a/simple-handle/Setup.hs b/simple-handle/Setup.hs
new file mode 100644 (file)
index 0000000..9a994af
--- /dev/null
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/simple-handle/domain/WeatherProvider.hs b/simple-handle/domain/WeatherProvider.hs
new file mode 100644 (file)
index 0000000..d61ef7c
--- /dev/null
@@ -0,0 +1,19 @@
+module WeatherProvider where
+
+type Temperature = Int
+data WeatherData = WeatherData { temperature :: Temperature }
+
+type Location = String
+type Day = String
+
+-- | Our Handle is empty, but usually other dependencies are stored here
+data Handle = Handle
+
+-- | Constructor for Handle
+new :: Handle
+new = Handle
+
+-- | This is some concrete implementation.
+-- In this example we return a constant value.
+getWeatherData :: Handle -> Location -> Day -> IO WeatherData
+getWeatherData _ _ _ = return $ WeatherData 30
diff --git a/simple-handle/domain/WeatherReporter.hs b/simple-handle/domain/WeatherReporter.hs
new file mode 100644 (file)
index 0000000..88a9d44
--- /dev/null
@@ -0,0 +1,16 @@
+module WeatherReporter where
+
+import WeatherProvider
+
+type WeatherReport = String
+
+-- | Domain logic. Usually some pure code that might use mtl, free monads, etc.
+createWeatherReport :: WeatherData -> WeatherReport
+createWeatherReport (WeatherData temp) =
+  "The current temperature in London is " ++ (show temp)
+
+-- | Domain logic that uses external dependency to get data and process it.
+getCurrentWeatherReportInLondon :: WeatherProvider.Handle -> IO WeatherReport
+getCurrentWeatherReportInLondon wph = do
+  weatherData <- WeatherProvider.getWeatherData wph "London" "now"
+  return $ createWeatherReport weatherData
diff --git a/simple-handle/simple-handle.cabal b/simple-handle/simple-handle.cabal
new file mode 100644 (file)
index 0000000..2a349a8
--- /dev/null
@@ -0,0 +1,21 @@
+cabal-version:       >=2
+name:                simple-handle
+version:             0.1.0.0
+license-file:        LICENSE
+author:              Evgenii Akentev
+maintainer:          i@ak3n.com
+build-type:          Simple
+extra-source-files:  CHANGELOG.md
+
+library domain
+  hs-source-dirs: domain
+  exposed-modules: WeatherProvider
+                 , WeatherReporter
+  default-language: Haskell2010
+  build-depends:    base
+
+executable simple-handle-exe
+  main-is:             Main.hs
+  build-depends:       base >=4.13 && <4.14
+                     , domain
+  default-language:    Haskell2010