functional ruby
DESCRIPTION
A presentation I gave at the Utah Ruby Users Group meeting on applying functional programming to the Ruby programming language. The topics covered are immutability, persistence, and expressions as applied to Ruby. The exercises at the end are inspired by www.4clojure.com exercises for the Clojure programming language. The challenge is use a functional mindset and write some methods in Ruby that are the analogous Clojure solution to some exercises. There is a gist with sample code for this endeavor at https://gist.github.com/tallguy-hackett/ddfef3b3e963838dfb0bTRANSCRIPT
FUNCTIONAL RUBYNOT THE RUBY YOU'RE USED TO... OR IS IT?
Dave Hackett / @tallguy_hackett
WHAT IS FUNCTIONAL PROGRAMMING?Funtional Programming (FP) is a style of building the structure
and elements of computer programs, that treats computation asthe evaluation of mathematical functions and avoids state andmutable data. It is a declarative programming paradigm, which
means programming is done with expressions. In functional code,the output value of a function depends only on the arguments
that are input to the function, so calling a function f twice with thesame value for an argument x will produce the same result f(x)
both times. —Wikipedia
TL;DRFuntional Programming is like math equations.
IT'S NOT THAT BADThere's just a few things to remember.
IMMUTABILITYValues are values are values. They don't change.
PERSISTENCEValues aren't forgotten, overwritten, or removed over time.
EXPRESSIONSFP is declarative not imperative.
EXAMPLES IN CLOJURE(doseq [e [1 2 3]] (println e))
; => 1; => 2; => 3
(def orig [1 2 3])
(def added (map inc orig))
; => orig == [1 2 3]; => added == [2 3 4]
(reduce + [1 2 3])
; => 6
DOES RUBY HAVE SOME OF THIS ALREADY?Yes... kind of... sometimes.
CHANCES ARE YOU'RE ALREADY APPLYINGSOME FP-ISH TECHNIQUES
ENUMERABLE MODULE
FP IN RUBY![1, 2, 3].each do |e| puts eend
# => 1# => 2# => 3
orig = [1, 2, 3]added = orig.map do |e| e + 1end
# => orig == [1, 2, 3]# => added == [2, 3, 4]
[1, 2, 3].reduce(:+)
# => 6
OOPS!orig = [1, 2, 3]added = orig.map! do |e| e + 1end
# => orig == [2, 3, 4]# => added == [2, 3, 4]
greeting = "hello world"
def end_with_a_bang(phrase) phrase << "!"end
puts end_with_a_bang(greeting)# => hello world!puts greeting# => hello world!puts end_with_a_bang(greeting)# => hello world!!puts greeting# => hello world!!
DOES RUBY HAVE IMMUTABLE VALUES?
WHY DOESN'T RUBY SUPPORT ++ OR --?
NUMBERS ARE IMMUTABLEEVERYTHING IS MESSAGE PASSING IN RUBY
explanation
SYMBOLS ARE IMMUTABLE
RANGES ARE IMMUTABLE
DATE AND TIME ARE IMMUTABLE
VALUES GEMrequire 'values'
Point = Values.new(:x, :y)p = Point.new(1, 2)p.x = 2# => NoMethodError
Values
WHAT ABOUT DATA STRUCTURES?
BUILT IN... EPIC FAIL!
HAMSTER GEMrequire 'hamster'
v = Hamster.vector(1, 2, 3)v1 = v.add(4)# => v == [1, 2, 3]# => v1 == [1, 2, 3, 4]
v2 = v << 5# => v == [1, 2, 3]# => v2 == [1, 2, 3, 5]
v3 = v.set 0, 5# => v == [1, 2, 3]# => v3 == [5, 2, 3]
hamster
EXPRESSION COMPOSITION
LIKE IN MATH...FP is composed of a bunch of functions. Complex functions are
functions composed of other functions.
MATHSo for instance is an example of function
composition. Another way to write the same thing is
f(g(h(x)))
(f ∘ g ∘ h)(x)The story is that the result of is fed as input to whose
result is then fed into for the final result
h(x) g(x)f(x)
CODESo in code this [[...],[...],...][3].drop(2).first
There are 3 method calls (functions)
[3] occurs first
.drop(2) occurs next
.first occurs last
It makes sense to us that results are passed down the messagechain
EXERCISES4clojure
THE ENDBY DAVE HACKETT