#!/usr/bin/env ruby # by "lens" I mean "traversal" # TODO: lenses should be first-class, not this nonsense class Lensed def initialize(obj, *lls) @obj = obj @lls = lls end # ion claims this is supposed to work; I claim it couldn't possibly work #include Enumerable #def each(&b) # go = lambda {|lls| lambda {|obj| # if lls.empty? # b[obj] # nil # else # one(obj, lls[0], &go[lls[1..-1]]) # end # } } # go[@lls][@obj] #end def map(&b) # traverseOf/mapMOf go = lambda {|lls| lambda {|obj| if lls.empty? b[obj] else one(obj, lls[0], &go[lls[1..-1]]) # yes, it's easy to improve this by passing an index end } } go[@lls][@obj] end # fold functions def each(&b); map {|x| b[x]; nil }; nil; end # traverseOf_ def reduce(acc, &b); each {|x| acc = b[acc, x] }; acc; end def length; reduce(0) {|x, y| x + 1 }; end def sum; reduce(0) {|x, y| x + y }; end def to_a; acc = []; each {|x| acc << x }; acc; end # setter functions # map (over) def +(n); map {|x| x + n }; end def -(n); map {|x| x - n }; end def *(n); map {|x| x * n }; end def /(n); map {|x| x / n }; end def ^(n); map {|x| x ^ n }; end private def one(obj, ll, &b) case ll when Symbol obj.public_send(ll, &b) when Proc ll.call(obj, &b) else p ll raise "oh no!" end end end class Object def lens(*a) Lensed.new(self, *a) end end # useful lenses _head = proc {|x, &f| if x.empty? x else [f[x[0]]] + x[1..-1] end } # folds/getters only! def to(&b) proc {|x, &f| f[b[x]] } end _str = proc {|x, &f| x.chars.map(&f).join # this ought to be polymorphic but it's not } # demonstration nested = [[1,2,3],[4,5,6],[],[7,8,9]] p nested.lens(:map, :map).to_a p nested.lens(:map, :map).map {|x| -x } p nested.lens(:map, :map) + 5 p nested.lens(:map, _head).to_a p nested.lens(:map, _head) + 1 p nested.lens(:map, :map).length __END__ shachaf@carbon:~/toys/lensrb$ ./lens.rb [1, 2, 3, 4, 5, 6, 7, 8, 9] [[-1, -2, -3], [-4, -5, -6], [], [-7, -8, -9]] [[6, 7, 8], [9, 10, 11], [], [12, 13, 14]] [1, 4, 7] [[2, 2, 3], [5, 5, 6], [], [8, 8, 9]] 9