Consider the following Ocaml code:
let app f y = let x = 4 in (f y)+1 ;;
let proc x = let change z = z-x in app change (x+3) ;;
(proc 2)
From what I understand, static scoping uses the structure of the code to determine the value of the variables, so in this case, x is replaced with 2 both in change and in (x+3). However, what I don't understand is why dynamic scoping only replaces the x in change with 4 but not the x in (x+3) with 4.
This is a bit confusing, I'm wondering if there's any trick to how to think through these problems.
A free variable is a variable that's used in a function but isn't defined in the function. In your example code, the only free variable is the variable x of the function change:
let change z = z - x
The function change uses x but doesn't define it.
The essence of scoping is to determine what variable definition a free variable is referring to, what its referent is. In your example code, it comes down to determining the referent of the free variable x in the change function.
For static scoping, there is a single static referent for every free variable. The referent is determined by looking outward in the text of the program through statically containing blocks of code until a binding (a definition) is found. In OCaml, variable bindings are introduced by function definitions and by let. So you're looking for the nearest enclosing block of change that binds x. The nearest binding of x is the function parameter x in let proc x =. For the example call, it has the value 2.
For dynamic scoping, the referent is determined by looking up through the nested function calls that are active at the time the value is required. In other words, you want to find the innermost function in the call chain that defines a variable named x. If you pretend that OCaml has dynamic scoping (which it most definitely does not), the call chain looks like this:
proc => app => change
The function just outside change in the call chain is app, and it defines a variable named x. So for the example code, the free variable x of change refers to the variable x defined by app. In the example, it has the value 4.
The x in x + 3 is not a free variable. It's defined by proc and is used in proc. For the example call it has the value 2 no matter what scoping is used.
For what it's worth I don't think it's particularly useful to think of replacing variables with values. It's better to think of them as being bound to values.
I'd also like to say (though I probably shouldn't) that dynamic scoping is insane. The extra flexibility is definitely not worth the extra complexity in figuring out what's going on. It shouldn't be necessary to trace through chains of function calls to determine the binding of a variable. (In my opinion.)
Related
I was reading about Haskell and it stated that once a variable is bound to an expression it cannot be rebound for example
x = 10
x = 11
Assign.hs:2:1: error:
Multiple declarations of ‘x’
Declared at: Assign.hs:1:1
Assign.hs:2:1
So if this is the case... how is recursion working where the same variable keeps getting bound to other things? For example
drop n xs = if n <= 0 || null xs
then xs
else drop (n-1) (tail xs)
the variable n... is being bound again and again every time the function recurses. Why is this allowed?
Also if you guys could tell me some key words to search for so I can learn more about this myself I'd highly appreciate it.
Haskell has lexical scoping. When you declare a variable inside a scope, it hides any variable with the same name from an outer scope. For example, in the following program,
x :: Int
x = 5
main :: IO ()
main = do
x <- readLn :: IO Int
print x
If you compile with ghc -Wall, it will compile correctly, but give you the following warnings:
sx-scope.hs:2:1: warning: [-Wunused-top-binds]
Defined but not used: ‘x’
sx-scope.hs:6:10: warning: [-Wname-shadowing]
This binding for ‘x’ shadows the existing binding
defined at sx-scope.hs:2:1
So the x outside main and the x in main are different variables, and the one in the inner scope temporarily hides the one in the outer scope. (Okay, “temporarily” in this example means until main returns, which is as long as the program is running, but you get the concept.)
Similarly, when you declare drop n xs or \n xs ->, n and xs are local variables that only exist during that invocation of the function. It can be called more than once with different values of n and xs.
When a function is tail-recursive, that is, returns a call to itself, the compiler knows it is about to replace the old parameters, which were from a scope that no longer exists, with updated parameters of the same type. So it can re-use the stack frame and store the new parameters in the same locations as the previous ones. The resulting code can be as fast as iteration in a procedural language.
In almost any language, identifiers are only meaningful because they exist in a scope; conceptually a sort of implicit map from names to the things they denote.
In modern languages you usually have (at least) a global scope, and each local scopes associated with each function/procedure. Pseudocode example:
x = 1
print x
x = 2
print x
function plus_one(a):
b = 1
return a + b
print plus_one(x)
x is a name in the global scope, a and b are names in the local scope of the function plus_one.
Imperative languages (and non-pure declarative languages) are generally understood by thinking of those names as mapped to a sort of slot or pigeon hole, into which things can be stored by assignment, and the current connects referred to by using the name. This works only because an imperative step-by-step way of thinking about these programs gives us a way of understanding what "current" means.1 The above example shows this; x is first assigned 1, then printed, then assigned 2, then printed again; we'd expect this to print "1" and then print "2" (and then print "3" from the last line).
Given that understanding of variables as slots that can store things, it's easy to fall into the trap of thinking of the local variables that represent a function's arguments as just slots that get filled when you call the function. I say trap because this is not a helpful way of thinking about function arguments and calls, even in imperative languages. It more or less works as long as each function only ever has one call "in flight" at once, but introducing one of any number of common programming features breaks this model (recursion, concurrency, laziness, closures, etc). That's the misunderstanding at the heart of a number of questions I've seen here on SO, where the poster was having trouble understanding recursive calls, or wanted to know how to access the local variables of a function from outside, or etc.
You should actually think of a function as having a separate scope associated with each call of that function2. The function itself is kind of like a template for a scope, rather than a scope itself (although common language does usually talk about "the scope of a function" as shorthand). If you provided bindings for the function's parameters then you can produce a scope, but a typical function is called many times with different parameters, so there isn't just one scope for that function.
Consider my pseudocode plus_one, with argument a. You could imagine that a is a local name for a variable, and the call plus_one(x) just assigns the contents of x into the slot for a and then starts executing the code of plus_one. But I contend that it's better to think that when you call plus_one on x you're creating a new scope, in which there is a variable called a (containing with the contents of the global scope x at this point), but it is not "the" a variable.
This is vitally important for understanding recursion, even in imperative languages:
function drop(n, xs):
if n <= 0 || null(xs):
return xs
else:
return drop(n - 1, tail(xs))
Here we could try to imagine that there's only one xs variable, and when we make the recursive call to drop we're assigning the tail of the original xs to the xs variable and starting the function's code again. But that falls down as soon as we change it to something like:
function drop(n, xs):
if n <= 0 || null(xs):
return xs
else:
result = drop(n - 1, tail(xs))
print xs
return result
Now we're using xs after the recursive call. What this does is hard to explain if we're imagining that there's only one xs, but trivial if we think of there being a separate xs in a separate scope every time we call drop. When we make the recursive call to drop (passing it n - 1 and tail(xs)) it creates its own separate xs, so it's entirely unmysterious that print xs in this scope still can access xs.
So, is this story different with Haskell? It's true that the nature of variables is quite different in Haskell than from typical recursive languages. Instead of scopes mapping names to slots in which we can place different contents at different times, scopes map names directly to values, and there's no notion of time in which we could say an identifier "was" bound to one value (or was not bound to anything) and "now" is bound to a different value. x = 1 at global scope in Haskell isn't a step that "happens" and changes something, it's just a fact. x just is 1. Haskell isn't throwing an error at your x = 10 and x = 11 lines because the designers wanted to restrict you to only assigning a variable once, rather Haskell does not have a concept of assignment at all. x = 10 is giving a definition for x in that scope, and you can't have two separate definitions for the same name in the same scope.
The local scopes that come from functions in Haskell are the same; each name just is associated with a particular value3. But the actual values are parameterised on the values on which you call the function (they are quite literally a function of those values); each call has its own separate scope, with a different mapping from names to values. It's not that at each call n "changes" to become bound to the new argument, just that each call has a different n.
So recursion affects variable binding in Haskell in pretty much the same way it does in all major imperative languages. What's more, the extremely flippant way to describe how recursion affects name binding in all of these languages is to say "it doesn't". Recursion actually isn't special at all in this way; parameter passing, local scopes, etc, works exactly the same way when you call a recursive function as when you call an "ordinary" non-recursive functions, provided you understand the local scopes as being associated with each call of the function, not as a single thing associated with the function.
1 Sometimes even the scope mapping itself is mutable, and entries mapping names to slots can be added and removed as steps of the program. Python, for example, has a mutable global scope (in each module); you can add and remove module global variables dynamically, even with names determined from runtime data. But it uses immutable local scopes: a function's local variables exist even before they've been assigned a value (or after that value has been removed with del).
2 At least, this is how functions/procedures very common work in modern languages. It's not totally universal, but it's generally acknowledged to be a good way for functions/procedures to work.
3 Of course, thanks to laziness the particular value might be the bottom value, which is the "value" we pretend is the value of infinite loops and other forms of nontermination, so that we can interpret expressions as always having a value.
From https://en.wikibooks.org/wiki/Haskell/Variables_and_functions,
Within a given scope, a variable in Haskell gets defined only once and cannot change.
but scope isn't just the code of the function. Roughly, it's the code of function + its context, i.e. the arguments it's been called with, and anything from its outer scope. This is often referred to as a closure. Every time you call a function, the code runs under a new closure, and so the variables can evaluate to different things.
The recursive case isn't anything special with regards to this: a function being called twice by other code would run with a different closure, and its internal variables can evaluate to different things.
This question already has an answer here:
Julia: invoke a function by a given string
(1 answer)
Closed 6 years ago.
I know that you can call functions using their name as follows
f = x -> println(x)
y = :f
eval(:($y("hi")))
but this is slow since it is using eval is it possible to do this in a different way? I know it's easy to go the other direction by just doing symbol(f).
What are you trying to accomplish? Needing to eval a symbol sounds like a solution in search of a problem. In particular, you can just pass around the original function, thereby avoiding issues with needing to track the scope of f (or, since f is just an ordinary variable in your example, the possibility that it would get reassigned), and with fewer characters to type:
f = x -> println(x)
g = f
g("hi")
I know it's easy to go the other direction by just doing symbol(f).
This is misleading, since it's not actually going to give you back f (that transform would be non-unique). But it instead gives you the string representation for the function (which might happen to be f, sometimes). It is simply equivalent to calling Symbol(string(f)), since the combination is common enough to be useful for other purposes.
Actually I have found use for the above scenario. I am working on a simple form compiler allowing for the convenient definition of variational problems as encountered in e.g. finite element analysis.
I am relying on the Julia parser to do an initial analysis of the syntax. The equations entered are valid Julia syntax, but will trigger errors on execution because some of the symbols or methods are not available at the point of the problem definition.
So what I do is roughly this:
I have a type that can hold my problem description:
type Cmd f; a; b; end
I have defined a macro so that I have access to the problem description AST. I travers this expression and create a Cmd object from its elements (this is not completely unlike the strategy behind the #mat macro in MATLAB.jl):
macro m(xp)
c = Cmd(xp.args[1], xp.args[3], xp.args[2])
:($c)
end
At a later step, I run the Cmd. Evaluation of the symbols happens only at this stage (yes, I need to be careful of the evaluation context):
function run(c::Cmd)
xp = Expr(:call, c.f, c.a, c.b)
eval(xp)
end
Usage example:
c = #m a^b
...
a, b = 2, 3
run(c)
which returns 9. So in short, the question is relevant in at least some meta-programming scenarios. In my case I have to admit I couldn't care less about performance as all of this is mere preprocessing and syntactic sugar.
Pass-by-value semantics are easy to implement in an interpreter (for, say, your run-of-the-mill imperative language). For each scope, we maintain an environment that maps identifiers to their values. Processing a function call involves creating a new environment and populating it with copies of the arguments.
This won't work if we allow arguments that are passed by reference. How is this case typically handled?
First, your interpreter must check that the argument is something that can be passed by reference – that the argument is something that is legal in the left-hand side of an assignment statement. For example, if f has a single pass-by-reference parameter, f(x) is okay (since x := y makes sense) but f(1+1) is not (1+1 := y makes no sense). Typical qualifying arguments are variables and variable-like constructs like array indexing (if a is an array for which 5 is a legal index, f(a[5]) is okay, since a[5] = y makes sense).
If the argument passes that check, it will be possible for your interpreter to determine while processing the function call which precise memory location it refers to. When you construct the new environment, you put a reference to that memory location as the value of the pass-by-reference parameter. What that reference concretely looks like depends on the design of your interpreter, particularly on how you represent variables: you could simply use a pointer if your implementation language supports it, but it can be more complex if your design calls for it (the important thing is that the reference must make it possible for you to retrieve and modify the value contained in the memory location being referred to).
while your interpreter is interpreting the body of a function, it may have to treat pass-by-referece parameters specially, since the enviroment does not contain a proper value for it, just a reference. Your interpreter must recognize this and go look what the reference points to. For example, if x is a local variable and y is a pass-by-reference parameter, computing x+1 and y+1 may (depending on the details of your interpreter) work differently: in the former, you just look up the value of x, and then add one to it; in the latter, you must look up the reference that y happens to be bound to in the environment and go look what value is stored in the variable on the far side of the reference, and then you add one to it. Similarly, x = 1 and y = 1 are likely to work differently: the former just goes to modify the value of x, while the latter must first see where the reference points to and modify whatever variable or variable-like thing (such as an array element) it finds there.
You could simplify this by having all variables in the environment be bound to references instead of values; then looking up the value of a variable is the same process as looking up the value of a pass-by-reference parameter. However, this creates other issues, and it depends on your interpreter design and on the details of the language whether that's worth the hassle.
Quote from here: http://www.haskell.org/haskellwiki/Global_variables
If you have a global environment,
which various functions read from (and
you might, for example, initialise
from a configuration file) then you
should thread that as a parameter to
your functions (after having, very
likely, set it up in your 'main'
action). If the explicit parameter
passing annoys you, then you can
'hide' it with a Monad.
Now I'm writing something that needs access to configuration parameters and I wonder if someone could point me to a tutorial or any other resource that describes how monads can be used for this purpose. Sorry if this question is stupid, I'm just starting to grok monads. Reading Mike Vainer's tutorial on them now.
The basic idea is that you write code like this:
main = do
parameters <- readConfigurationParametersSomehow
forever $ do
myData <- readUserInput
putStrLn $ bigComplicatedFunction myData parameters
bigComplicatedFunction d params = someFunction params x y z
where x = function1 params d
y = function2 params x d
z = function3 params y
You read the parameters in the "main" function with an IO action, and then pass those parameters to your worker function(s) as an extra argument.
The trouble with this style is that the parameter block has to be passed down to every little function that needs to access it. This is a nuisance. You find that some function ten levels down in the call tree now needs some run-time parameter, and you have to add that run-time parameter as an argument to all the functions in between. This is known as tramp data.
The monad "solution" is to embed the run-time parameter in the Reader Monad, and make all your functions into monadic actions. This gets rid of the explicit tramp data parameter, but replaces it with a monadic type, and under the hood this monad is actually doing the data tramping for you.
The imperative world solves this problem with a global variable. In Haskell you can sort-of do the same thing like this:
parameters = unsafePerformIO readConfigurationParametersSomehow
The first time you use "parameters" the "readConfigurationParametersSomehow" gets executed, and from then on it behaves like a constant value, at least as long as your program is running. This is one of the few righteous uses for unsafePerformIO.
However if you find yourself needing such a solution then you really need to have a think about your design. Odds are you are not thinking hard enough about generalising your functions lower down; if some previously pure function suddenly needs a run-time parameter then look at the reason and see if you can exploit higher order functions in some way. For instance:
Pass down a function built using the parameter rather than the parameter itself.
Have the worker function at the bottom return a function as a result, which gets
passed up to be composed with a parameter-based function at the higher level.
Refactor your call stack so that fundamental operations are done by lower level
primitives at the bottom which are composed in a parameter-dependent way at the top.
Either way is going to involve
I am currently studying the small-step semantics using Context- Environment machine for lambda calculus.
In this kind of machine, or say interpreter, the definition of the closure is open lambda terms paired with an environment component that defines the meaning of free variables in the closure.
When defining the environment component, the literature says:
ρ ∈ Env = Var ⇀ Clo.
which is to map an variable to a closure.
My question is:
Why closure? It is not straightforward to understand.
For example, you can imagine:
According to the definition of closure, every expression has its environment, and thus a closure, then if the current expression to evaluate is a variable v, then we can reference to its environment for v, which will return a closure? What's that? If the variable's value is 5, why not just give me 5, rather than a closure?
Those examples are often defined in the context of λ-calculus without
constants :
terms ::=
| x variable
| t₁ t₂ application
| λx.t abstraction
In this case, only abstractions are values : the only values (normal
forms of a closed terms) are of the form λx.t; indeed, x is not
a closed term and t₁ t₂ can be further reduced.
When using an (t,ρ) term-environment pair (the idea is that ρ
keeps definitions for free variables of t, instead of substituting
them away which is a costly operation), the values in ρ may have free
variables themselves, and therefore need to carry their own
environment : environment should be Var → (Value * Env). As in this
restricted example, the only possible values are abstractions, we name
a pair of a lambda and its environment a "closure", hence Var → Clo.
Now you may want to add other things to your language, such as
constants (5, true, etc.), pairs, let-definitions, continuations,
etc. The definition of terms will be extended in each case, and the
definition of values may also change, for example 2 + 3 won't be
a value but 5 will. Value may or may not capture variables from the
environment : (λf.f x) does, but 5 doesn't. However, we keep the
uniform definition of Env := Var → (Value * Env), so that we don't
have to distinguish between those.
You don't actually need the captured environment to be exactly the
same as the environment you had at the time of the value
construction. You only need to keep bindings for the value that are
actually captured in the value, such as x in (λf. f x) (this is
called "weakening"). In particular, you can always express the
"closure" for 5 as (5, ∅).