Skip to content

Instantly share code, notes, and snippets.

@lucaong
Created February 8, 2016 22:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lucaong/57e2ef8e4a17c58f522e to your computer and use it in GitHub Desktop.
Save lucaong/57e2ef8e4a17c58f522e to your computer and use it in GitHub Desktop.
Proof of concept of Clojure-like atom in Crystal
class Atom(T)
@state : T
@channel : Channel::Buffered({(T -> T), Channel::Buffered(T | ValidationError)})
@validator : T -> Bool
def initialize(@state : T, @validator = ->(i : T){ true } : T -> Bool)
@channel = Channel({(T -> T), Channel::Buffered(T | ValidationError)}).new(1)
spawn do
loop do
block, c = @channel.receive
updated = block.call(@state)
if @validator.call(updated)
@state = updated
c.send @state
else
c.send ValidationError.new("Invalid value #{updated}")
end
end
end
end
def self.new(state : T, &block : T -> Bool)
new(state, block)
end
def swap!(&block : T -> T)
rec = Channel(T | ValidationError).new(1)
@channel.send({block, rec})
receive(rec)
end
def reset!(value : T)
rec = Channel(T | ValidationError).new(1)
@channel.send({ ->(i : T){ value }, rec})
receive(rec)
end
def get
@state
end
private def receive(rec : Channel::Buffered(T | ValidationError))
r = rec.receive
raise r if r.is_a? ValidationError
r
end
class ValidationError < Exception; end
end
# Usage:
# Define an atom with a validator
a = Atom.new(0) do |i|
i > 0
end
# Use #swap! to update the atom depending on previous state
100.times do |i|
spawn do
puts a.swap! { |i| i + 1 }
end
end
sleep 1
# Get the atom state
puts a.get
# Reset the atom state to a given value
a.reset!(42)
puts a.get
# Trying to set the atom to an invalid state raises `Atom::ValidationError`
a.reset!(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment