README.md in beethoven-0.0.1 vs README.md in beethoven-0.0.2

- old
+ new

@@ -1,9 +1,120 @@ # Beethoven -TODO: Write a gem description +Composition is an incredibly useful technique in functional programming. +I have been missing that in my development with Ruby, so I set out to implement it here. +In Haskell, you can write a function like: + +```haskell +-- f is a function that takes a value of type a +-- and returns a value of type b +f :: a -> b +``` + +We need some analogy with Ruby concepts. +It doesn't appear to be methods, messages, or objects. +Classes, however, seem to do it nicely. + +1. Replace the arrow with `new` +2. `a` is the *interface* or *duck* that fits the single parameter of the class. +3. `b` is the interface/duck that fits the object produced by new. + +So, we might express a class `F` that implements message `b` and expects an object responding to `a` as: + +```ruby +class F + attr_reader :b + + def initialize(x) + @b = x.a + end +end +``` + +Next up, we want some class that implements the duck that `F` expects. + +```ruby +class G + attr_reader :a + + def initialize(x) + @a = x + end +end + +G.new(5).a +# => 5 + +F.new(G.new(5)).b +# => 5 +``` + +This is class composition. But really, it'd be a lot nicer if we could write: + +```ruby +(F * G).new(5).b +# => 5 +``` + +Or, perhaps you prefer the bash-like pipe operator and reading your compositions from left to right. +No problem: + +```ruby +(G | F).new(5).b +# => 5 +``` + +Naturally, this is quite a bit more interesting when your classes do something other than simply returning the value they were given. +In this example, the classes expect a parameter that duck-types `value`. + +```ruby +class Add5 + def initialize(x) + @value = x.value + end + + def value + @value + 5 + end +end + +class Multiply10 + def initialize(x) + @value = x.value + end + + def value + @value * 10 + end +end + +class Lift + attr_reader :value + + def initialize(x) + @value = x + end +end + +(Add5 * Multiply10 * Lift).new(7).value +#=> 75 + +(Lift | Multiply10 | Add5).new(4).value +#=> 45 +``` + +If you'd prefer to compose classes directly, use `Beethoven::Composer`: + +```ruby +Mul10Add5 = Beethoven::Composer.new(Lift, Multiply10, Add5) +Mul10Add5.new(5).value +#=> 55 +``` + +A more practical example is presented [here](https://gist.github.com/parsonsmatt/c1abbb830b6976566198) + ## Installation Add this line to your application's Gemfile: ```ruby @@ -15,13 +126,9 @@ $ bundle Or install it yourself as: $ gem install beethoven - -## Usage - -TODO: Write usage instructions here ## Contributing 1. Fork it ( https://github.com/[my-github-username]/beethoven/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`)