Last active
December 17, 2015 06:19
-
-
Save freakhill/5564328 to your computer and use it in GitHub Desktop.
having fun with "haskell like list comprehension in ruby" - https://gist.github.com/andkerosine/3356675
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby-head | |
class VeryBasicObject < BasicObject | |
undef :== | |
undef :!= | |
undef :! | |
end | |
class Captured < VeryBasicObject | |
def method_missing meth, *args | |
if meth == :< and ::Enumerator === args[0] # #is_a? not available | |
::Draw.new(@__name, args[0]) | |
elsif meth == :__ # escape mechanism to avoid globbing, kludge for quotes i guess | |
::Escaped.new(self) | |
else | |
::Message.new self, meth.inspect, args | |
end | |
end | |
end | |
class Escaped < VeryBasicObject # with an escaped value the following processing is delegated to #__id__ | |
attr_accessor :val | |
def initialize val | |
@val = val | |
end | |
def method_missing meth, *args | |
@val.__id__.send(meth, *args) | |
end | |
end | |
class Draw < Struct.new(:var, :from); end | |
class Message < Captured | |
attr_accessor :__self, :__method, :__args | |
def initialize s,m,a | |
@__self, @__method, @__args = s,m,a | |
end | |
end | |
class PrivateMessage < Captured | |
attr_accessor :__method, :__args | |
def initialize m,a | |
@__method, @__args = m,a | |
end | |
end | |
class Var < Captured | |
attr_accessor :__name | |
def initialize n | |
@__name = n | |
end | |
end | |
class Blank < VeryBasicObject | |
def method_missing var, *args | |
if args.empty? | |
::Var.new var | |
else # called a method from the surrounding bindings | |
# var is actually a method name there | |
::PrivateMessage.new var.inspect, args | |
end | |
end | |
end | |
def make_proc_from exp, freevars | |
r = proc do |exp| | |
case exp | |
when Var | |
exp.__name | |
when Escaped | |
r[exp.val] | |
when Message | |
"(#{r[exp.__self]}).send(#{[exp.__method, *exp.__args.map {|a| r[a]}].join ','})" | |
when PrivateMessage | |
"send(#{[exp.__method, *exp.__args.map {|a| r[a]}].join ','})" | |
when Enumerable | |
mapped = exp.map { |entry| r[entry] }.join ',' | |
case exp | |
when Hash | |
"Hash[[#{mapped}]]" | |
when Array | |
"[#{mapped}]" | |
else | |
"#{exp.class}.new([#{mapped}])" | |
end | |
else | |
exp.inspect | |
end | |
end | |
eval "proc { |#{freevars.join ','}| #{r[exp]} }" | |
end | |
def extract_var_draws_conds_from draws_and_conditions | |
vars, draws, conditions = [], [], [] | |
draws_and_conditions.each do |dc| | |
if Draw === dc | |
vars << dc.var | |
draws << dc.from.to_a # easy way to get product working... | |
else | |
conditions << dc | |
end | |
end | |
[ vars, draws, conditions ] | |
end | |
def lk &exp | |
exp, *draws_and_conditions = Blank.new.instance_eval(&exp) # mainly to isolate #method_missing | |
vars, draws, conditions = extract_var_draws_conds_from draws_and_conditions | |
exp, *conditions = [exp, *conditions].map {|ast| make_proc_from ast, vars} | |
[].tap { |res| draws.shift.product(*draws).each { |draw| res << exp[*draw] if conditions.all? {|c| c[*draw]} } } | |
end | |
module RefinedArray | |
refine Array do # syntactic sugar | |
def -@ | |
case map(&:class).index Range | |
when 0 then first.to_enum | |
when 1 then last.step(last.min.ord - first.ord) | |
else self.to_enum | |
end | |
end | |
end | |
end | |
using ::RefinedArray | |
z = 7 | |
def inc x | |
x + 1 | |
end | |
res = lk{[ | |
[ inc(x.to_i * y), {(x+y).__ => x+y, :z => z}, s % [x], rand(10) ], # expression | |
x <- [1..5], # draw with syntactic sugar | |
y < [2,3,4,5].to_enum, # raw draw | |
x + y > 4, # condition | |
x < 4, # condition | |
s <- [">>%s"] # draw | |
]} | |
# can't use "#{something}"... can't use if-like syntax etc. | |
# (x+y).__ will behave like a random integer exclusive to all other similar escaped expressions | |
# so be careful... it might clash with something else | |
puts res.inspect |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment