This is a syntax question. I am confused as to when the "return variable" in a Function is used as a return variable or as a method call. For instance, if I have:
Function foo() As Boolean
foo = True
foo = foo And bar
End Function
Does the second line in this function act as a recursive call to foo, or does it resolve to true from the previous assignment?
To get the value as of the last assignment:
foo = foo And bar
To make a recursive call:
foo = foo() And bar
The () makes all the difference. (BTW, this applies to VBA too.)
Related
For languages that allow for meta-programming is it possible to parse and evaluate a string locally? For example within a function? I'm trying to do this in Julia currently but also curious about the ability of other languages?
Is this what you mean?
function foo()
println("Calling foo")
end
function main(functionName)
functionCall = #eval $(Symbol(functionName))
functionCall()
end
Calling main with the function name "foo" string passed evaluates foo
julia> main("foo")
Calling foo
The Problem
I would like to check whether a function factory in R is "safe". Here "safe" means the results of functions created by the factory depend only on their arguments, not on Global Variables.
Description
This is an unsafe factory:
funfac_bad = function(){
newfun = function()
return(foo)
return(newfun)
}
The return value of newfun will depend on the value of foo at time of execution of newfun. It may even through an error if foo happens to be undefined.
Now - quite obviously - this factory can be made safe by binding foo to a value inside the factory
funfac_good = function(){
foo = 4711
newfun = function()
return(foo)
return(newfun)
}
I thought I could validate safety by checking for Global Variables in the factory. And indeed:
> codetools::findGlobals(funfac_bad)
[1] "{" "=" "foo" "return"
> codetools::findGlobals(funfac_good)
[1] "{" "=" "return"
But my actual use case is (much) more complicated. The functions of the factory depend on subfunctions and variables with hundreds of lines of code. Hence I sourced the definition and my factories in principle look like this:
funfac_my = function(){
sys.source("file_foo.R", envir = environment())
newfun = function()
return(foo)
return(newfun)
}
This is a safe factory if and only if code executed in "file_foo.R" binds the name "foo" to a value.
But (quite logically) codetools::findGlobals will always report "foo" as global variable.
Question
How can I detect unsafe behaviour of such a function factory when definitions are sourced?
Why not just ensure you define a default value for foo locally before sourcing the external files? For example, suppose I have this file:
foo.R
foo <- "file foo"
and this file
bar.R
bar <- "bar"
If I write my function factory like this:
funfac_my <- function(my_path) {
foo <- "fun fac foo"
if(!missing(my_path)) sys.source(my_path, envir = environment())
function() foo
}
Then I get the following results:
foo <- "global foo"
funfac_my("foo.R")()
#> [1] "file foo"
funfac_my("bar.R")()
#> [1] "fun fac foo"
funfac_my()()
#> [1] "fun fac foo"
So the output will simply never depend on whether there is an object in the global environment called "foo", (unless the scripts you are running maliciously look for a global called "foo" to copy - but then that would presumably be what you wanted by sourcing that file anyway)
Note that you could set this up to throw an error instead of returning a default value by adding the line if(foo == "fun fac foo") stop("object 'foo' not found") just before the final line. This will therefore complain that foo is not found even though you have a wrong object called foo in the global workspace.
You ask "How can I detect unsafe behaviour of such a function factory when definitions are sourced?" I think the answer is that you can't, but changing it slightly would make it easy.
For example, suppose currently you have
foo <- undefined_value
as the only line in "file_foo.R", and you want to be warned about the use of undefined_value. My suggestion is that you don't do that. Instead, put the whole definition of funfac_my into "file_foo.R", wrapping that one line:
funfac_my = function(){
foo <- undefined_value
newfun = function()
return(foo)
return(newfun)
}
Now you can source that file, and have a function funfac_my to pass to codetools::findGlobals:
codetools::findGlobals(funfac_my)
#> [1] "{" "<-" "=" "return"
#> [5] "undefined_value"
I'd like to start that I've not been able to recreate my problem in a stripped down version of the code. The code below works as intended, so this post is perhaps not well posed. The extended code, which is too long to post here, fails. I'll describe what I'm trying to do as maybe it'll help someone else out there.
I create three types: Bar, Baz, and Qux, which contains the method foo on Bar and Baz. I create a qux and query it's foo
qux = Wubble.Qux()
qux.foo
I get the following two methods, as expected:
foo(bar::Bar)
foo(baz::Baz)
Then when I try to actually use qux.foo with a bar or a baz, it gives me an error 'foo' has no method matching foo(::Bar).
Sadly, I can't recreate this error with stripped down code and the real code is unattractively long. What are the various ways of getting this error in this scenario that I've missed? It may be related to method extension and function shadowing like in this post, but I couldn't work a fix.
module Wibble
type Bar
data::Number
function Bar(num::Number=0)
this = new(num)
return this
end
end
end
module Wobble
type Baz
data::String
function Baz(vec::String="a")
this = new(vec)
return this
end
end
end
module Wubble
using Wibble
using Wobble
typealias Bar Wibble.Bar
typealias Baz Wobble.Baz
type Qux
foo::Function
function Methods(this::Qux)
function foo(bar::Bar)
println("foo with bar says ", bar.data)
end
function foo(baz::Baz)
println("foo with baz says ", baz.data)
end
this.foo = foo
return this
end
function Qux()
this = new()
this = Methods(this)
return this
end
end
end
I'm not really sure what's going wrong, but a couple of points which might help
You almost never want to have Function field in a type: this is a common idiomatic mistake made by people coming from "dot-based" OOP languages. Julia methods are generic, and don't belong to a particular type. There is no advantage to doing this, and not only is it more confusing (you have a lot of nested levels to write something that could be written in 2 lines), but it can make it harder for the compiler to reason about types, impacting performance.
You should use import Wibble.Bar instead of a typealias. If you use this, you don't need using.
Outer constructors are easier to use for specifying default arguments.
So in short, my version would be:
module Wibble
type Bar
data::Number
end
Bar() = Bar(0)
end
module Wobble
type Baz
data::String
end
Baz() = Baz("a")
end
module Wubble
import Wibble.Bar
import Wobble.Baz
qux(bar::Bar) = println("foo with bar says ", bar.data)
qux(baz::Baz) = println("foo with baz says ", baz.data)
end
Is there any introspective magic that would give me a list of functions defined in a module?
module Foo
function foo()
"foo"
end
function bar()
"bar"
end
end
Some mythical function like:
functions_in(Foo)
Which would return: [foo,bar]
The problem here is that both names and whos list exported names from a module. If you wanted to see them then you would need to do something like this:
module Foo
export foo, bar
function foo()
"foo"
end
function bar()
"bar"
end
end # module
At this point both names and whos would list everything.
If you happen to be working at the REPL and for whatever reason did not want to export any names, you could inspect the contents of a module interactively by typing Foo.[TAB]. See an example from this session:
julia> module Foo
function foo()
"foo"
end
function bar()
"bar"
end
end
julia> using Foo
julia> whos(Foo)
Foo Module
julia> names(Foo)
1-element Array{Symbol,1}:
:Foo
julia> Foo.
bar eval foo
Somehow the tab completion is looking up un-exported names, so there must be a way to get Julia to tell them to you. I just don't know what that function is.
EDIT
I did a little digging. The un-exported function Base.REPLCompletions.completions seems to work, as demonstrated in a continuation of the REPL session we were previously using:
julia> function functions_in(m::Module)
s = string(m)
out = Base.REPLCompletions.completions(s * ".", length(s)+1)
# every module has a function named `eval` that is not defined by
# the user. Let's filter that out
return filter(x-> x != "eval", out[1])
end
functions_in (generic function with 1 method)
julia> whos(Foo)
Foo Module
julia> names(Foo)
1-element Array{Symbol,1}:
:Foo
julia> functions_in(Foo)
2-element Array{UTF8String,1}:
"bar"
"foo"
Suppose I have the following type:
type Foo
a::Int64
b::Int64
end
I can instantiate this with
bar = Foo(1,2)
Is there a way to use keywords here, because in the above I have to remember that a is first, and b is second. Something like this:
bar = Foo(a=1, b=2)
Edit:
The solution by spencerlyon2 doesn't work if called from the function:
#!/usr/bin/env julia
type Foo
a::Float64
b::Float64
end
function main()
Foo(;a=1, b=2.0) = Foo(a,b)
bar = Foo(a=1, b=2.0)
println(bar.a)
end
main()
Why? Is there a workaround?
Edit 2:
Doesn't work from inside a function:
#!/usr/bin/env julia
type Foo
a::Int64
b::Int64
end
function main()
Foo(;a=1, b=2) = Foo(a,b)
bar = Foo(a=1, b=2)
println(bar.a)
end
main()
but if take it out of the function -- it works:
#!/usr/bin/env julia
type Foo
a::Int64
b::Int64
end
# function main()
Foo(;a=1, b=2) = Foo(a,b)
bar = Foo(a=1, b=2)
println(bar.a)
# end
# main()
Yep, but you will need default values for the arguments:
julia> type Foo
a::Int64
b::Int64
end
julia> Foo(;a=1, b=2) = Foo(a, b)
Foo
julia> Foo(b=10)
Foo(1,10)
julia> Foo(a=40)
Foo(40,2)
julia> Foo(a=100, b=200)
Foo(100,200)
Edit
Let's break down the syntax Foo(;a=1, b=1) = Foo(a, b).
First, defining a function with the same name as a type defines a new constructor for that type. This means we are defining another function that will create objects of type Foo. There is a whole chapter on constructors in the manual, so if that term is unfamiliar to you you should read up on them.
Second, Julia distinguishes between positional and keyword arguments. Positional arguments are the default in Julia. With positional arguments names are assigned to function arguments based on the order in which the arguments were defined and then passed into the function. For example if I define a function f(a, b) = .... I know that the first argument I pass to f will be referred to as a within the body of the function (no matter what the name of the variable is in the calling scope).
Keyword arguments are treated differently in Julia. You give a function's keyword arguments non-default values using the syntax argument=value when calling the function. In Julia you tell the compiler that certain arguments are to be keyword arguments by separating them from the standard positional arguments with a semicolon (;) and giving them default values. For example, if we define g(a; b=4) = ... we can give a a value by making it the first thing passed to g and b a value by saying b=something. If we wanted to call the g function with arguments a=4, b=5 we would write g(4; b=5) (note the ; here can be replaced by a ,, but I have found it helps me remember that b is a keyword argument if I use a ; instead).
With that out of the way, we can finally understand the syntax above:
Foo(;a=1, b=2) = Foo(a, b)
This creates a new constructor with zero positional arguments and two keyword arguments: a and b, where a is given a default value of 1 and b defaults to 2. The right hand side of that function declaration simply takes the a and the b and passes them in order to the default inner constructor (that was defined automatically for us when we declared the type) Foo.
EDIT 2
I figured out the problem you were having when defining a new outer constructor inside a function.
The lines
function main()
Foo(;a=1, b=2.0) = Foo(a,b)
actually create a completely new function Foo that is local to the main function. So, the left hand side creates a new local Foo and the the right hand side tries to call that new local Foo. The problem is that there is not a method defined for the local Foo that takes two positional Int64 arguments.
If you really want to do this you need to tell the main function to add a method to the Foo outer function, by specifying that Foo belongs to the global scope. This works:
function main()
global Foo
Foo(;a=1, b=2.0) = Foo(a,b)
bar = Foo(a=1, b=2.0)
println(bar.a)
end
About using inner constructors. Sure you can do this, but you will also want to define a default inner constructor. This is because if you do not define any new inner constructors, Julia generates a default one for you. If you do decide to create one of your own, then you must create the default constructor by hand if you want to have it. The syntax for doing this is
type Foo
a::Int64
b::Int64
# Default constructor
Foo(a::Int64, b::Int64) = new(a, b)
# our new keyword constructor
Foo(;a::Int64=1, b::Int64=2) = new (a, b)
end
I should note that for this particular use case you almost certainly do not want to define the keyword version as an inner constructor, but rather as an outer constructor like I did at the beginning of my answer. It is convention in Julia to use the minimum number of inner constructors as possible -- using them only in cases where you need to ensure invariant relationships between fields or partially initialize an object.