Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Going functional with algebraic effects
Search
Nikita Shilnikov
September 28, 2019
Technology
890
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Going functional with algebraic effects
Introducing algebraic effects for Ruby with dry-effects
Nikita Shilnikov
September 28, 2019
More Decks by Nikita Shilnikov
See All by Nikita Shilnikov
How to algebraic effects
flashgordon
0
250
Typed Ruby
flashgordon
4
850
Other Decks in Technology
See All in Technology
VS Code × GitHub Copilot での Fabric 開発
ryomaru0825
1
200
なぜAI任せのゲームは面白くならないのか?
hirohasuyoutube
0
350
ビジネスを止めない技術的負債の返済のための戦略とその手法 - 技術的負債と向き合う / Complexity and Simplicity
soudai
PRO
1
260
大阪オフィスに Unitree Go2 がやってきたので Physical AI やってみた
dafujii
0
190
GitHub Agentic Workflows を触ってみる
htkym
2
900
The kernel report
ennael
PRO
1
160
Amazon Bedrock Agents ClassicからAmazon Bedrock AgentCoreへ移行した際、ガードレール設定が2箇所に割れた話
matsunobu
0
160
高負荷プロダクション環境におけるAWS Lambdaのリアル 〜スケールとコストを左右する実行ライフサイクルの技術仕様〜
maimyyym
2
840
Lambda MicroVMsは常駐サーバーの代わりに なるか? Kiro Crew を動かして検証してみた / Kiro Crew on Lambda MicroVMs
k_adachi_01
2
340
Terraformを用いたJamf Pro構成のIaC, GitOps化への挑戦
yukun
0
120
AWS FinOps Agent 結局何が得意なの?
siromi
0
230
営業オントロジーの作り方と、エージェントからの辿り方 ── ナレッジワークの現場から
kworkdev
PRO
1
210
Featured
See All Featured
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
How STYLIGHT went responsive
nonsquared
100
6.3k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
My Coaching Mixtape
mlcsv
0
330
Visualization
eitanlees
153
17k
Unsuck your backbone
ammeep
672
58k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.1k
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
530
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
RailsConf 2023
tenderlove
30
1.6k
Transcript
None
Going Functional with Algebraic Effects
! 3/118
Me Nikita Shilnikov — I write code — I write
code in Ruby 4/118
Algebraic effects? 5/118
#hype 6/118
Algebraic effects are a new burrito 7/118
8/118
Already in production 9/118
React is wired with algebraic effects 10/118
Functional programming? 11/118
f = !" x { x + 5 } 12/118
Integer !" Integer f = !# x { x +
5 } 13/118
f = !" x { puts(x); x + 5 }
14/118
Launching rockets problem 15/118
doSomethingNice !" Integer !# IO () 16/118
Integer !" Integer, ? f = !# x { puts(x);
x + 5 } 17/118
Integer !" Puts[Integer] : Integer f = !# x {
puts(x); x + 5 } 18/118
Integer !" Puts[Integer] : Integer ^^^^^^^^^^^^^ f = !# x
{ puts(x); x + 5 } 19/118
Puts[Integer] is an effect 20/118
g = !" x { x + get(:y) } 21/118
Integer !" Get[Symbol !" Integer] : Integer g = !#
x { x + get(:y) } 22/118
Integer !" Get[Symbol !" Integer] : Integer ^^^^^^^^^^^^^^^^^^^^^^ g =
!# x { x + get(:y) } 23/118
Effects in type signatures reveal code's intentions 24/118
Effects are not side effects 25/118
f = !" x { puts(x); x + 5 }
Side effects: Integer !" Integer Effects: Integer !" Puts[Integer] : Integer 26/118
To run effectful code you'll need a handler 27/118
f = !" x { puts(x); x + 5 }
with_puts { f.(10) } 28/118
f = !" x { puts(x); x + 5 }
with_puts { f.(10) } ^^^^^^^^^ 29/118
Every effect must have a handler 30/118
f = !" x { puts(x); x + 5 }
f.(10) # !# Error! 31/118
The World 32/118
def greet print "Hello" end def main handle_print { greet
} end 33/118
main handle_print greet print 34/118
World main # | handle_print # | greet # |
print # ↓ 35/118
World main # | handle_print # | * greet #
| ↑ print # ↓ | 36/118
World main # | handle_print # | * | greet
# | ↑ | print # ↓ | ↓ 37/118
World main # | ↑ | # | | |
greet # | | | print # ↓ | ↓ 38/118
World is the Handler 39/118
It's a side effect when it's handled by the World
40/118
Examples 41/118
Passing pseudo-global values around 42/118
class SetLocaleMiddleware def call(env) locale = detect_locale(env) with_locale(locale) { @app.(env)
} end end 43/118
Testing features 44/118
Testing features class RenderView def call(values) if feature? render_with_feature(values) else
render_without_feature(values) end end end 45/118
Testing features def call(env) feature_response, no_feature_response = with_feature do @app.(env)
end if feature_response !" no_feature_response # !!# end end 46/118
Testing features def call(env) feature_response, no_feature_response = with_feature do @app.(env)
end if feature_response !" no_feature_response # !!# end end 47/118
dry-effects 48/118
Controlling time 49/118
Accessing current time class CreatePost include Dry!"Effects.CurrentTime def call(values) publish_at
= values[:publish_at] !# current_time # !!$ end end 50/118
Providing current time class WithCurrentTime include Dry!"Effects!"Handler.CurrentTime def call(env) with_current_time
{ @app.(env) } end end 51/118
Providing time class WithCurrentTime include Dry!"Effects!"Handler.CurrentTime def call(env) with_current_time {
@app.(env) } end end 52/118
Testing include Dry!"Effects!"Handler.CurrentTime example do with_current_time { !!# } end
53/118
Testing RSpec.configure do |c| c.include Dry!"Effects!"Handler.CurrentTime now = Time.now c.around
do |ex| with_current_time(proc { now }, &ex) end end 54/118
Testing example do next_day = Time.now + 86_400 with_current_time(proc {
next_day }) { !!" } end 55/118
Dependency injection 56/118
Dependency injection class CreatePost include Dry!"Effects.Resolve(:post_repo) def call(values) if valid?(values)
post_repo.create(values) else !!# end end end 57/118
Dependency injection class CreatePost include Dry!"Effects.Resolve(:post_repo) def call(values) if valid?(values)
post_repo.create(values) else !!# end end end 58/118
Dependency injection class CreatePost include Dry!"Effects.Resolve(:post_repo) def call(values) if valid?(values)
post_repo.create(values) else !!# end end end 59/118
Making a container AppContainer = { post_repo: PostRepo.new, !!" }
60/118
Providing dependencies class ProvideApplication include Dry!"Effects!"Handler.Resolve(AppContainer) def call(env) provide {
@app.(env) } end end 61/118
Providing dependencies class ProvideApplication include Dry!"Effects!"Handler.Resolve(AppContainer) def call(env) provide {
@app.(env) } end end 62/118
Testing include Dry!"Effects!"Handler.Resolve example do post_repo = double(:post_repo) provide(post_repo: post_repo)
do # !!# end end 63/118
Tracing AppContainer = AppContainer.to_h do |key, value| [key, Wrapper.new(value)] end
64/118
Tracing AppContainer = AppContainer.to_h do |key, value| [key, Wrapper.new(value)] end
65/118
Batteries included Dry!"Effects.load_extensions(:system) class App < Dry!"Effects!"System!"Container Import = injector(!!#)
end 66/118
Frozen application Dry!"Effects.load_extensions(:system) class App < Dry!"Effects!"System!"Container Import = injector(!!#)
end # boot.rb App.finalize! 67/118
class CreateUser def initialize end def call end end 68/118
State 69/118
State class Add include Dry!"Effects.State(:result) def call(b) self.result += b
nil end end 70/118
State class Mult include Dry!"Effects.State(:result) def call(b) self.result *= b
nil end end 71/118
State class Calc include Dry!"Effects!"Handler.State(:result) def add Add.new end def
mult Mult.new end end 72/118
State def call(x, y, z) with_result(x) do add.(y) mult.(z) "
! " end end 73/118
State calc = Calc.new calc.(5, 6, 7) 74/118
State calc = Calc.new calc.(5, 6, 7) # !" (5
+ 6) * 7 !# 77 75/118
State calc = Calc.new calc.(5, 6, 7) # !" [77,
" ! "] 76/118
All effects are composable* 77/118
Composition class Program include Dry!"Effects.Cmp(:feature) include Dry!"Effects.State(:counter) def call if
feature? self.counter += 2 "bye" else self.counter += 1 "hi!" end end end 78/118
program = Program.new Dry!"Effects[:state, :counter].(10) do Dry!"Effects[:cmp, :feature].() do program.()
end end # !# [13, ["hi!", "bye!"]] 79/118
program = Program.new Dry!"Effects[:cmp, :feature].() do Dry!"Effects[:state, :counter].(10) do program.()
end end # !# [[11, "hi!"], [12, "bye!"]] 80/118
More examples 81/118
Timeout class MakeRequest include Dry!"Monads[:try] include Dry!"Effects.Timeout(:http) def call(url) Try()
{ HTTParty.get(url, timeout: timeout) } end end 82/118
Timeout class TimeoutMiddleware include Dry!"Effects!"Handler.Timeout(:http) def call(env) with_timeout(5.0) { @app.(env)
} end end 83/118
Parallel class PullData include Dry!"Effects.Parallel def call(urls) join(urls.map { |url|
par { make_request.(url) } }) end end 84/118
Parallel class PullData include Dry!"Effects.Parallel def call(urls) join(urls.map { |url|
par { make_request.(url) } }) ^^^^ ^^^ end 85/118
Parallel class ParallelMiddleware include Dry!"Effects!"Handler.Parallel def call(env) with_parallel.() { @app.(env)
} end end 86/118
dry-effects is a practical- oriented implementation 87/118
v0.1 Cache, Cmp, CurrentTime, Defer, Env, Implicit, Interrupt, Lock, Parallel,
Random, Reader, Resolve, Retry, State, Timeout, Timestamp 88/118
Effects almost don't affect existing code 89/118
Just like monads, effects are language agnostic 90/118
91/118
Many shades of algebraic effects 92/118
Level 0 93/118
React 94/118
React.useState Dry!"Effects.CurrentTime 95/118
Level 1 96/118
dry-effects 97/118
Dry!"Effects.Retry Dry!"Effects.Parallel 98/118
Level 2 99/118
Fibers 100/118
Async/await 101/118
Async/await server.rb # scheduler !!" # ↑ user_repo.rb # find_user
102/118
Async/await server.rb # scheduler !!" # ↑ ↓ user_repo.rb #
find_user 103/118
Level 3 104/118
Multi-shot continuations server.rb # scheduler !!" # ↑ ↓ ↓
↓ user_repo.rb # find_user 105/118
Multi-shot continuations allow backtracking, parsers, etc. 106/118
callcc / Fiber#dup 107/118
Level 4 108/118
Typed effects 109/118
Algebraic effects are coming 110/118
It works Trust me! 111/118
Pros — New abilities — Easy to use — Already
works (React!) — Easy to test — Traceable effects 112/118
Cons — Unfamiliar — Can be overused — Can be
abused — Require glue code with threading 113/118
Next steps for dry-effects — Add async/await — Polishing APIs
— More integrations with existing gems — More docs and examples — Multi-shot continuations? 114/118
It's not all 115/118
Learn more — github.com/topics/algebraic-effects — github.com/yallop/effects-bibliography 116/118
Thank you — twitter.com/NikitaShilnikov — dry-rb.org/gems/dry-effects — dry-rb.org/gems/dry-system — github.com/dry-rb/dry-effects
— t.me/flash_gordon 117/118
Questions? 118/118