Created
September 1, 2016 20:51
-
-
Save bvandgrift/7fd6bd2550af7432007fb0fb91d186ce to your computer and use it in GitHub Desktop.
for jason, closures
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## 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