Functional Ruby

f:id:vasilyjp:20180927090637j:plain


Modern Ruby: Functional Ghost in an imperative shell

Ruby is a language designed in the following steps: take a simple lisp language (like one prior to CL). remove macros, s-expression. add simple object system (much simpler than CLOS). add blocks, inspired by higher order functions. add methods found in Smalltalk. add functionality found in Perl (in OO way). So, Ruby was a Lisp originally, in theory. Let's call it MatzLisp from now on. ;-) matz.

Buzzwords such as "Functional Programming", "Curried Functions", "Partial Function Application" and "Lazy Evaluation" have been filling forms and tech blogs, mostly in a pure academic sense and also spoken about in the realm of purely functional languages. It turns out that ruby has its roots in functional programming, even blocks one of the most prominent features of ruby comes from higher order functions. Functional programming ideas can be implemented and exercised in non purely functional languages to produce better testable, readable and less error prone code. Purely functional languages don't own these ideas but merely borrow from them, they belong partially to lambda calculus a branch of mathematics.

What ideas can be taken away from the realm of purely functional languages?

As far as I know a consensus as to what functional programming is has not been reached in as strong a way that object oriented programming is defined by: Inheritance Encapsulation Objects may carry both state and behaviour

Functional Programming has the qualities of:

Immutability & Stateless Functions - No Surprises!

Once a variable is defined its state may never change. (i.e only named constants, not variables) A function provided the same input will always produce the same output, no internal function state is allowed to change between calls. (The desired effect of having immutable variables.)

Higher Order Functions - Functions in Functions out.

Functions take functions as input and return new functions return as output. Partially applied functions:

Lazy Evaluation - ...should really be called Programatic Procrastination.

Values are calculated only when needed and not before.

State v.s No State

[ruby] #A function that carries external state def numberUniq!(inptLst) return inptLst.uniq!.length end #Stateless Implementation def numberUniq(inptLst) input = inptLst.dup.freeze return input.uniq.length end inputList = [1,1,1,2,3,3,3,3] puts "Input Before Execution:#{inputList}\n Function Output:#{numberUniq!(inputList)} Input After Execution:#{inputList}" puts "Input Before Execution:#{inputList}\n Function Output:#{numberUniq(inputList)} Input After Execution:#{inputList}" [/ruby]

Analysis

After running the above program the output was as follows: Input Before Execution:[1, 1, 1, 2, 3, 3, 3, 3] Function Output:3 Input After Execution:[1, 2, 3] Input Before Execution:[1, 1, 1, 2, 3, 3, 3, 3] Function Output:3 Input After Execution:[1, 1, 1, 2, 3, 3, 3, 3] In imperative programming there is no contract between the compiler and the author of the software about ensuring the output of a function will always be the same for the same input. If we call "numberUniq!" in conjunction with other functions that operate on the same array as input it can easily be seen that interference may easily occur. In the second implementation we can guarantee immutability by first making a duplicate and then calling freeze to virtually turn the value into a constant. By calling these two functions we can promise to the caller of the function that A: The function will not modify the input(.dup) and B: The same data passed to this function for N times will return the same result N times (.freeze).

Higher Order Functions - Ground Zero Basics

The concept of higher order functions is just as easy to grasp as functions that work on scalars and lists. First before getting into the discussion of what currying and partial function application are and how we can put them to work in everyday production code, lets get a mathematical definition of exactly what a function is. "A function f takes an input x, and returns a single output f(x). One metaphor describes the function as a "machine" or "black box" that for each input returns a corresponding output." - Wikipedia So the two requirements are that in order to be a function it must not only accept input but always return some form of output otherwise it is only a procedure or a block instructions.

Currying and partial function application

The process of currying is very simple, instead of one function that takes N arguments, a curried function is a series of N functions that take one argument each. For example if we define a function 'f' to be a function that takes four arguments and returns their sum: f (x, y, z, w) = x + y + z + w we define a function f{x,y,z,w} = f{ x + g {y + h{z + i {w}} } } In reality the curried function runs considerably slower compared to its simpler one dimensional implementation that takes N arguments since we now have four times the overhead of calling the function, but there is great merit to this approach. By writing our functions this way it allows us to reuse the curried definition of the function to easily define new variations (partial application) and for testing purposes we now have four smaller functions we can write tests for rather than one large function we need to cover.

Partial Application of functions

Say we wanted to write a function that multiplies a variable x by a constant K. We could write: [ruby] def mul8(x) x * 8 end [/ruby] But what if we wanted to define a bunch of functions like this? Do we need to formally write the definition each time? Partial function application to the rescue! [ruby] mulXY = lambda{|x,y| x * y} curriedMulXY = mulXY.curry 25 == curriedMulXY.(5).(5) #true mul3Y = curriedMulXY.(3) #returns Partially Applied Function equivalent to lambda{|y| 3 * y} mul9Y = curriedMulXY.(9) #returns Partially Applied Function equivalent to lambda{|y| 9 * y} [/ruby] This use of Proc#curry is more or less syntactic sugar that can be used in place of writing mulXY manually as: [ruby] def mulXY(x) Proc.new do |y| x * y end end [/ruby] The key idea to take away here is that inside the body of the proc we define x's value is fixed to whatever we initially passed in, so from a functional perspective calling mulXY(3) is nearly identical to writing the following proc directly: [ruby] Proc.new do |y| 3 * y end [/ruby] As you can see partial function application is really just another way to view writing closures and the process of currying simply transforms a single function that takes N parameters to N functions that take one parameter each successively calling the next. If a curried function is called with all of the parameters that it takes passed in, then the normal output of the function is returned. If that same curried function taking N values as input is called with less than N values the returned result is a function with its behaviour partially defined.

Why hasn't Currying been adapted yet despite having been introduced since the release of Ruby 1.9?

There are a few opinions as to why curried functions in ruby are still not in widespread use but after using Proc#curry it may appear a little obvious. Currying is an after thought, an addition to Proc that got merged into trunk later. You have to take a Proc and then run .curry on it which means a curried state is not a natural state for functions to exist in. Currying most likely isn't in production code for the main reason still being a non-standard way of doing things that many rubyists haven't touched yet for fear of being called out for splitting from the herd. I personally think this is a bad reason not to apply currying where it makes sense and hope this little article is enough to spark your interest. If you want to write ruby with our team please contact: info@vasily.jp WE ARE HIRING!! Till Next time, Nick

カテゴリー