Skip to content

Instantly share code, notes, and snippets.

@autotelicum
Created August 1, 2011 15:18
Show Gist options
  • Save autotelicum/1118326 to your computer and use it in GitHub Desktop.
Save autotelicum/1118326 to your computer and use it in GitHub Desktop.
Partial application with free vars
#!/usr/local/bin/node /Users/eh/bin/coffee
# Adjust according to install and chmod +x script
# Compatibility with both node.js and browsers
show = if exports? then console.log else alert
# Step-by-step derivation of a short version of
# partial function application with free vars.
# Inspired by http://blog.mirotin.net/tag/coffeescript
# 15 - Fix indentation of i++
_ = {}
partial = () ->
[func, args...] = arguments
wrapper = () ->
i = 0
j = 0
res_args = []
while i < args.length
if args[i] == _
res_args.push arguments[j]
j++
else
res_args.push args[i]
i++
return func.apply null, res_args
# 12 - Use splats and remove redundant words
_ = {}
partial = (func, args...) ->
(moreargs...) ->
i = j = 0
res_args = []
while i < args.length
if args[i] == _
res_args.push moreargs[j++]
else
res_args.push args[i]
i++
func.apply null, res_args
# 10 - Use while as an expression
_ = {}
partial = (func, args...) ->
(moreargs...) ->
i = j = 0
func.apply null,
while i++ < args.length
if args[i-1] == _
moreargs[j++]
else
args[i-1]
# 5 - Use for loop and splat in call
_ = {}
partial = (func, a...) -> (b...) ->
i = 0
func (for arg in a
if arg == _ then b[i++] else arg)...
# 5 - Eliminate low level counter
_ = {}
partial = (func, a...) -> (b...) ->
b.reverse()
func (for arg in a
if arg == _ then b.pop() else arg)...
# 4 - Use undefined instead of empty object
_ = undefined
partial = (func, a...) -> (b...) ->
b.reverse()
func (for arg in a then arg ?= b.pop())...
# Make it 140 char twitter compatible with semicolons
#CoffeeScript partial with free vars
#_ = undefined; partial = (f, a...) -> (b...) -> b.reverse(); f (for arg in a then arg ?= b.pop())...
# Export module definitions
(exports ? this)._ = _
(exports ? this).partial = partial
# Command line adhoc test
if process.argv[1] == __filename
name = (require 'path').basename __filename, '.coffee'
f = (x, y, z) -> x + 2*y + 5*z
g = partial f, _, 1, _
show "#{name} => #{g 3, 5}" # partialFreeVars => 30
#!/usr/local/bin/node /Users/eh/bin/coffee
# Compatibility with both node.js and browsers
show = if exports? then console.log else alert
{_, partial} = require './partialFreeVars'
# Command line adhoc test
if process.argv[1] == __filename
name = (require 'path').basename __filename, '.coffee'
f = (x, y, z) -> x + 2*y + 5*z
g = partial f, _, 1, _
show "#{name} => #{g 3, 5}" # partialFreeVars => 30
# Modified from alexkg example
fold = (f, z, xs) ->
z = f(z, x) for x in xs
z
max = partial fold, Math.max, -Infinity, _
show max [-10..10]
# Without free vars
partial = (f, a...) -> (b...) -> f a..., b...
min = partial fold, Math.min, Infinity
show min [-10..10] # => -10
@autotelicum
Copy link
Author

Found this related implementation in JavaScript, that predates mirotin's:
http://javascriptweblog.wordpress.com/2010/05/17/partial-currys-flashy-cousin/

@autotelicum
Copy link
Author

From A Tour of Scala: Currying

object CurryTest extends Application {

  def filter(xs: List[Int], p: Int => Boolean): List[Int] =
    if (xs.isEmpty) xs
    else if (p(xs.head)) xs.head :: filter(xs.tail, p)
    else filter(xs.tail, p)

  def modN(n: Int)(x: Int) = ((x % n) == 0)

  val nums = List(1, 2, 3, 4, 5, 6, 7, 8)
  println(filter(nums, modN(2)))
  println(filter(nums, modN(3)))
}
// => List(2,4,6,8)
// => List(3,6)

In CoffeeScript becomes:

do curryTest = ->
  filter = (xs, p) -> x for x in xs when p x
  partial = (f, a...) -> (b...) -> f a..., b...
  modN = (n, x) -> x % n is 0

  nums = [1..8]
  console.log filter nums, partial modN, 2
  console.log filter nums, partial modN, 3
# => [ 2, 4, 6, 8 ]
# => [ 3, 6 ]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment