I'm looking for examples of functional code in ruby. Maybe you know some gems, where I can find such a code?
6 Answers
I've been gathering examples of functional programming with Ruby:
- Wiki page
- Presentation: Functional programming with Ruby.
3 Comments
Enumerable#lazy (which is similar to Scala's view method if you are familiar with that), which returns a lazy "view" of the underlying collection and provides lazy implementations of Ruby's Enumerable protocols. So, e.g. calling an_enumerator.lazy.map { ... } will not result in an infinite loop even if the Enumerator is infinite.Set should return a Set, an Enumerator should return an Enumerator and so on. The way it is done in Ruby or .NET, where all operations return the same type, regardless of what you started with, is really annoying. At least in .NET, you get a very general, abstract, potentially lazy interface type (IEnumerable), but in Ruby you always get a concrete, eager, fully realized Array. Scala gets this right. (Scala's collections framework is hands down the best. Smalltalk, STL, Ruby aren't even close.)A couple of months ago, I wondered whether it was possible to make a mixin that behaves exactly like Enumerable, but is based on folding instead of enumerating. Or more precisely, I already knew that it was possible to do this, since fold is a generic iteration operation that is equal in expressive power to foreach, but I wanted to know what it would look like and how hard it would be.
I got bored after I had implemented most methods up to the letter g, so it's incomplete. Also, the version I show here is simplified, since making it behave exactly like Enumerable is a PITA in Ruby. (For example, there are overloaded methods in there, but Ruby doesn't support overloading. This isn't a problem for most Ruby implementations, because they implement Enumerable in Java or C# or other languages that do support overloading, but it is pretty painful when doing it in pure Ruby, so I decided to leave it out.)
Enumerable is full of higher-order procedures, and of course fold (or reduce / inject as it is called in Ruby) is itself a higher-order procedure, so this code is full of them.
module Enumerable def all? reduce(true) {|res, el| res && yield(el) } end def any? reduce(false) {|res, el| res || yield(el) } end def collect reduce([]) {|res, el| res + yield(el) } end alias_method :map, :collect def count reduce(0) {|res, el| res + 1 if yield el } end def detect reduce(nil) {|res, el| if yield el then el end unless res } end alias_method :find, :detect def drop(n=1) reduce([]) {|res, el| res.tap {|res| res + el unless n -= 1 >= 0 }} end def each reduce(nil) {|_, el| yield el } end def each_with_index reduce(-1) {|i, el| (i+1).tap {|i| yield el, i }} end def find_all reduce([]) {|res, el| res.tap {|res| res + el if yield el }} end alias_method :select, :find_all def grep(pattern) reduce([]) {|res, el| res.tap {|res| res + yield(el) if pattern === el }} end def group_by reduce(Hash.new {|hsh, key| hsh[key] = [] }) {|res, el| res.tap {|res| res[yield el] << el }} end def include?(obj) reduce(false) {|res, el| break true if res || el == obj } end def reject reduce([]) {|res, el| res.tap {|res| res + el unless yield el }} end end You will notice that I use side-effects in some places. I could have written it without side-effects instead, but it would have been much more complicated. And the problem is that there is no way for me to ensure that the reduce method, which is provided by the client of the mixin, or any of the methods from the core library, don't have side-effects.
Another nice example is the Prelude library for Ruby, which is an implementation of some parts of the Haskell Prelude (Haskell's equivalent to Ruby's core library) in Ruby.
Comments
You might be interested in the talk "Better Ruby through Functional Programming" by Dean Wampler, it shows how to use a FP style with Ruby:
There's also "Thinking functionally in Ruby" by Tom Stuart:
2 Comments
"The Ruby Programming Language" has half a chapter where they work on a couple of additions to enumerable to make ruby look like Haskell.
You may also want to browse previous Stack Overflow questions tagged with both "Ruby" and "functional-programming" here.
Comments
This is not pretty or efficient code. It is to explain the difference between a functional and non functional style.
Something you need to think about when doing functional concepts in ruby is that our vm is not built for it, so without transparent referential optimization, you WILL eat a lot more memory writing in a functional style.
# non functional class Post attr_accessor :title, :body end all_posts = [] # create a post p = Post.new p.title = "Hello world" p.body = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." # put post in the array all_posts << p # fix post title all_posts[0].title = "Opps, fixed title" #functional class Post attr_reader :title, :body def initialize(attrs={}) attrs.each {|k,v| instance_variable_set "@#{k.to_s}", v } end end all_posts = [] # create a post p = Post.new({title: "Hello world", body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."}) # add to array via new array creation all_posts = all_posts + [p] # be wary when all_posts is a very large collection! old_post = p p = Post.new({title: "Oops, fixed title", body: old_post.body}) all_posts = all_posts - [old_post] + [p] I post on functional programming in ruby quite often at my blog: http://rubylove.io
select,%,==,map,*ortakehave side-effects or not. But if even one of them has even a single side-effect, then the entire piece of code is not functional according to the second definition. Side-effects are contageous. That's why it is so hard to write side-effect-free code in Ruby: you cannot call any method that you haven't written yourself.