{"id":41560,"date":"2023-12-22T14:33:33","date_gmt":"2023-12-22T19:33:33","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=41560"},"modified":"2023-12-22T14:34:25","modified_gmt":"2023-12-22T19:34:25","slug":"how-to-control-the-world","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2023\/12\/22\/how-to-control-the-world\/","title":{"rendered":"How to Control the World"},"content":{"rendered":"<p><a href=\"https:\/\/www.pointfree.co\/blog\/posts\/21-how-to-control-the-world\">Brandon Williams and Stephen Celis<\/a> (2018, via <a href=\"https:\/\/christiantietze.de\/posts\/2023\/12\/dependency-injection-via-protocol-composition\/\">Christian Tietze<\/a>):<\/p>\n<blockquote cite=\"https:\/\/www.pointfree.co\/blog\/posts\/21-how-to-control-the-world\"><p>While unconventional, we hope that it&rsquo;s obvious that this solution of controlling dependencies is superior to the traditional solutions in use today. It also gives us an opportunity to reevaluate deep-seated beliefs we may have. We should continuously question our assumptions. In this case, we found that:<\/p><ul><li><p>Singletons can be good (as long as we have a means to control them) and global mutation can be good (when it&rsquo;s limited to development and testing). Blanket statements against singletons and global mutation are fun to make, but we were able to find real value in using them.<\/p><\/li><li><p>Protocols aren&rsquo;t necessarily a good choice to control dependencies. Protocol-oriented programming is all too easy to reach for when a simple value type requires less work.<\/p><\/li><\/ul><\/blockquote>\n<p>The global mutable struct approach certainly reduces boilerplate code, but I&rsquo;ve never understood how it can work with threads. Most basically, how do you deal with modifying the dependencies for multiple unit tests that are running concurrently?<\/p>\n\n<p>Their newer <a href=\"https:\/\/github.com\/pointfreeco\/swift-dependencies\">Dependencies<\/a> library handles this more directly:<\/p>\n<blockquote cite=\"https:\/\/github.com\/pointfreeco\/swift-dependencies\"><p>A dependency management library inspired by SwiftUI&rsquo;s &ldquo;environment.&rdquo;<\/p><p>[&#8230;]<\/p><p>For example, you can easily control these dependencies in tests. If you want to test the logic\ninside the <code>addButtonTapped<\/code> method, you can use the <a href=\"https:\/\/swiftpackageindex.com\/pointfreeco\/swift-dependencies\/main\/documentation\/dependencies\/withdependencies(_:operation:)-4uz6m\"><code>withDependencies<\/code><\/a>\nfunction to override any dependencies for the scope of one single test.<\/p><\/blockquote>\n<p>The dependencies are stored in a <code>TaskLocal<\/code>. But it still feels like a partial solution because not all code uses Swift Concurrency, and you still need to <a href=\"https:\/\/github.com\/pointfreeco\/swift-dependencies\/blob\/main\/Sources\/Dependencies\/Documentation.docc\/Articles\/Lifetimes.md\">worry about<\/a> propagating dependencies:<\/p>\n\n<blockquote cite=\"https:\/\/github.com\/pointfreeco\/swift-dependencies\/blob\/main\/Sources\/Dependencies\/Documentation.docc\/Articles\/Lifetimes.md\"><p>It is important to note that task locals are not inherited in <em>all<\/em> escaping contexts. It does work\nfor <a href=\"https:\/\/developer.apple.com\/documentation\/swift\/task\/init(priority:operation:)-5k89c\"><code>Task.init<\/code><\/a> and <a href=\"https:\/\/developer.apple.com\/documentation\/swift\/taskgroup\/addtask(priority:operation:)\"><code>TaskGroup.addTask<\/code><\/a>, which make use of\nescaping closures, but only because the standard library special cases those tools to inherit task\nlocals (see <code>copyTaskLocals<\/code> in <a href=\"https:\/\/github.com\/apple\/swift\/blob\/60952b868d46fc9a83619f747a7f92b5534fb632\/stdlib\/public\/Concurrency\/Task.swift#L500-L509\">this<\/a> code).<\/p><p>But generally speaking, task local overrides are lost when crossing escaping boundaries.<\/p><p>[&#8230;]<\/p><p>In order to access dependencies across escaping closures, <em>e.g.<\/em> in a callback or Combine operator, you must do additional work to &ldquo;escape&rdquo; the dependencies so that they can be passed into the closure.<\/p><\/blockquote>\n\n<p>It&rsquo;s more ergonomic not to have to propagate dependencies explicitly, but relying on implicit behavior can be harder to understand and error-prone.<\/p>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2023\/04\/20\/canopy\/\">Canopy<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2021\/12\/31\/lightweight-dependency-injection-using-async-functions\/\">Lightweight Dependency Injection Using Async Functions<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2017\/04\/27\/using-swift-protocol-compositon-for-dependency-injection\/\">Using Swift Protocol Composition for Dependency Injection<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2016\/05\/31\/mocking-dependencies-with-generics\/\">Mocking Dependencies With Generics<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2014\/03\/27\/hypo-dependency-injection-framework\/\">Hypo Dependency Injection Framework<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2013\/02\/13\/typhoon-dependency-injection-framework\/\">Typhoon Dependency Injection Framework<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2013\/01\/07\/dependency-injection-is-a-virtue\/\">Dependency Injection Is a Virtue<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2008\/11\/04\/dependency-injection-myth\/\">Dependency Injection Myth: Reference Passing<\/a><\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Brandon Williams and Stephen Celis (2018, via Christian Tietze): While unconventional, we hope that it&rsquo;s obvious that this solution of controlling dependencies is superior to the traditional solutions in use today. It also gives us an opportunity to reevaluate deep-seated beliefs we may have. We should continuously question our assumptions. In this case, we found [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2023-12-22T19:33:40Z","apple_news_api_id":"039156f6-a1a0-4bb4-a2a5-d89becc3a3dd","apple_news_api_modified_at":"2023-12-22T19:34:27Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAQ==","apple_news_api_share_url":"https:\/\/apple.news\/AA5FW9qGgS7Sipdib7MOj3Q","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[800,27,71,2200,901,268],"class_list":["post-41560","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-concurrency","tag-craft","tag-programming","tag-swift-concurrency","tag-swift-programming-language","tag-testing"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/41560","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=41560"}],"version-history":[{"count":3,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/41560\/revisions"}],"predecessor-version":[{"id":41563,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/41560\/revisions\/41563"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=41560"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=41560"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=41560"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}