some_random_word while lives_left && $progress != $word guess = take_guess unless guess_in_word(guess) $lives -= 1 end show_progress(guess) end Monday, April 8, 13 What are the problems?
last_guess: "" }.freeze def lose_life_if_needed(state) return state if state[:secret].include?(state[:last_guess]) state.clone.merge!(lives: state[:lives] - 1) end #... while should_continue(game_state) game_state = show_progress( lose_life_if_needed( take_guess(game_state))) end Monday, April 8, 13 that’s one approach, we’ll look at it again later
Objects don’t depend directly on others’ state Buzzword: encapsulation Monday, April 8, 13 i.e. objects don’t retrieve state of others and make decisions based on it
self, lives: 10, secret: "foo") while continuing @input.take_next_guess(@progress) end end end class Hangman::Progress def add_letter(l) unless @secret.include?(l) @lives -= 1 end #... @display.show(progress_mask) end end Monday, April 8, 13 take_next_guess calls add_letter only switching is based on local state ok, two approaches. Next question:
to make global variables require mutates global shared state Bash-inspired Monday, April 8, 13 “What does require do?” -unscoped import + runs code OK, so clearly it’s multi-paradigmatic. We all know this, tell us something new!
def main_loop @progress = Progress.new(lives: 10, secret: "foo") while @input.take_next_guess(@progress) end end end class Hangman::Progress def add_letter(l) unless @secret.include?(l) @lives -= 1 end #... @display.show(@progress) end end Monday, April 8, 13
def main_loop @progress = ::Progress.new(lives: 10, secret: "foo") while @input.take_next_guess(@progress) end end end class Hangman::Progress def add_letter(l) unless @secret.include?(l) @lives -= 1 end #... @display.show(@progress) end end Monday, April 8, 13 What does this return?
show(progress) #massage puts progress end end class Hangman::WebDisplay def show(progress) #massage erb :hangman_display, :progress => progress end end Monday, April 8, 13 Some nefarious developer writes code expecting html retval
return, we ask: “What should I return?” instead of: “Should I return?” Monday, April 8, 13 And this leads us back toward functional programming, where we care about retvals, but without removing mutation. So we’re in the procedural morass
mutable Anything else can be mutable What happens when we combine with FP style? Monday, April 8, 13 (via monkey-patching and lack of protection against it in the lang)
} def lose_life_if_needed(state) return state if state[:secret].include?(state[:last_guess]) state.clone.merge!(lives: state[:lives] - 1) end #... while should_continue(game_state) game_state = show_progress( lose_life_if_needed( take_guess(game_state))) end TRYING TO WRITE FUNCTIONAL CODE IN RUBY Monday, April 8, 13 Everything is side-effect free
1) end #... while should_continue(game_state) game_state = show_progress( lose_life_if_needed( take_guess(game_state))) end TRYING TO WRITE FUNCTIONAL CODE IN RUBY Monday, April 8, 13 Until now - I hope you had integration tests
We accept that Ruby isn’t the purest language. That doesn’t automatically mean you can’t write good code. OK, but we seem to have problems - code not as good as we want it to be, arguments still raging about how to write good Ruby code. Maybe those problems aren’t the language’s fault. Maybe they’re cultural?
the web Session key sent with every request/response - used to pick up the session where it left off REST? Whassat? Requires a persistently running app server Monday, April 8, 13 And this seems to be a better way to write ‘telling’ code in a web context. Not sure how practical it is, but interesting to see the way a purer OO lang has attempted it
some problems lend themselves to functional solutions, others to OO solutions. It’s our responsibility to pick the right paradigm and the right tool for the job. And that’s the end. Advance to next during applause