« first day (209 days earlier)      last day (2190 days later) » 

8:37 AM
The distinction between an expression and a value is clearer in logic and mathematics than it is in programming, and the meaning of "expression" in programming is very close to that in logic and mathematics. (It can be considered the same... in some languages.) See this article and this earlier conversation of ours.
Suppose Bob has a brother Carl and no other brothers. Then "Bob's brother" and "Carl" are expressions. They have the same value. That value is Carl, not "Carl". Carl is a person. "Carl" is a sequence of symbols.
Note that the conceptual distinction is unaffected by whether the things being talked about are real or fictional. It is no more necessary that I commit to the belief that Bob and Carl are actual people than that I commit to the belief that numbers like 2 and 3 are actual things. There are philosophical difficulties that arise when talking about things that might not exist, but they do not eradicate the distinction between expressions and values.
Most programming languages maintain this distinction, whether they are statically or dynamically typed, and whether they are strongly or weakly typed. This is a way that most programming languages are similar to natural language. (Though there are conventions, in natural language, where quoting is suppressed. In English, I say, "My name is Eliah," not, "My name is 'Eliah.'")
It is possible to have a programming language that does not distinguish between things and the expressions used to name them. Source code in such a language is effectively text on which transformation rules take place to turn it into other text, and Bash is such a language.
I am not aware of any programming languages that work this way besides shell scripting languages, though. (I don't know any Tcl, and I don't know enough about to know if this would be an accurate way to characterize it, but perhaps Tcl is another example.)
It is useful to have a name for the sort of expression that can't be reduced to anything simpler and contains no variables or operator symbols. Examples of such expressions include "5", "23", "π" (when used in the sense of the ratio of any circle's circumference to its diameter, and not as a variable), and "sin" (when used in the sense of the trigonometric sine function, and provided one's system lets one talk about functions in the same way that other things like numbers can be talked about).
Some people call those expressions "values." Some people call them "constants." Some people make other distinctions that cut across or otherwise differ from the definition I gave above for that sort of expression. (My definition is arguably not all that good anyway, for I certainly don't want to regard "sin π" as an expression of that sort, but the operator symbol for function evaluation is implicit.)
This is the sense in which one might say that an expression can be a value, or a values connected by operators, etc. This is not the same sense of "value" that I mean when I say that expressions have type in statically typed languages whilst values have type in dynamically typed languages.
The expression "5" is still not conceptually the same thing as the number 5, even though some people say that "'5' is a value" and mean that it is the sort of expression that uncomplicatedly designates a particular value.
So, when this article says, "An expression in a programming language is a combination of one or more explicit values, constants, variables, operators, and functions..." it is using the word "value" in the sense of an expression that doesn't have any variables in it and is as simple as possible.
It is not using the word "value" the way I typically use it (and the way I am using it when I say values, rather than expressions, are what have type in a dynamically typed language). However, "values" in that article is a hyperlink, and the other article it links to actually does define and (mostly) use "value" to mean what I mean by it.
Part of what I mean when I say "the distinction between an expression and a value is clearer in logic and mathematics than it is in programming" is that expressions in mathematics and logic are referentially transparent but this is only sometimes the case in programming. (Some functional programming languages, like Haskell, enforce referential transparency, and these are considered to be "pure functional" languages, but most do not.)
The other part of what I mean is that, in programming, when one refers to something as "an expression," one is often referring to and talking specifically about a particular occurrence of a sequence of symbols in source code. It is possible, of course, for x + y to occur and not be an expression, for example if it is in a string, but it can even be part of an expression but not a subexpression, e.g., "w / x + y" in most languages.
But it is also possible for "x + y" to occur separately as an expression in multiple places in the source code of the same program. When I say that expressions, rather than values, have type in a statically typed language, I don't mean that the separate occurrences of "x + y" necessarily have the same type.
That referential transparency cannot usually be assumed relates to a deep difference between logic and mathematics on the one hand and programming on the other. But using "expression" to mean a particular occurrence of an expression in programming might not, and I suspect it doesn't.
I have not thought deeply about that aspect of things, and I am not even sure that this is distinction outside the way that I personally write and talk. After all, clearly there are cases in math where one does use "expression" to mean an occurrence of an expression. For example, one might say, "You need not simplify that expression, because you're just going to multiply by zero in the next step anyway."
@Zanna No, duck typing (see also this article) is quite distinct both from parametric polymorphism (genericity) and from ad-hoc polymorphism (overloading). I can give an example of ad-hoc polymorphism, but I want to check first if this vast wall of text makes sense before adding a code sample in yet another language. :)
 
9:37 AM
I forgot to mention that the sort of expression some people call a "value," in programming, I call a literal. I believe that I am following the most common convention by doing so; that is, I think most programmers would regard expressions like "23" to be literals.
However, when talking about some programming languages, "literal" encompasses more than such simple expressions (see the JavaScript example in that article). As another example, it's common to refer to expressions like [1,2,3] in Python as literals, even though I think such an expression in that language is technically considered to be a "display."
In many programming languages, terms like "value," "constant," "literal," "object," "term," and "expression," have specific technical meanings officially specified for the language or deeply embedded in the culture surrounding the language. As a particular notorious example, Python and Ruby programmers use "object" to mean a particular idea, C and C++ programmers use it mean something else, and Java programmers mean yet another thing.
From a Python or Ruby perspective, it would be appropriate, and perhaps clearer than what I have been saying, to say something like, "An object has a type; an expression does not."
 
@EliahKagan OK, that makes sense. Maybe it helps me to think about the square root of 2 (I don't know an appropriate symbol I can write in place of the root sign here). I know the square root of 2 can be written approximately as a decimal, and in my (unmathematical) mind the never-ending decimal form is its "real" value, but that form is an inconvenient way of expressing it. So, thinking about surds, it's easier to feel the distinction between the Form of the number and its representation
@EliahKagan Hah yes, I knew that link was going to be to where you said that parameter expansion is like putting the actual dog into the sentence
(to paraphrase what you said in a really incomprehensible way
 
10:05 AM
@EliahKagan that makes sense
 
@Zanna As you probably expect me to say: the unending sequence of symbols that is the exact decimal representation of √2 isn't conceptually the same thing as √2 itself. I can't say for sure they're are not the same thing, any more than I can insist that the number 5 is not equal to some particular musk ox. But the idea of a sequence of symbols is quite different from the idea of a surd, in much the same way that the idea of an integer is different from the idea of an arctic-dwelling bovine.
There are reasons to construct numbers like √2 in terms of other simpler ideas, for example in a development of mathematics based entirely on set theory, but constructing them as the sequences of symbols that make up their canonical base-10 representations strikes me as inelegant. Note that I am not disagreeing with anything you have said, just commenting on it. (Though I am skeptical of the claim that your mind is unmathematical.)
 
10:27 AM
To illustrate how expressions have type in a statically typed language, consider this C++ code:
int multiply(int x, int y)
{
    return x * y;
}

double multiply(double x, double y)
{
    return x * y;
}
This shows the assembly language code corresponding to the machine code that one may get when compiling that program, for an x86-64 processor, by GCC 7.3. I've passed the -Os flag to the compiler to optimize for size, so the code is smaller. In this case that actually makes the assembly language representation considerably more readable than if compiled with no optimizations.
(A compiler can translate source code into assembly language, which directly represents machine language instructions, and which can be assembled into that machine code; in practice, compiler can also give you machine code, either directly, or by automatically running an assembler.)
The function that multiplies integers (type int) compiles to code that uses the imul instruction. The function that multiplies floating-point numbers (type double) compiles to code that uses the mulsd instruction. The bodies of both functions are the same, and the symbol * indicates multiplication in both cases, but * has a different meaning in each case. Its meaning is determined, statically, from the data type of its operands (the expressions x and y).
Thus the operation designated by x * y is different in each case, but for each occurrence of that expression, the type is fully determined and known at compile time. The compiler has to know it, in order to know what assembly language code or machine code to emit.
 
10:52 AM
@EliahKagan I agree that it is inelegant, and that's why I say that my mind is unmathematical, because I know the square root of 2 can't be described better than as that Number which when multiplied by itself makes 2, but in my mind it's 1.41421.... because I only know how to think about numbers in base 10.
In teacher training (for the first time) we were asked to grapple with the problem "what is energy?"
we were able to say that energy is that which is transferred when a force moves through a distance
and that's the most precise description of energy I know of... but the point was to realise that "energy" is used in physics as an accounting system, an abstraction, and it was very difficult to explain it to teenagers in a non-confusing way
The Institute of Physics advised teachers quite seriously to represent energy as an orange fluid filling up one type of store and being passed to another type of store...
so what I mean is that I can't deal with abstract concepts, but a surd has a shimmer about it, a sense of representing something difficult to describe, like energy
so I won't fall into the trap of thinking that "√2" is the same as √2
@EliahKagan what kind of thing is a double?
 
11:09 AM
@Zanna double is a floating point data type. It is common that double is implemented as IEEE 754 binary64. If not, usually it is still 64-bit, though not always. The name comes from how a double precision floating point number is twice as wide, bits, as a single precision floating point number.
 
oh I see :)
 
In much the same way that a double in C or C++ is commonly IEEE 754 binary64, a float in C or C++ is commonly IEEE 754 binary32. The C and C++ standards impose specific requirements on each of the fundamental data types, but considerable leeway is given, so that they they be implemented in ways that perform well or are otherwise suitable. In practice, float and double differ based on hardware.
This is true also of int, long, and long long, though the size of long often differs based on the OS (which is essentially cultural--long on x86_64 Linux and long on x86_64 Windows are different sizes, but that doesn't track any difference in computational capabilities). See this article in general, and that one for specific info on 64-bit ABIs.
 
11:31 AM
Unlike C++ (as in that example with int and double) which is a compiled language (C++ interpreter exist but are rare), Ruby is an interpreted language (the reference implementation, MRI, as well as other implementations that are sometimes used, like JRuby and IronRuby, are interpreters).
However, like in most interpreted languages including Perl and Python, Ruby source code does get compiled to a special bytecode format which is what actually gets interpreted when the program is running. This is the machine language of the Ruby virtual machine. I believe that it is allowed to differ across Ruby implementations.
In any case, there is a textual assembly language for it, and the libraries that come with Ruby include functionality to disassemble methods (all functions in Ruby are methods) and view this text. I can write a method in Ruby that just multiplies numbers, then disassemble it:
ek@Io:~$ irb
irb(main):001:0> def multiply(x, y)
irb(main):002:1>   x * y
irb(main):003:1> end
=> :multiply
irb(main):004:0> print RubyVM::InstructionSequence.disasm(method(:multiply))
== disasm: #<ISeq:multiply@(irb)>=======================================
local table (size: 3, argc: 2 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 3] x<Arg>     [ 2] y<Arg>
0000 trace            8                                               (   1)
0002 trace            1                                               (   2)
(See ruby-doc.org/core-2.2.3/RubyVM/… for more information on how that code works.)
So in high-level Ruby bytecode, opt_mult is used. (If you change it to x + y then you get opt_plus.) This behaves differently depending on the types of the operands, which are known only at runtime.
 
12:02 PM
@EliahKagan that's really clear
 
 
2 hours later…
2:05 PM
@Zanna So, that C++ code is also the example of ad-hoc polymorphism that I had said I was going to give. In it, two completely separate functions are defined, but they both have the same name. Which one is meant is determined by the number and type of arguments passed to it in a function call.
(But they have the same number of parameters, so they must each be passed two arguments when called, so in this case it is only the types of the arguments that are used to determine which of the two overloads gets called.)
So multiply(2, 3) would call multiply(int, int) while multiply(2.0, 3.0) would call multiply(double, double). This still works even when the expressions used for the arguments are arbitrarily complex, because C++ is a statically typed language, so every expression has a type that is known at compile time.
The code in the body of each function definition is the same in this case, but there is no requirement that separate overloads have copied code. And in fact, when the code for multiple functions is intended to be the same, often there are better implementation strategies in C++ for achieving that.
 
 
3 hours later…
4:51 PM
So... I could have foo play function with make a sandwich with the args and bar play function with make an origami sculpture with the args and that's fine because if play is called with foo type arguments it will call the sandwich-making function, and if it's called with bar type arguments it will call the origami-making function?
 
haha something like that
awesome ^__^
 
5:22 PM
When the implementation is written the same, but needs to be compiled into different code, as in the previous example, it's usually better to write a function template rather than two overloads.
template<typename T>
T multiply(T x, T y)
{
    return x * y;
}
It isn't technically a function. It is a function template, which will be instantiated at compile time into zero or more distinct functions. If it is never used then no code has to be generated. Anywhere it is used, a type argument for the type parameterT can be specified (e.g., multiply<int>(2, 3)), or, in the far more common case, the compiler infers it from the types of the arguments (e.g., multiply(2, 3)). Templates in C++ are very flexible but they exist as such only at compile-time.
So they work the same way as an "inline" generic function in F#, like that one.
(C++ also has a notion of "inline" and also has an inline keyword. But it does not have the same meaning as in F#. Though there is some overlap in the situations where one would use them.)
 
turkey is a nice bird.
 
what do you like about it?
I've never met one...
 
it never shits on you
 
XD
 
5:37 PM
One consequence of this is that if I write a library in C++ that contains a template with one or more type parameters, and I want you as the user of the library to be able to use that template with your own types that I don't have access to because, for example, maybe they don't even exist at the time that I write the template, then I will have to give you the source code of the template so your compiler can instantiate it with your types.
Also, your compiler will have to actually perform that compilation. Template-heavy code in C++ sometimes takes a while to compile. However, though this is less commonly done, you (or I) can force a template to be instantiated with specific types that do exist at the time we compile the code. This is achieved by declaring the whatever constructed types you want the compiler to instantiate from the template (I'll show the syntax below).
Suppose I do that; then you can write code that links to my compiled code and calls those functions (I have only introduced the idea of function templates, but there are other kinds of templates in C++, too; in particular, class templates are extremely widely used), and you do not have to have the source code of the template because the constructed types you need have already been instantiated and compiled.
Another use for this is to test a template you have written to make sure it can actually be instantiated. Of course this is not substitute for real unit tests though that actually test the functionality of the template (and which would also cause it to be instantiated, automatically.)
Sorry, I should've said "constructed functions," since the example I'm giving is of a function template, not of a class template.
Anyway, these declarations will cause the multiply function template to be instantiated for int and double as T. I hasten to reiterate that you usually do not need to do this; if you have the template definition then you can simply use it however you need to and your compiler will produce the necessary constructed functions.
template int multiply<int>(int, int);
template double multiply<double>(double, double);
The real main reason I am bringing this up is that it has an application that is useful in the specific context of this conversation: by forcing the template to be instantiated, we can cause the compiler to emit code for the functions without having to write other code, so you can get nice, clear, readable, short output from the compiler explorer that is easy to compare to what the version using function overloading produced.
 

« first day (209 days earlier)      last day (2190 days later) »