Look Around You



Look Around You

0 2


currb

Presentation on currying and function composition in Ruby

On Github parsonsmatt / currb

Look Around You

Look around you.
Look around you.

Just...

look around you!

Have you worked out what we're looking for?

Currying, Composition, and You

What is currying?

  • Technique for changing how many arguments a function takes
  • Popularized by, and named after, American mathematician Haskell Curry
  • (Yes, that's where the name comes from)

Actually...

invented by Moses Schönfinkel

Boyer's Law

"Nothing in math is named after the person that discovered it"

(discovered by Hubert Kennedy)

First, you need a function

add = lambda do |x, y|
  x + y
end

add.call(5, 6)

More tersely,

add = -> x, y { x + y }
add[5, 6]

Now, instead of returning the value, wrap the value in a lambda.

add = lambda do |x, y|
  lambda do 
    x + y
  end
end

add.call(5, 6).call

Now, just move the argument from the outer lambda to the inner lambda!

add = lambda do |x|
  lambda do |y|
    x + y
  end
end

add.call(5).call(6)

Congrats!!

Y'all are curry masters!

explanations usually end here why do we care? what use could this have? not well explained usually

What is a use for Currying?

Function composition!

Scary Math Time

$ foo(x) = 2x + 1 $ $ bar(x) = x^2 $ $ (foo \circ bar)(x) = foo(bar(x)) $ $ \circ(f, g, x) = f(g(x)) $

And, in Ruby...

foo = lambda do |x| 
  2 * x + 1
end

bar = lambda do |x|
  x ** 2
end

(foo * bar).call(x) == foo.call(bar.call(x))

Ruby doesn't have a function composition operator...

... yet!!

class Proc
  def *(other)
    lambda do |x|
      self.call(other.call(x))
    end
  end
end

You may be more used to...

Bash pipes!

# How many classes are defined in your app?
$ cat **/*.rb | grep "^class" | wc -l
# Using the * style:
$ (wc -l * grep "^class" * cat)( **/*.rb )
$  wc -l ( grep "^class" ( cat ( **/*.rb ) ) )

Wait a minute...

Those Unix commands are partially applied!

Fully Applied

$ wc -l **/*.rb
$ grep "^class" **/*.rb
$ cat **/*.rb

surprise curry

Unfortunately, Ruby doesn't have the pipe composition operator...

... yet!

class Proc
  def |(other)
    lambda do |x|
      other.call(self.call(x))
    end
  end
end

Ruby is Object Oriented,

not Lambda Oriented.

Can we use what we've learned today?

Super easy in JavaScript!

Tons of higher order functions and callbacks

Normal

function update(index, item) {
  App.data[index] = item;
}

function renderItems() {
  return App.data.map(function (item, index) { 
    return new Item(item, update.bind(this, index));
  });
}

Currified

function update(index) {
  return function (item) {
    App.data[index] = item;
  }
}

function renderItems() {
  return App.data.map(function (item, index) {
    return new Item(item, update(index));
  });
}

Lambdafied

let update = index => item => App.data[index] = item;

function renderItems() {
  return App.data.map(
    (item, index) => new Item(item, update(index))
  );
}

javascript :(

If you'll permit...

let's go to space!

I really like space. Like, a lot.
This is literally my LinkedIn profile picture
I may be guilty of Joel Spolsky's "architecture astronaut" thing
But it's fine, there's curry in space too!
I want everyone to go to space today, so if anyone is getting lost, please feel free to ask any questions!
So if you're feeling less like this,
And more like this
Please ask something, so we can maybe feel like this
Instead of like this.

What is a function?

The add function

Language Implemented Ruby
add = -> x, y { x + y }
Java
public static int add(int x, int y) {
  return x + y; 
}
Haskell
add :: Int -> Int -> Int
add x y = x + y
Math $add : \mathbb{Z} \rightarrow \mathbb{Z} \rightarrow \mathbb{Z}$$ add(x, y) = x + y $

A function is:

a transformation from something to some other thing

represented like: $$ A \rightarrow B $$

where $A$ is the type of the original thing, and $B$ is the type of the new thing, and the arrow ($\rightarrow$) represents the transformation

Ruby is Object Oriented, not Lambda or Function Oriented.

Is there an equivalence? How can we translate these ideas?

Ruby Class as a Function

RCaaF -- startup idea!

(this is kinda out there so please stop me and ask questions if you want)

How to make a Ruby class like a function?

$$ f : A \rightarrow B $$

Where:

  • $f$ is Class#new
  • $A$ is the duck-type of the input argument
  • $B$ is the duck-type of the resulting object

Formal Definition of a Duck

A Duck is the methods we care about on some object.

Ok...

class F
  attr_reader :b

  def initialize(object)
    @b = object.a
  end
end

Objects from F have the duck b, and it's input argument has the duck a

So...

class G
  attr_reader :a

  def initialize(object)
    @a = object
  end
end

Objects from G have the duck a, and it's input argument doesn't have a duck

Will It Blend Compose?

>> F.new( G.new( 5 ) ).b
=> 5

Yeah!

I wrote the gem Beethoven to make this sort of thing easier

The following are equivalent:

F.new(G.new(5)).b
(F * G).new(5).b
(G | F).new(5).b
[G, F].reduce(&:|).new(5)

To Curry a Class

Single argument functions are boring...

Currying a function:

Go from:
lambda do |x, y|
  x + y
end.call(5, 6)
to
 lambda do |x|
  lambda { |y| x + y }
end.call(5).call(6)  

What is the Duck of an instance of Class?

Class#new

Return an object that responds to call new...

Add

class Add
  def initialize(x)
    @x = x
  end

  def new(y)
    @y = y; return self
  end

  def value
    @x + @y
  end
end
>> Add.new(5).new(6).value
=> 11  

Higher order functions?

class Map
  def initialize(func)
    @func = func
  end

  def new(object)
    @list = object.list; self
  end

  def list
    @list.map(&@func)
  end
end

Higher order functions?

class Filter
  def initialize(func)
    @func = func
  end

  def new(object)
    @list = object.list; self
  end

  def list
    @list.filter(&@func)
  end
end

Higher order functions?

class Reduce
  def initialize(func)
    @func = func
  end

  def new(object)
    @list = object.list; self
  end

  def value 
    @list.reduce(&@func)
  end
end

Verbosely...

List = Struct.new(:list)
list = List.new( (1..100).to_a )

Reduce.new(-> sum, next { sum + next }).new(
  Map.new(-> x { x + 10 }).new(
    Filter.new(-> x { x.even? }).new(list)
  )
).value
=> 3050
          

Better!

Pipeline = Filter.new(-> x { x.even? })
         | Map.new(-> x { x + 10 }) 
         | Reduce.new(-> sum, next { sum + next })

Pipeline.new(list).value
=> 3050
          

ooooooh

even    = -> x { x.even? }
add_ten = -> x { x + 10 }
add     = -> x, y { x + y }

Pipeline = Filter.new(even)
         | Map.new(add_ten) 
         | Reduce.new(add)

Pipeline.new(list).value
=> 3050
          

you can curry that

even = -> x { x.even? }
add  = -> x, y { x + y }.curry

Pipeline = Filter.new(even)
         | Map.new(add.call(10)) 
         | Reduce.new(add)

Pipeline.new(list).value
=> 3050

(•_•)

( •_•)>⌐■-■

(⌐■_■)

I'm Matt Parsons?

Ruby and JavaScript @(we're they're hiring!)

Now, Haskell @ Layer 3 Communications

Thanks! :D

questions?

@mattoflambda (Twitter)

@parsonsmatt (Github)