Dean Radcliffe
Dean Radcliffe

Reputation: 2206

How can you determine if a proc call is still free or bound?

I'd like to see if a proc call with the same arguments will give the same results every time. pureproc called with arguments is free, so every time I call pureproc(1,1), I'll get the same result. dirtyproc called with arguments is bound within its environment, and thus even though it has the same arity as pureproc, its output will depend on the environment.

ruby-1.9.2-p136 :001 > envx = 1
=> 1 
ruby-1.9.2-p136 :003 > pureproc = Proc.new{ |a,b| a+b }
 => # 
ruby-1.9.2-p136 :004 > dirtyproc = Proc.new{ |a,b| a+b+envx }

How can I programmatically determine whether a called proc or a method is free, as defined by only being bound over the variables that must be passed in? Any explanation of bindings, local variables, etc would be welcome as well.

Upvotes: 1

Views: 103

Answers (2)

sawa
sawa

Reputation: 168199

Probably you can parse the source using some gem like sourcify, take out all the tokens, and check if there is anything that is a variable. But note that this is a different concept from the value of the proc/method call being constant. For example, if you had things like Time.now or Random.new in your code, that does not require any variable to be defined, but will still vary every time you call. Also, what would you want to be the case when a proc has envx - envx? That will remain constant, but will still affect the code in the sense that it will return an error unless envx is defined.

Upvotes: 1

Michael Kohl
Michael Kohl

Reputation: 66837

Hm, tricky. There's the parameters method that tells you about expected arguments (note how they are optional cause you are using a procs, not lambdas).

pureproc.parameters
=> [[:opt, :a], [:opt, :b]] 
dirtyproc.parameters
=> [[:opt, :a], [:opt, :b]]

As for determining whether or not one of the closed over variables are actually used to compute the return value of the proc, walking the AST comes to mind (there are gems for that), but seems cumbersome. My first idea was something like dirtyproc.instance_eval { local_variables }, but since both closures close over the same environment, that obviously doesn't get you very far...

The overall question though is: if you want to make sure something is pure, why not make it a proper method where you don't close over the environment in the first place?

Upvotes: 0

Related Questions