Slide 1

Slide 1 text

Algebraic Effects in Ruby What, when, why, how? Introduction to a new, cool concept

Slide 2

Slide 2 text

Agenda 1. Effects 2. Side effect and algebraic effect 3. Theory, history 4. Scientific approach 5. Examples - dry-effects 6. dry-rb philosophy 7. Closing notes 8. Sources This is you right now, but I need you to focus, I am doing this for you

Slide 3

Slide 3 text

An effect is some capability your code requires to be executed(1) Effects - definitions

Slide 4

Slide 4 text

Side Effect An effect of code taking place outside the callers local environment Side effect is not an effect

Slide 5

Slide 5 text

Side Effects ● Non deterministic ● Hard to test ● Unexpected ● Callbacks Change introduced by your code that is not a direct return value of - for example - a method

Slide 6

Slide 6 text

Side Effects Writing purely functional code can be an attractive idea; it makes your code robust, testable, //. and useless!(1)

Slide 7

Slide 7 text

Quote explanation - what do algebraic effects achieve/provide “Computational effects that can be represented by an equational theory whose operations produce the effects at hand” A way to model and describe effects of different types of operations in a deterministic way. Gordon Plotkin paper(2) 2003 Me, now

Slide 8

Slide 8 text

Do people even use it? Not just papers! Programming languages! Daan Leeijen had one paper(3) and made Koka(4) but Matija Pretnar wrote a more accessible introduction paper(5) and made eff(6)

Slide 9

Slide 9 text

Algebraic Effect Explicit effect of a code, introduced in a composable and modular way. Huh? Category Theory?

Slide 10

Slide 10 text

Algebraic Effects ● Can replace side effects ● Works well with callbacks ● Expected ● Easy to test ● Makes for an Explicit code.(1)

Slide 11

Slide 11 text

Ugly graphics to get your attention Algebraic Effect Eliminate Introduce

Slide 12

Slide 12 text

It is a relatively new in programming, concept, still under a lot of research, even with some funding from EU in Poland(7) More science words and facts

Slide 13

Slide 13 text

Quick comparison Side effects are necessary, but they are pretty much one of the most common cause of bugs(8) Side Effects Algebraic Effects nondeterministic deterministic

Slide 14

Slide 14 text

Whatever

Slide 15

Slide 15 text

Functional programmers use AE as alternative to monads But we, god fearing ruby programmers can use for normal stuff! But what good can we do for the world with it?

Slide 16

Slide 16 text

● Continuable Error Handling ● Promises and Parallel execution ● Caching ● Timeouts ● Feature toggles ● Dependency injection ● Move the context around abstraction layers in a safer and predictable way ● And more/! VERY COOL STUFF (if you are a loser nerd)

Slide 17

Slide 17 text

Introduction and Elimination in practice - mockup def hey print "hey" end def app handle_printing { hey } end

Slide 18

Slide 18 text

Dry-effects, ruby gem One of the least (second to last) popular dry gems It is very nice! Originally made by Nikita Shilnicov, now maintained by the dry-rb team

Slide 19

Slide 19 text

Read class ApplicationController < ActionController::Base include Dry::Effects/:Handler.Reader(:current_user) before_action :set_current_user private def set_current_user @current_user = User.find(session[:user_id]) # with_current_user is provided by the include statement with_current_user(@current_user) { yield } end end

Slide 20

Slide 20 text

Read - 2 class UserProfileComponent < ViewComponent::Base include Dry::Effects.Reader(:current_user, default: nil) def logged_in_user_name 'You are logged in as: ' + current_user.name end end

Slide 21

Slide 21 text

RSpec.configure do |rspec| rspec.include Dry::Effects/:Handler.Reader(:current_user), type: :component end RSpec.describe UserProfileComponent, type: :component do let(:current_user) { User.new(name: 'Dr. Brule') } before do with_current_user(current_user) { render_inline(component) } end it do is_expected.to have_text 'You are logged in as: Dr. Brule' end end

Slide 22

Slide 22 text

Introduction and Elimination in dry-rb include Dry::Effects/:Handler.Reader(:current_user) with_current_user(@current_user) { yield } include Dry::Effects.Reader(:current_user, default: nil) Eliminate Introduce

Slide 23

Slide 23 text

