Skip to content

Instantly share code, notes, and snippets.

@bvandgrift
Created September 1, 2016 20:51
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 bvandgrift/7fd6bd2550af7432007fb0fb91d186ce to your computer and use it in GitHub Desktop.
Save bvandgrift/7fd6bd2550af7432007fb0fb91d186ce to your computer and use it in GitHub Desktop.
for jason, closures
## off the top of my head, syntax problems are an exercise for the reader
list = [:a, 'list', 3, 1.15, {b: 'yes, really'}]
# a simple list comprehension.
list.each do |thing|
puts thing.class # or some triviality
end
# functions take blocks, by default -- a block is a closure over
# set of functionality, so consider this:
def other_each(l)
if block_given?
for i in l # yes, this is manky
yield l
end
else
puts "gots nothin to do"
end
end
# now we can:
other_each(list) do |thing|
puts thing.class # or some triviality
end
# this works exactly like .each above, because .each is implemented this way,
# and tacked onto the Array class via the Enumerable mixin. it also works on hashes:
a_hash = {a: 1, b: 2}
a_hash.each { |key, value| puts "key is #{key}, value is #{value}" }
# once you get your head all the way around this -- that blocks can be passed around
# like a cute puppy at the park, you can do some pretty interesting things. imaging we want to
# create a class that can be extended overwriting its primary execution function 'do':
class Doer
DEFAULT_DOER = ->(*things) { puts "handed #{things.length} things." }
@@doer_fn = DEFAULT_DOER
attr_accessor :doer_fn
def initialize
@doer_fn = @@doer_fn
end
def do_with
if block_given?
@doer_fn = block
end
end
# class-level fn, see below
def self.do_with_block
if block_given?
@@doer_fn = block
end
end
def self.do_with(block)
@@doer_fn = block
end
def do(*stuff) # accepts any number of arguments
@doer_fn.call(stuff)
end
end
# subclass:
class SingleDoer < Doer
# using the class_level fn, we can just redefine doer_fn, and everything
# works
do_with ->(first) { "got one argument: #{first.inspect}" }
end
class DoubleDoer < Doer
# using the other class level fn, we can use a block instead of a lambda.
# each thing is useful at different times.
do_with_block do |f, s|
puts "got two arguments, #{f.inspect}, #{s.inspect}"
end
end
# this is all because blocks are closures that can be passed around,
# and because functions take a block argument.
# incidentally, this is how you descend into DSLs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment