Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Algebraic Effects in Ruby

Krzysztof
December 21, 2023

Algebraic Effects in Ruby

A presentation about what effects are, side effects, algebraic effects. It approaches the topic from theoretical POV and then a more practical, showcasing usages in ruby.
It is a second version of the presentation, cut down to be much shorter than the initial one which took an hour to present, this one should be more fit for a 30 minute time frame.

Krzysztof

December 21, 2023
Tweet

More Decks by Krzysztof

Other Decks in Programming

Transcript

  1. 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
  2. An effect is some capability your code requires to be

    executed(1) Effects - definitions
  3. Side Effect An effect of code taking place outside the

    callers local environment Side effect is not an effect
  4. 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
  5. Side Effects Writing purely functional code can be an attractive

    idea; it makes your code robust, testable, //. and useless!(1)
  6. 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
  7. 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)
  8. Algebraic Effect Explicit effect of a code, introduced in a

    composable and modular way. Huh? Category Theory?
  9. Algebraic Effects • Can replace side effects • Works well

    with callbacks • Expected • Easy to test • Makes for an Explicit code.(1)
  10. 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
  11. 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
  12. 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?
  13. • 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)
  14. Introduction and Elimination in practice - mockup def hey print

    "hey" end def app handle_printing { hey } end
  15. 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
  16. 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
  17. 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
  18. 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
  19. How do you guys deal with DP? (it obviously stands

    for dependency injection) Ever received a PR comment like this?
  20. 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
  21. 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
  22. 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
  23. 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"]
  24. 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
  25. 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"]]
  26. 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"]]
  27. What happens if we introduce an effect without a handler?

    Let’s try to access data from reader, but without handling it
  28. 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
  29. 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
  30. 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