How do you guys deal with DP? (it obviously stands for dependency injection) Ever received a PR comment like this?

Slide 24

Slide 24 text

This is how I do it

Slide 25

Slide 25 text

Dependency Injection - regular class UsersController def index render json: UserService.new(UserRepository.new).call end end class UserService def initialize(user_repository) @user_repository = user_repository end def call @user_repository.get_all end end class UserRepository def get_all User.all end end

Slide 26

Slide 26 text

DP - Algebraic style class UserService include Dry::Effects.Resolve(:user_repo) def call user_repo.get_all end end class UsersController include Dry::Effects/:Handler.Resolve def index provide(user_repo: UserRepository.new) do render json: UserService.new.call end end end

Slide 27

Slide 27 text

DP - testing include Dry::Effects/:Handler.Resolve context do user_repo = double(:user_repo) provide(user_repo: user_repo) do #==. end end

Slide 28

Slide 28 text

State class Add include Dry::Effects/:State(:result) def call(x) self.result += x nil end end class Subtract include Dry::Effects/:State(:result) def call(x) self.result -= x nil end end

Slide 29

Slide 29 text

State - 2 class Calc include Dry::Effects/:Handler.State(:result) def add Add.new end def subtract Subtract.new end end

Slide 30

Slide 30 text

State - 3 def call(x, y, z) with_result(x) do add.(y) subtract.(z) "Done" end end calc = Calc.new calc.(42, 12, 4) #=> 42 + 12 - 4 = 50 # => [50, "Done"]

Slide 31

Slide 31 text

Composing Algebraic Effects class MyThing include Dry::Effects.Cmp(:feature_flag) include Dry::Effects.State(:result) def call() if feature_flag self.result += 7 "Branch 1" else self.result += 1 "Branch 2" end end end

Slide 32

Slide 32 text

Composition - 2 work = MyThing.new Dry::Effects[:state, :result].(42) do Dry::Effects[:cmp, :feature_flag].() do work.() end end # => [50, ["Branch 1", Branch 2"]]

Slide 33

Slide 33 text

Composition - 3 work = MyThing.new Dry::Effects[:cmp, :feature_flag].() do Dry::Effects[:state, :result].(42) do work.() end end # => [[43, "Branch 2"], [49, "Branch 1"]]

Slide 34

Slide 34 text

What happens if we introduce an effect without a handler? Let’s try to access data from reader, but without handling it

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

Dry-effects philosophy Algebraic Effects can do what side effects do but more explicitly Dry-effects want to put you in control of function effects in your application. They are harder to control, and understand, must be used more cautiously Writing code that way takes more time, but it scales better, and crashes less

Slide 38

Slide 38 text

Just an introduction as I said at the start dry-rb introduction is a lovely resource for cementing what you might have learned today And if you like this mix of OOP and FP, try hanami

Slide 39

Slide 39 text

More about dry-effects? Algebraic effects in rb? https://hanamimastery.com/episodes/11-effective-ruby-pr ogramming-with-dry-effects#localization-in-hanami-with- dry-efects - by Sebastian Wilgosz https://github.com/yallop/effects-bibliography

Slide 40

Slide 40 text

Sources: 1. Dry-effects introduction by dry-rb team https://dry-rb.org/gems/dry-effects/0.1/ 2. HANDLING ALGEBRAIC EFFECTS by GORDON D. PLOTKIN AND MATIJA PRETNAR https://homepages.inf.ed.ac.uk/gdp/publications/handling-algebraic-effects.p df 3. Algebraic Effects for Functional Programming by Daan Leijen https://www.microsoft.com/en-us/research/wp-content/uploads/2016/08/algeff-t r-2016-v2.pdf 4. Koka-lang by Daan Leeijen https://github.com/koka-lang/koka 5. An Introduction to Algebraic Effects and Handlers by Matija Pretnar https://www.eff-lang.org/handlers-tutorial.pdf 6. eff-lang by Matija Pretnar https://github.com/matijapretnar/eff/ 7. Algebraic Effects and Continuations by Maciej Piróg & Dariusz Biernacki https://ii.uni.wroc.pl/~mpirog/alg-eff-cont/ 8. Isolate Side Effects in Ruby by Tom Dalling https://semaphoreci.com/community/tutorials/isolate-side-effects-in-ruby Thanks to smart people like them we get new stuff to code with