Tutor profile: Jinesh D.
How do you avoid race conditions between asynchronous function calls?
Subject: Java Programming
Why doesn't Java allow multiple inheritance?
The "diamond problem" is an interesting consequence of multiple inheritance that Java chose to avoid entirely by not allowing multiple inheritance to begin with. The diamond problem goes as follows. Suppose I have a class A, and classes B and C that inherit from A. Let's say I have some method "foo" in A, and let's say both B and C have their own override of foo. To be clear, let's say "foo" in A prints "Car", while "foo" in B prints "Mercedes" and "foo" in C prints "BMW". Suppose I have a fourth class, D, which extends both B and C. The question that the diamond problem poses is, "what will calling 'foo' on an object of class D do?". The answer is pretty unclear, because D should be inheriting the overrides of both B and C for the same method! How do we know which "foo" override should be called? The simple answer is that we don't. There's no way to resolve this issue reasonably. We could arbitrarily choose one or the other, or we could throw an exception, but either of these is a reasonably inelegant solution. The creators of Java decided that it would be a better idea for people who wanted multiple inheritance-like behavior to use interfaces to specify that behavior instead (a class may implement any number of interfaces) and implement it independently to avoid confusion. Some people argue that this reduces the power and expressiveness of the object-oriented paradigm, but this is just the decision that the creators of Java made in favor of safety.
Subject: Computer Science (General)
Explain to someone who knows nothing about programming what a higher-order function is.
Note: I've avoided defining unnecessary terminology like "calling" functions or "variables" or "references" to streamline the answer and also to be less confusing to a reader. Programming is essentially telling a computer to do things for you. In programming, a function is a name for a specific list of instructions. If I have a function named "takeScreenshot", and I write a program that tells my computer to use "takeScreenshot", my computer knows that I want it to execute the list of instructions associated with the name "takeScreenshot". A function can have "arguments". What this means is that a function can receive some sort of input, (it could be several distinct values, and each of these values is one "argument") from the programmer and take some action whose result depends on that input. However, you have to clearly tell the computer what those arguments are, so that it knows how to use them. For example, I might define the function "takeScreenshot" to accept 4 numbers as arguments. I might then use the arguments "200,200,300,300" with the function "takeScreenshot", which would be analogous to telling my computer "take a screenshot of the rectangle on the screen from the coordinates (200,200) to (300,300)" if my "takeScreenshot" function has instructions that tell my computer how to interpret the arguments as coordinates on the screen. Arguments can be any kind of information, provided that I show the computer how to use that information. What this means is that in theory, I can also allow a function to be the argument to another function. What does this mean? It means something like telling the computer "When I need use function X, I'll give you the name of some list of instructions. Whatever those instructions may be, execute them and do something with the result and output the final result." However, this type of function that uses another function as an argument, called a "higher-order function", has one caveat: you also have to tell the higher-order function what the argument function is going to be generally. This means we need to specify to the higher-order function what arguments the argument function itself will use, and what kind of output it will give us, so that the computer can reasonably determine how to use the function without a guarantee of what exactly it does. To be clear, in my above example, I might have said "I'll give you the name of some list of instructions that takes 2 numbers as input and outputs 1 number. Use that list of instructions on the first two numbers I give you, then use the instructions again on the result of that and the third number. Output the result, which is a single number". In this way I can use any function which uses two numbers and outputs one number as an argument, but if I use a different kind of function, my instructions won't make any sense. To give another example, let's say you have three functions, "doMath", "addNumbers", and "multiplyNumbers". Let's say doMath takes 5 arguments, 2 function arguments and 3 number arguments. Let's say the instructions defined by "doMath" are something like "use the two functions I give you to operate on the 3 numbers and give me the result". So then if I use "doMath" with the arguments "addNumbers, addNumbers, 1, 2, 4", it would add 1, 2 and 3 to give me 7. However, If I used doMath with the arguments "addNumbers, multiplyNumbers, 1, 2, 4" it would first add 1 and 2 to give 3, and then multiply 3 and 4 to give 12 as the final output. In this way, "doMath" can be used to do dramatically different things based on the functions I use with it as arguments. This is useful because it lets me capture complex, generalized logic that can be reused in a number of different ways.
needs and Jinesh will reply soon.