It's a side effect when
it's handled by the World
40/118
Slide 41
Slide 41 text
Examples
41/118
Slide 42
Slide 42 text
Passing pseudo-global
values around
42/118
Slide 43
Slide 43 text
class SetLocaleMiddleware
def call(env)
locale = detect_locale(env)
with_locale(locale) { @app.(env) }
end
end
43/118
Slide 44
Slide 44 text
Testing
features
44/118
Slide 45
Slide 45 text
Testing features
class RenderView
def call(values)
if feature?
render_with_feature(values)
else
render_without_feature(values)
end
end
end
45/118
Slide 46
Slide 46 text
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
Slide 47
Slide 47 text
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
Slide 48
Slide 48 text
dry-effects
48/118
Slide 49
Slide 49 text
Controlling time
49/118
Slide 50
Slide 50 text
Accessing current time
class CreatePost
include Dry!"Effects.CurrentTime
def call(values)
publish_at = values[:publish_at] !# current_time
# !!$
end
end
50/118
Slide 51
Slide 51 text
Providing current time
class WithCurrentTime
include Dry!"Effects!"Handler.CurrentTime
def call(env)
with_current_time { @app.(env) }
end
end
51/118
Slide 52
Slide 52 text
Providing time
class WithCurrentTime
include Dry!"Effects!"Handler.CurrentTime
def call(env)
with_current_time { @app.(env) }
end
end
52/118
Slide 53
Slide 53 text
Testing
include Dry!"Effects!"Handler.CurrentTime
example do
with_current_time { !!# }
end
53/118
Slide 54
Slide 54 text
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
Slide 55
Slide 55 text
Testing
example do
next_day = Time.now + 86_400
with_current_time(proc { next_day }) { !!" }
end
55/118
Slide 56
Slide 56 text
Dependency injection
56/118
Slide 57
Slide 57 text
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
Slide 58
Slide 58 text
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
Slide 59
Slide 59 text
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
Slide 60
Slide 60 text
Making a container
AppContainer = {
post_repo: PostRepo.new,
!!"
}
60/118
Slide 61
Slide 61 text
Providing dependencies
class ProvideApplication
include Dry!"Effects!"Handler.Resolve(AppContainer)
def call(env)
provide { @app.(env) }
end
end
61/118
Slide 62
Slide 62 text
Providing dependencies
class ProvideApplication
include Dry!"Effects!"Handler.Resolve(AppContainer)
def call(env)
provide { @app.(env) }
end
end
62/118
Slide 63
Slide 63 text
Testing
include Dry!"Effects!"Handler.Resolve
example do
post_repo = double(:post_repo)
provide(post_repo: post_repo) do
# !!#
end
end
63/118
Slide 64
Slide 64 text
Tracing
AppContainer = AppContainer.to_h do |key, value|
[key, Wrapper.new(value)]
end
64/118
Slide 65
Slide 65 text
Tracing
AppContainer = AppContainer.to_h do |key, value|
[key, Wrapper.new(value)]
end
65/118
Slide 66
Slide 66 text
Batteries included
Dry!"Effects.load_extensions(:system)
class App < Dry!"Effects!"System!"Container
Import = injector(!!#)
end
66/118
Slide 67
Slide 67 text
Frozen application
Dry!"Effects.load_extensions(:system)
class App < Dry!"Effects!"System!"Container
Import = injector(!!#)
end
# boot.rb
App.finalize!
67/118
Slide 68
Slide 68 text
class CreateUser
def initialize
end
def call
end
end
68/118
Slide 69
Slide 69 text
State
69/118
Slide 70
Slide 70 text
State
class Add
include Dry!"Effects.State(:result)
def call(b)
self.result += b
nil
end
end
70/118
Slide 71
Slide 71 text
State
class Mult
include Dry!"Effects.State(:result)
def call(b)
self.result *= b
nil
end
end
71/118
Slide 72
Slide 72 text
State
class Calc
include Dry!"Effects!"Handler.State(:result)
def add
Add.new
end
def mult
Mult.new
end
end
72/118
Slide 73
Slide 73 text
State
def call(x, y, z)
with_result(x) do
add.(y)
mult.(z)
"
!
"
end
end
73/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
Slide 79
Slide 79 text
program = Program.new
Dry!"Effects[:state, :counter].(10) do
Dry!"Effects[:cmp, :feature].() do
program.()
end
end
# !# [13, ["hi!", "bye!"]]
79/118
Slide 80
Slide 80 text
program = Program.new
Dry!"Effects[:cmp, :feature].() do
Dry!"Effects[:state, :counter].(10) do
program.()
end
end
# !# [[11, "hi!"], [12, "bye!"]]
80/118
Slide 81
Slide 81 text
More examples
81/118
Slide 82
Slide 82 text
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
Slide 83
Slide 83 text
Timeout
class TimeoutMiddleware
include Dry!"Effects!"Handler.Timeout(:http)
def call(env)
with_timeout(5.0) { @app.(env) }
end
end
83/118
Slide 84
Slide 84 text
Parallel
class PullData
include Dry!"Effects.Parallel
def call(urls)
join(urls.map { |url| par { make_request.(url) } })
end
end
84/118
Slide 85
Slide 85 text
Parallel
class PullData
include Dry!"Effects.Parallel
def call(urls)
join(urls.map { |url| par { make_request.(url) } })
^^^^ ^^^
end
85/118
Slide 86
Slide 86 text
Parallel
class ParallelMiddleware
include Dry!"Effects!"Handler.Parallel
def call(env)
with_parallel.() { @app.(env) }
end
end
86/118
Slide 87
Slide 87 text
dry-effects is a practical-
oriented implementation
87/118
Multi-shot continuations allow
backtracking, parsers, etc.
106/118
Slide 107
Slide 107 text
callcc / Fiber#dup
107/118
Slide 108
Slide 108 text
Level 4
108/118
Slide 109
Slide 109 text
Typed effects
109/118
Slide 110
Slide 110 text
Algebraic
effects are
coming
110/118
Slide 111
Slide 111 text
It works
Trust me!
111/118
Slide 112
Slide 112 text
Pros
— New abilities
— Easy to use
— Already works (React!)
— Easy to test
— Traceable effects
112/118
Slide 113
Slide 113 text
Cons
— Unfamiliar
— Can be overused
— Can be abused
— Require glue code with threading
113/118
Slide 114
Slide 114 text
Next steps for dry-effects
— Add async/await
— Polishing APIs
— More integrations with existing gems
— More docs and examples
— Multi-shot continuations?
114/118
Slide 115
Slide 115 text
It's not all
115/118
Slide 116
Slide 116 text
Learn more
— github.com/topics/algebraic-effects
— github.com/yallop/effects-bibliography
116/118