From: Evgenii Akentev Date: Wed, 2 Nov 2022 19:23:58 +0000 (+0400) Subject: Fix links to code X-Git-Url: https://git.xn--bdkaa.com/?a=commitdiff_plain;h=49de2a81d9081eeff43c5222e1e415a9043d9c18;p=xn--bdkaa.com.git Fix links to code --- diff --git a/content/posts/implementations-of-the-handle-pattern.md b/content/posts/implementations-of-the-handle-pattern.md index 26f28a7..f97ae7f 100644 --- a/content/posts/implementations-of-the-handle-pattern.md +++ b/content/posts/implementations-of-the-handle-pattern.md @@ -5,9 +5,9 @@ draft: false tags: [haskell, backpack] --- -In ["Monad Transformers and Effects with Backpack"](https://blog.ocharles.org.uk/posts/2020-12-23-monad-transformers-and-effects-with-backpack.html) [@acid2](https://twitter.com/acid2) presented how to apply Backpack to monad transformers. There is a less-popular approach to deal with effects — [Handle](https://jaspervdj.be/posts/2018-03-08-handle-pattern.html) ([Service](https://www.schoolofhaskell.com/user/meiersi/the-service-pattern)) pattern. I recommend reading both posts at first since they answer many questions regarding the design decisions behind the Handle pattern (why `IO`, why not type classes, etc). In this post, I want to show different implementations of the Handle pattern and compare them. All examples described below are available [in this repository](https://sr.ht/~ak3n/handle-examples/). +In ["Monad Transformers and Effects with Backpack"](https://blog.ocharles.org.uk/posts/2020-12-23-monad-transformers-and-effects-with-backpack.html) [@acid2](https://twitter.com/acid2) presented how to apply Backpack to monad transformers. There is a less-popular approach to deal with effects — [Handle](https://jaspervdj.be/posts/2018-03-08-handle-pattern.html) ([Service](https://www.schoolofhaskell.com/user/meiersi/the-service-pattern)) pattern. I recommend reading both posts at first since they answer many questions regarding the design decisions behind the Handle pattern (why `IO`, why not type classes, etc). In this post, I want to show different implementations of the Handle pattern and compare them. All examples described below are available [in this repository](https://git.ak3n.com/?p=handle-examples.git). -### When you might need the Handle pattern [[simple](https://git.sr.ht/~ak3n/handle-examples/tree/main/item/simple)] +### When you might need the Handle pattern [[simple](https://git.ak3n.com/?p=handle-examples.git;a=tree;f=simple)] Suppose we have a domain logic with a side effect: @@ -54,7 +54,7 @@ At some point in time, there appeared a need for tests to ensure that the domain All solutions have their pros and cons and the final choice depends on many factors — especially on the number of side effects and how they interact with each other. We are interested in how to achieve the second one with the Handle pattern. -### Simple Handle [[simple-handle](https://git.sr.ht/~ak3n/handle-examples/tree/main/item/simple-handle)] +### Simple Handle [[simple-handle](https://git.ak3n.com/?p=handle-examples.git;a=tree;f=simple-handle)] Let's start with a simple Handle that doesn't support multiple implementations. Here is the updated domain logic: @@ -112,7 +112,7 @@ getWeatherData _ _ _ = return $ WeatherData 30 We have wrapped our service with the Handle interface. It's not possible to have multiple implementations yet, but we got an interface of the service and can hide all the dependencies of the service into Handle. -### Handle with records [[records-handle](https://git.sr.ht/~ak3n/handle-examples/tree/main/item/records-handle)] +### Handle with records [[records-handle](https://git.ak3n.com/?p=handle-examples.git;a=tree;f=records-handle)] The approach with records is described in the aforementioned posts. Records are used as a dictionary with functions just like dictionary passing with type classes but explicitly. `WeatherReporter` module stays the same — it continues to use `WeatherProvider.Handle` while the `WeatherProvider` becomes an interface: @@ -236,7 +236,7 @@ The result of `getWeatherData "London" "now" = ipv1_s1Cp` is then passed to `cre The STG looks as expected. There are two allocations: one in `getCurrentWeatherReportInLondon1` and the other one in `$wcreateWeatherReport`. -### Handle with Backpack [[backpack-handle](https://git.sr.ht/~ak3n/handle-examples/tree/main/item/backpack-handle)] +### Handle with Backpack [[backpack-handle](https://git.ak3n.com/?p=handle-examples.git;a=tree;f=backpack-handle)] `WeatherProvider` becomes a signature in the cabal file: @@ -400,7 +400,7 @@ Handle = \r [eta_B1] Handle [eta_B1]; We can see that GHC inlined our constant implementation in the `getCurrentWeatherReportInLondon3` — we show `30` immediately. -### Handles with Backpack [[backpack-handles](https://git.sr.ht/~ak3n/handle-examples/tree/main/item/backpack-handles)] +### Handles with Backpack [[backpack-handles](https://git.ak3n.com/?p=handle-examples.git;a=tree;f=backpack-handles)] I decided to go further and make `WeatherReporter` a signature as well. Turned out this step required more actions with libraries. Here is `WeatherReporter.hsig`: @@ -447,9 +447,9 @@ library impl-reporter build-depends: base, domain-provider ``` -Instead of `domain` we have `domain-provider` and `domain-reporter`. It allows to depend on them individually and instantiate with different implementations. In [the example](https://git.sr.ht/~ak3n/handle-examples/tree/main/item/backpack-handles/backpack-handles.cabal#L52), I have instantiated the provider with implementation from `test-impl` using the reporter from `impl-reporter`. This is useful if you want to gradually write tests for different parts of the logic. +Instead of `domain` we have `domain-provider` and `domain-reporter`. It allows to depend on them individually and instantiate with different implementations. In [the example](https://git.ak3n.com/?p=handle-examples.git;a=blob;f=backpack-handles/backpack-handles.cabal;h=6cfe4f87359e2bf02fff72cfea75d2669818b7e1;hb=HEAD#l52), I have instantiated the provider with implementation from `test-impl` using the reporter from `impl-reporter`. This is useful if you want to gradually write tests for different parts of the logic. -### Handle with Vinyl [[vinyl-handle](https://git.sr.ht/~ak3n/handle-examples/tree/main/item/vinyl-handle)] +### Handle with Vinyl [[vinyl-handle](https://git.ak3n.com/?p=handle-examples.git;a=tree;f=vinyl-handle)] Suppose that we want to extend our `WeatherData` and return not only temperature but wind's speed too. We need to add a field to `WeatherData`: