25
$\begingroup$

I typically have the same students two semesters in a row, for programming I (VB.NET) and programming II (C#). In our early, impressionable time together, I stress to these students (high-schoolers) and show them by example (I write lots of code for them) the importance of writing clear, readable code, which is partly achieved by explicitly specifying types for all variables (at first I do not tell them there are other options)

However, at some point, I have to reveal to my students that they can, indeed, declare a variable as an Object (i.e. Object x = new Horse("Silver");) and that it can hold anything; or, that they can declare a variable as var (i.e. var y = "foo";) and that the compiler will figure out what type it is. Some of them discover this on their own, of course.

Then I inevitably have students who abandon the good habits they had formed and want to make everything an Object, or declare everything as var (note I realize this is not equivalent in the language, but students tend to abuse the usage in the same way). I am always torn about how insistent to be that they revert, because I know the use of var in C# is controversial in the industry. (See the SO discussion use of var in C# for an example). Plus, the more savvy students are hanging out in SO anyway, and they see some questions and answers that lack explicit typing and other good programming habits.

My question is, since these students are still learning, and since it is my classroom after all, should I emphatically require that they always explicitly specify types unless absolutely necessary? (Of course, sometimes it is justifiable, preferred, and necessary NOT to). And, if I do require it, how can I explain the (in my opinion) sloppiness they sometimes see in online communities?

$\endgroup$
  • 3
    $\begingroup$ Code on SO is not necessarily "sloppy". Often the type, and other information is "noise" and the OP has created an MCV example. $\endgroup$ – Gypsy Spellweaver Apr 30 '18 at 13:29
  • 6
    $\begingroup$ Maybe they need to be introduced to the Code Review stack as well. That ought to give them a view to the differences between clear/muddy code. (Code golf serving as examples of muddy.) $\endgroup$ – Gypsy Spellweaver Apr 30 '18 at 14:16
  • 3
    $\begingroup$ Write up some really ugly code using var & object, maybe taking your lead from @Ben I.'s example. As a pop quiz, have them write up documentation for what it actually does, describing the role, lifetime, etc of every object and variable. And this is a time-limited quiz, of course. Next week, give them the same code quiz but with meaningful names and identified types. $\endgroup$ – user4104 Apr 30 '18 at 17:25
  • 2
    $\begingroup$ @JavaJive, IIUC GetType gets the type of actual, runtime value, so it obviously has to work. That is different from type inference, which is about type of variable. $\endgroup$ – Frax Apr 30 '18 at 22:21
  • 1
    $\begingroup$ If you declare an Int32 as Object, you've just boxed a value type, which is terrible sloppy coding with real performance implications. If you declare an Int32 as var, all you've done is shift the reader's focus on the parts of the code that actually matter, as opposed to making your C# read like Java. FWIW you shouldn't even need to mention var until you're ready to get into anonymous types and LINQ. Also, hover the mouse over var, the IDE tells you what the compiler type is. var has NOTHING to do with Object whatsoever. And it's NOT dangerous, even less "sloppy". $\endgroup$ – Mathieu Guindon May 1 '18 at 19:09
29
$\begingroup$

Then I inevitably have students who abandon the good habits they had formed and want to make everything an Object, or declare everything as var.

These are very different situations. As you observe, the use of var in C# is controversial, but using object as the type of all variables (and then needing to cast any time you want to actually use the variable) is not controversial: it's just wrong.

The first page of answers on the linked SO question about var in C# is incomplete. It doesn't mention that using var can avoid bugs due to typing in one edge case: foreach (var foo in foos) vs foreach (Foo foo in foos). For legacy reasons, if foos is a System.Collections.IEnumerable but not a System.Collections.IEnumerable<> the latter is equivalent to foreach (Foo foo in foos.Cast<Foo>()), which is not especially obvious. (In fact I misremembered and was corrected in comments, which reinforces the advantage of using var here).

TL;DR: blanket prohibitions on var are almost certainly going too far.

My question is, since these students are still learning, and since it is my classroom after all, should I emphatically require that they always explicitly specify types unless absolutely necessary? ... And, if I do require it, how can I explain the (in my opinion) sloppiness they sometimes see in online communities?

var is far from the only controversial subject in programming praxis. The solution in industry is the style guide: whether as a formal written document or as an informal internalised set of opinions which are imparted to new team members by word of mouth when they submit changes which violate the house style. If you're explicitly preparing your students for industry, it is perfectly reasonable to dedicate a bit of time to explaining the concept of the house style, and then when you introduce new material to explain the house style you will enforce for that feature.

The really heavy-duty approach would be to prepare a full industry-style style guide and then to produce a series of increasingly large subsets so that every couple of weeks you can give them a link to an updated style document which specifies how to use the new features you just introduced, or the new features you're about to introduce, and highlights the additions from the previous one.

$\endgroup$
  • $\begingroup$ Yes, the overuse of Object/var are different situations, but in my experience the tendency for new(-ish) programmers to resort to their use in laziness is about the same. I do mention to them that a style guide will probably be present in their careers and that they should plan to conform to it. Having a written one for my classroom would be good practice for that. Maybe I'll get them to write it! $\endgroup$ – Java Jive Apr 30 '18 at 14:03
  • $\begingroup$ Often enough the style guide is not about making the program, in its initial state, better, or the programmer's job any easier. It is, however, about making the "clarity" clearer for any programmer who later deals with that code, including the author. Few rules in style guides are based on rules in the language, but rules in the praxis of the author(s) of the guide. $\endgroup$ – Gypsy Spellweaver Apr 30 '18 at 14:13
  • $\begingroup$ foreach (Foo foo in foos) is somewhat equivalent to foreach (Foo foo in foos.Cast<Foo>()). It will throw InvalidCastException not filter by actual type like OfType<T>. $\endgroup$ – Johnbot May 1 '18 at 8:59
  • $\begingroup$ Why write your own style guide? Use the one that is official to the language. $\endgroup$ – pojo-guy May 2 '18 at 0:38
  • $\begingroup$ @pojo-guy, that's a fair point (when one exists), but it would create copyright problems with the subsetting, and dumping a full style guide which talks about features you won't mention for 3 months is not ideal. $\endgroup$ – Peter Taylor May 2 '18 at 7:31
8
$\begingroup$

Introducing these sorts of shortcuts can cause exactly the problem that you've outlined. "Oh, goody!" says the misguided student, "Now I can stop worrying about the type!"

There are a few principles that I directly teach, and repeat extensively in my instruction:

  1. There are two audiences for code, machines and people. We need to create code that is good for both.
  2. Compiler errors are better than runtime errors. (This is hard for kids to understand at first, but I return to this idea repeatedly until it is thoroughly drilled into their heads.)
  3. All code must be written with the Four C's in mind: Clarity, Clarity, Clarity, and Clarity.

It has never particularly occurred to me to illustrate that every variable could be declared an Object instead of with a more specific subtype, but the following terrible code could illustrate the dangers of doing this. (This is in Java, but could just as readily be created in C#):

/* Pre-condition: Object[] passed in is length >= 1
 * arrange puts an Object into o[0];
 */
public void arrange(Object[] o){
    int rand = (int) (Math.random()*4);
    if (rand == 0)
        o[0] = "This is a String!";
    else if (rand == 1)
        o[0] = new Integer(37);
    else if (rand == 2)
        o[0] = new Double(3.1415926);
    else {
        o[0] = new Object[1];
        arrange((Object[]) o[0]);
    }
}

Of course, code like this should make us want to puke just a little bit into our mouths. We'd ideally want the compiler to stop us. and that's the key point: declaring variables as Object removes our beautiful compile-time protections, and leaves both the coder and other human readers adrift. Sure, it's an Object, but what in the world is it?

Similarly, with var, you can borrow some examples from the very page you linked to illustrate why var can be a bad idea. I also might not introduce the concept until you have introduced Generics, as that is one of the first times that it can actually make things easier. This would allow you to couple the example of where it can make things easier with examples of where it also can make things insanely more difficult (the more over-the-top, the better for your illustrating purposes). This will enable you to have a discussion about appropriate usage based on real principles, not just course policy.

After this discussion, you can explain where you will accept these in your course, and there should be little resistance from your students. After all, what you are asking for is simply sensible. Hopefully, this will allow you to create some balance in the lab work you receive.

$\endgroup$
  • $\begingroup$ I remember when C♯ and Java were like this. Containers were containers of Objects (except arrays). $\endgroup$ – ctrl-alt-delor Apr 30 '18 at 17:27
  • $\begingroup$ I think point #2 is one of the most important, and it's one that I wish language designers would pay more attention to. If there are two things a piece of code might plausibly be intended to mean, and if a slight adjustment would make the intent unambiguous, having a language require that a programmer be explicit in both cases may be better than having it allow one to be implicitly assumed. From a teaching perspective, though, I think the issue is probably that if one fixes all the underlying problems that could lead to compile errors, one may be close to having a working program, and... $\endgroup$ – supercat May 1 '18 at 16:46
  • $\begingroup$ ...this in turn wants causes students to want to reach the point of having their program compile successfully as soon as possible. What's important for them to learn is that there are ways of making programs compile successfully without fixing their real problems, but if one uses such approaches the real problems will still remain. $\endgroup$ – supercat May 1 '18 at 16:50
  • $\begingroup$ @ctrl-alt-delor I remember when golang was like that... oh, wait... $\endgroup$ – Jared Smith May 2 '18 at 14:59
6
$\begingroup$

If I learned anything reading Dale Carnagie, if you want to convince someone to do something, don't tell them "Do this because I want you to," tell them "Do this because if you don't, you're going to suffer for it and it's your loss." Speak in terms of their own self interest.

You might want to tell them, "Now, you may want to use Objects for everything. This is bad and it will suffer because of it. You'll get weird errors when you start using it. If you don't want to spend 30 minutes debugging a program for a silly error, don't use Object like that. Here are some examples of using Objects will make your life more painful..."

As far as using var for everything, I'm a professional programmer and I use var all the time. I can't help you there (if I were one of your students, I would wonder why I couldn't work more efficiently either!). It's important to know what type you're working with, but as long as you do that, var away.

$\endgroup$
  • $\begingroup$ I am/was a professional programmer too, and I always use types when I declare. The only place I think var is justified is when working with LINQ queries and their results (foreach var...). Efficiency now causes headaches later. $\endgroup$ – Java Jive Apr 30 '18 at 18:15
  • $\begingroup$ @JavaJive That's fine... it's ultimately a matter of debate. But honestly, as a mildly rebellious student myself I would have chafed under required "vars" if there isn't an airtight case for them (which there isn't). Then again, I don't know much about teaching so I can't say when you should lay down the law and when you should back off. $\endgroup$ – Slothario Apr 30 '18 at 18:26
5
$\begingroup$

There are a number of issues to be addressed from clarity of the written code to efficiency of the compiled code. For beginners, I think the clarity issue is more important so I'll address that first.

Before I begin, though, I'll also note that the inferred typing of variables in C# is still static (compile time), strong (types don't change) typing. That hasn't changed. Inferred typing is just a syntactic hack (syntactic sugar) that appear to make the programmer's job easier. However, that is only true when you are writing the program the first time, not when you are updating/modifying/maintaining the program.

I'll note also, that programming languages are for people, not for computers. Compilers take the human-understandable code that we write and transform it into something that a machine can utilize.

Specifying the type of a variable in a declaration, while not always required introduces a kind of good redundancy. Much redundancy in programming is bad (repeating a sequence of statements) but redundancy in typing is good since it makes the intent of the programmer clearer and also, in some situations finds errors in a program at compile time that could otherwise only become noticeable at run-time. This lets the programmer fix issues earlier in the development process, which, research shows, reduces cost.

Think of the phases of program development as follows: conceptualization, analysis, design, coding, testing, deployment, maintenance (maybe not a universally agreed list, I realize). Research shows an order of magnitude increase in the cost of correction for each phase transition. If you catch the error in the conceptualization phase it costs nearly nothing to fix. (Sorry, I don't have a reference to the research - it is quite old).

When you say that a variable is int and later (or immediately) assign it an int value the compiler can check correctness. But you the program reader are also assured that your intent (x is an int) is faithfully represented in the code.

var x = 5;

reduces the redundancy. Note also that 5 is a valid value of the following C# types: byte, sbyte, int, uint, long, ulong, short, and ushort. What is your intention here. The declaration above doesn't say. The compiler will choose something it "thinks" is appropriate. By saying less about your intent you give less information to the compiler, which may then provide a less-than-optimal translation of your code. So this sort of redundancy is a good thing.

When used with objects (or otherwise polymorphic types) there is an additional consideration.

If you say

var x = new Horse("Trigger");

then the compiler has little recourse than to give x the most specific possible type. It might, in fact be preferable to give x a more general type, such as an interface type used to create the Horse class. It is generally good programming practice to give variables interface (as opposed to class) types, though this requires other rules about how interfaces are used - a separate discussion. But, in some contexts

Animal x = new Horse("Trigger");

might be preferable and it also better clarifies your intent.

None of the above uses of var is necessarily a mind-killer, but an alternative use of what I want to call sloppy typing is.

Suppose you declare a new object reference as follows:

Object x = new Horse("Trigger");

Now you have likely introduced a mess into your program. Later on, you will probably want to say

int howMany = x.count();

(We know, horses like Trigger can count, of course). The compiler will complain about this, of course since x is only known as an Object reference, not a Horse reference. The sloppy typer will then cast x to quiet the complaints of the compiler:

int howMany = ((Horse)x).count();

which is, of course ugly. We need to assure the compiler that x is, indeed, a Horse so that the count method is available. However, this cast introduces a check into the runtime code, which is both inefficient and can be wrong. What if somewhere else prior to this x was reassigned to an Aardvark object. Then whether or not Aardvarks can count or not, the run-time type check will flag an error.

This sloppy typing combined with casting is an attempt to replace the normal static type checking with dynamic type checking (as is done in, say Python). But that is misuse of the language.

Moreover, it introduces a maintenance nightmare into your program. You declare a variable in your program to have a given type, but in use it has a different (more specific) type. The compiler isn't going to remember those actual types for you, so you have to do this yourself. You can't look back to the declaration of x to find out what sort of thing it represents, you have to keep the details of the dynamic system in mind for every use of the variable. In a program of any size this is obviously infeasible. It is why we have types (and also polymorphism) in the first place.

Again, this sort of casting (down casting) is a recognized Code Smell, indicating poor design. Every explicit cast in a program is a potential point of error. Just Say No.

For completeness, let me give a few rules here that will lead to better design and better code.

  1. When you declare a variable give it an interface type (rather than a class type) - a good but not essential rule. Interfaces define concepts.

  2. When you implement that interface the only public methods of the class should be those defined in the interface. You won't need to cast. - an essential rule.

  3. When you extend a class (create a subclass) the only public methods in the subclass should be those declared in the superclass. Essential. If you need more methods then you have a different concept than the superclass, so extend the interface of the original class to define the new concept and then have the subclass also implement the new interface.

  4. When you declare a variable to have some (object) type, don't try to send messages defined only in some subtype, requiring casting.

The above rules are intended to make your programs easier to understand, hence easier to update and maintain. When tempted to break the rules, spend a bit of time asking why, and looking to see if you can come up with a better design. Breaking the rules tends to make your overall program into a Big Ball of Mud.

All of the above has focused, primarily, on clarity of the code for the programmer's benefit. But efficiency is also a consideration.

The more you can inform the compiler of your intent, the better job it can do in its translation. Some languages optimize for expressing intent rather than process, in fact. Database languages and Logic languages do this explicitly. If you tell a compiler how to do something it will be constrained to follow your directions. But if you tell it what you want, rather than how to achieve it, it has more options and can do better optimizations. So, explicit typing in programs is also a way to communicate intent (what) to the compiler rather than forcing later checks (casts). So, maybe x in our first example really only needs to be a short, or even a byte. But the declaration using var probably won't achieve that.

I also realize that "abstract" names like x are a poor choice. I only use it here as I don't have a context in which to provide an intention revealing name.


History note: Implicit typing is probably an incorrect term for this. FORTRAN had implicit typing where the type of a variable depends on the spelling of the name. Names beginning with I through N were implicitly Integer unless a specific declaration implied otherwise. Other names indicated Real variables.

On the other hand, Standard ML and related languages have a subsystem in the compiler that could infer types (type inference). ML has strong static typing also, but programmers seldom need to specify a type since the compiler can ordinarily figure it out.

But use of var isn't dynamic typing either. In dynamic typing (Python, Ruby), variables (names) don't have type. Only values have type. Dynamic typing can still be strong typing, but it is done at run-time, not compile-time. Errors are found later unless they are rigorously rooted out through testing during development.


Final Note:

The biological classification of Carl Linnaeus is probably a poor analogy to use for subclassing and polymorphism. Each entry in the classification tree is a different concept and all are abstract. There is no such thing as a pure Mammal. Moreover, classes define behavior of objects (via public methods) and the Linnaeus classification system is nothing about behavior. Trying to use it to generate examples of subclassing is one of the reasons students get terrible ideas about what should be done in a subclass.

$\endgroup$
  • $\begingroup$ Variables can be used either to represent values or containers. One could use a value of type Horse which identifies a Horse just about anyplace one could value of type Animal that identifies a Horse, but a container of type Horse which will always a Horse any time it's not null is very different from a container of type Animal which initially identifies a Horse, but might later identify a Dog. $\endgroup$ – supercat May 1 '18 at 16:53
  • $\begingroup$ @supercat, sorry, not sure how you are using the term container here. Are you contrasting reference (i.e. heap) values from others? $\endgroup$ – Buffy May 1 '18 at 17:26
  • $\begingroup$ If I say int x=someComputation(); and never write to x, then within its scope x would be synonymous with the value yielded by that call to someComputation. If instead I say, int x=0; and later within a loop say x+=someOtherComputation;, then there would be no particular value with which x would be associated throughout its scope. Instead x would be used as a place to store computations which are performed throughout its lifetime. $\endgroup$ – supercat May 1 '18 at 18:21
  • $\begingroup$ @supercat, If you have some way to specify that x is a constant (const in C++, final in Java, etc) then yes. Otherwise the system will treat both the same and it is only your intention that makes them different. $\endgroup$ – Buffy May 1 '18 at 19:52
  • $\begingroup$ The system will treat both the same, true. On the other hand, programs are usually intended to be read not just by computers but also by people. $\endgroup$ – supercat May 1 '18 at 20:11
5
$\begingroup$

If you expect that many of the students will pursue software development careers, it would not be unreasonable to declare style standards for the course, and include it in their grading.

When doing so, I strongly recommend explaining why particular rules are in place, because it moves disagreement into "arguing principles" instead of having "arbitrary rules".

The explanation could be as simple as "we're going to follow the corefx style guidelines" because the majority of example code produced by the .NET development team will adhere to these rules.

  1. We only use var when it's obvious what the variable type is (e.g. var stream = new FileStream(...) not var stream = OpenStandardInput()).

Or, the rules could be a bit more esoteric/practical. As a grader many years ago I got the professor to agree to a style score as a portion of the coursework. I told the class "I will be reviewing your code with the program less in an 80-character wide terminal. If I have to horizontally scroll you will get 0 points for style." -- I think I gave a 0 for breaking that rule just once.

$\endgroup$
1
$\begingroup$

There are a few reasons why this is bad, for example it could lead to code issues down the line where you need something from Horse that you don't have on Object, and it may also be a code smell. I'm going to suppose the code passes and runs fine, the architecture is fine, and just focus on the remaining issue: the issue generally is of course that it's not as specific as it could be, and the reason that's bad is it's bad for maintainability and code clarity.

In general, for maintainability, you want your code to model your ontology, to be resilient to counterfactuals. That way, if things need to change in the future, your code will more likely be constructed in a way thats amenable, because even though certain specifications changed, or some refactoring is being done, your code is still basically modeling the logic of the situation.

So Object x = new Horse("silver") is bad because x is not just an Object, it's a Horse, maybe it's an Animal.

(1) You lose the code clarity, future readers won't have the info of immediately knowing, if they want to change the value of x, what x "is" and a sense of how it's going to be used.

(2) You lose benefits of type-checking, because anything using x only knows it's an Object and doesn't know it's a Horse.

I'm not a teacher, I'm just a programmer in industry, but I think maybe the single most important general principle I use in my day to day is: code should model ontology, including in the sense that it's robust to counterfactuals. A lot of bad code falls under that category.

$\endgroup$
1
$\begingroup$

Then I inevitably have students who abandon the good habits they had formed and want to make everything an Object

This can easily be shown to be a bad approach if you need a class specific property later on.

object myStudent = new Student("John", "Doe");

string name = myStudent.Name; //doesn't work

They'll probably use a hard cast, at which point the issue is no longer that they use object, but that they are losing out on type safety.

The problem here is that an adequately skilled student who is ahead of the curve will always be able to work around simple pitfalls. If he is overskilled for the example, he has more than enough brainpower to ensure that his hard casts always work.
The only way to force them to make mistakes is to give them a sufficiently complex example, where they're struggling with the core intention so much that having to manage hard casts on top of that just makes everything so much worse.

But you may have to single them out for this complex task; which can in and of itself signal that they don't need to complete this hard task (since you can't just make tests harder for a particular student).

The only guaranteed solution is to explicitly disallow hard casting in the problem statement.
Any other (more implicit) approach is liable to be a cause for complaints when the student loses grades over this.

I am always torn about how insistent to be that they revert, because I know the use of var in C# is controversial in the industry. (See the SO discussion use of var in C# for an example).

Your conclusion does not match your observation. Suppose the community is split 50/50 between either using var freely, or not using it at all. That means that either choice is equally correct.
Standards and conventions are, unsurprisingly, decided by convention.

The only things you should be telling your students to not do are the things that a majority of the community agrees are bad approaches (such as hard casting for no good reason).


This goes even further. var may actually be the solution to your object-focused students. In all likelihood, they are choosing the blanket use of object because they find it unnecessarily hard to always write the correct type. var solves that problem by having the compiler (not the developer) figure out the types that are trivial to look up.

You're seeing this as two similar issues, but it's much more likely that one (object) is a consequence of your reluctance to allow the other (var).


Plus, the more savvy students are hanging out in SO anyway, and they see some questions and answers that lack explicit typing and other good programming habits.

This is again a matter of convention. If students are predominantly exposed to "StackOverflow code quality", then it makes sense for them to adapt "StackOverflow code quality".

Being better than your surrounding is not always objectively better. The first company I worked in was comprised of self-taught developers and one university graduate. His solutions were much more theoretical than ours. But he couldn't make those solutions work in our company, because everyone else disagreed with him.

While I'm not in any way arguing that CS theory is bad; you do need to realize that pretty much every argument of code quality rests on convention, not objectively provable superiority. If something were actually illogical or inferior, then no one would keep advocating its use, thus eventually rendering it outdated.

What you're trying to do, with the best of intentions, can also be construed to be bordering on requiring students to adhere to your preferred standard as opposed to the average community standard.

My question is, since these students are still learning, and since it is my classroom after all, should I emphatically require that they always explicitly specify types unless absolutely necessary?

As a teacher, it is your prerogative to be selective about what you consider a correct answer. However, if you are requiring a particular standard to be upheld, then you should explicitly mention this.

If you ask your students for "an application that does [thing]", then you're implying that any working code is a correct answer to the question (which deserves full marks).
If you ask your student for "an application that does [thing] and avoids string concatenation (+)", then you've made it clear that working code that uses string concatenation is not a correct answer to the question.

If you want to force a particular answer, you must phrase your question accordingly. Do not punish students for an implicit convention, because most students expect questions that reveal how their answer will be graded.

And, if I do require it, how can I explain the (in my opinion) sloppiness they sometimes see in online communities?

"Just because it works, does not mean it works well." is what my teacher used to tell me. And I was the kind of student who needed to repeatedly be told that.

(Of course, sometimes it is justifiable, preferred, and necessary NOT to).

Just out of curiosity, when is var ever a necessity? I can't think of a single case. As far as I'm aware, var is merely shorthand, not an actual technical benefit.

$\endgroup$
  • $\begingroup$ var is absolutely necessary in some contexts with anonymous types, which AIUI was one of the motivations for introducing it. My answer to this question also mentions a situation (foreach loops) where getting into the habit of using var always will allow the detection of problems at compile-time rather than runtime. $\endgroup$ – Peter Taylor May 4 '18 at 7:37
-1
$\begingroup$

Various forms of the same question to ask oneself:

  • Does this "thing" represent part of my design?
  • Is it a key aspect of a contract?
  • Do I want to anchor my thinking around a specific type at this point in my code?

In all cases a strict type declares to my reader (myself in 6 months) that this is important. If it's less important, let it go.


an implicit reference to a Disney movie is not explicitly declared, so it's not an anchor point for my argument.

$\endgroup$
-2
$\begingroup$

Teach them c/c++ first, or go into the common uses of each data type first so they have to understand the intrinsic properties.

$\endgroup$

protected by thesecretmaster May 1 '18 at 2:23

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.