 In programming, the term polymorphism refers to the ability of an operation or function to accept a varied number of inputs and or varied types of inputs and possibly change its behavior in these different cases. So for example, in Pigeon, the print operator is polymorphic in that it accepts different types of inputs. You can pass a string, a number, or a boolean, and whatever you pass, the print operator will print a text representation of that value on the console. In the case of print, the operator does basically the same thing no matter the type of input, but in principle, a polymorphic operation or function can do totally different things depending upon the number and or types of inputs. Say in Pigeon you want to write a function which changes its behavior depending upon whether its first argument is a number or not. Well, if we have an operation called isNum, which returns true when it's operand is a number, then we can simply test if the parameter is a number and branch accordingly. So when we invoke this function foo with a number for the first argument, the first branch will execute, but if we invoke the function foo with something else with the first argument, it will take the other branch. In these two branches, we of course can do entirely different things if we like. In fact, we could have one branch return one type of value and the other branch return a different type of value. So a polymorphic function or operation might change what type of value it returns depending upon the type or number of the inputs. The obstacle to polymorphism in static languages, of course, is that each function parameter must have a single declared type. To allow for polymorphism then, a static language must allow us to create multiple versions of the same function. For example, to make a function foo that accepts either one number argument or one string argument, we actually create two separate versions of foo, one with a single number parameter, and one with a single string parameter. In truth, it's probably best to think of the two versions of the function as really just separate functions that happen to share the same name. Despite sharing the same name, the compiler can distinguish between calls to one or the other by the argument types. A call with a number argument is of course meant to invoke the version of foo with a number parameter, and a call with a string argument is of course meant to invoke the version of foo with a string parameter. As for creating a function which takes a varied number of arguments, in JavaScript we can pass a function as few arguments as we like, and in the function any parameters that get no argument have the value undefined, and the length of the arguments array tells us how many arguments actually got passed in. So we can branch in the function based on the number of arguments passed. The pidgin requires us to always pass the same number of arguments as a function has parameters, we can sort of fake passing the varied number of arguments in pidgin by testing the trailing parameters for null. Here for example, when we invoke the function with null as the second argument, the second argument is tested, and if it's null, the function runs one branch but otherwise runs the other. So it's not strictly a function that takes a variable number of arguments, but passing a null for dummy values pretty much achieves the same effect. In static languages, to make a function foo that accepts either one number argument or two number arguments, we create two separate versions of foo, one with a single number parameter and one with two number parameters. Be clear then that in a static language, the compiler interpreter determines which function to call, not just by the function name, but also by the number and types of arguments. Together a function's name, number of parameters, parameter types, and the order of those parameter types are often called the function's signature, because together they uniquely identify the function when the compiler or interpreter matches function calls to functions. Obviously then, we can't have multiple functions with the same signature because then the compiler interpreter wouldn't be able to distinguish between calls to those functions. Notice that we said a function's order of its parameter types is part of its signature. What this means is we can have functions with signatures that only differ by the order of their types. For instance, a function foo with a string parameter followed by a number parameter has a different signature than a function foo with a number parameter followed by a string parameter. Note though that a function's return type is not part of its signature, so when we create multiple versions of a function those versions can have different return types as we please. Here the different versions of foo have totally different return types. In addition to classifying languages by whether their typing systems are dynamic or static, we also classify languages by whether their type systems are weak or strong. In a weakly typed language, the programmer can manipulate the bytes of any data value as they like. For example, in assembly languages, which are the primary examples of weakly typed languages, it's possible to take say a string in memory and multiply all the bytes together of that string, or say we can modify the string by flipping the third bit of every byte. The problem with doing these sorts of things is that we are not treating the data, in this case what's meant to be a string, in a way appropriate to its type. Whatever character set is used and whatever encoding is used, it really never makes any sense to multiply together the bytes of a string. The result just doesn't have any meaning. Similarly, if you flip every third bit in each byte, the end result is generally garbage. There's really no scenario in which doing this would be useful. The point though is that a language with weak typing lets us do whatever we want with the bits of our data, whether useful or not. The key idea in strong typing is that data types are defined not just by how they represent their information as bits, but also by the operations meant to be performed upon values of that type. The thinking behind strong typing is that because arbitrary manipulation of data may lead to errors, a language should preclude such errors by allowing the programmer to do nothing with a data value except perform those operations defined for its type. So, for example, the built-in types of JavaScript are strongly typed, such that we can't do anything with, say, arrays, except perform the operations defined to work on arrays. We can't just manipulate the bits of a JavaScript array as we please. In weekly typed languages, the trade-off is made that, well, yes, programmers might make mistakes, but sometimes programmers want the flexibility of complete control. These two distinctions between weak typing and strong typing and static typing and dynamic typing are often confused, so be clear, the difference is this. Static typing makes it possible to detect type errors before running the code while dynamic typing does not. In contrast, weak typing allows the arbitrary manipulation of the bits of any piece of data while strong typing does not. In principle, a language can have any combination of the two. Python, for example, combines strong typing and dynamic typing, while Java combines strong typing and static typing, and C combines weak typing and static typing. However, there's no language I know of which combines weak typing and dynamic typing.