Skip to content

Instantly share code, notes, and snippets.

@flash-gordon
Created February 9, 2019 09:31
Show Gist options
  • Save flash-gordon/4de40ea18cc38e33e54c4f9136b846a6 to your computer and use it in GitHub Desktop.
Save flash-gordon/4de40ea18cc38e33e54c4f9136b846a6 to your computer and use it in GitHub Desktop.
This shows that implementation of state effects doesn't require mutation. This example uses recursion that can be optimized after enabling TCO
require 'fiber'
class RunState
def call(fiber, state = {}, *xs)
result = fiber.resume(*xs)
if fiber.alive?
op, *args = result
case op
when :read
name = args[0]
call(fiber, state, state.fetch(name))
when :write
name, value = args
call(fiber, state.merge(name => value))
end
else
result
end
end
end
module State
def read(name)
Fiber.yield([:read, name])
end
def write(name, value)
Fiber.yield([:write, name, value])
end
end
require_relative 'run_state'
def main
State.write(:foo, 1)
puts(State.read(:foo))
State.write(:bar, "baz")
puts(State.read(:bar))
"done"
end
run = RunState.new.freeze
fiber = Fiber.new { main }
puts run.(fiber)
__END__
1
baz
done
RubyVM::InstructionSequence.compile_option = {
tailcall_optimization: true,
trace_instruction: false
}
require_relative 'run_state'
def deep_calc
State.write(:n, 0)
20000.times do
State.write(:n, State.read(:n) + 1)
end
State.read(:n)
end
run = RunState.new.freeze
fiber = Fiber.new { deep_calc }
puts run.(fiber)
__END__
20000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment