Dynamic Typing without duck typing? - duck-typing

I'm used to dynamic typing meaning checking for type info of object/non object oriented structure at runtime and throwing some sort of type error, ie if it quacks like a duck its a duck. Is there a different type of dynamic typing (please go into details).

Yes, absolutely. Duck-typing is an idiom which says that the type of a value at this moment in time is based on the fields and methods that it has right now. Dynamic typing just says that types are associated with run-time values, not with static variables and parameters. There is a difference between the two, and you can use the latter without the former.
For example, if you programmed in PHP and limited yourself to the basic types without using OO, then you would be using dynamic typing without using duck-typing.

No, dynamic typing is when values have type but variables do not, so most type checking is done at runtime. So, basically, if the value walks or quacks like a duck, it's a duck, else an error is thrown. Duck typing really just describes a feature of dynamic typing that ensures it will be typesafe (i.e. a method will only run if variable foo'has the right attribute or can execute that method).


What is the difference between a strongly typed language and a statically typed language?

Also, does one imply the other?
What is the difference between a strongly typed language and a statically typed language?
A statically typed language has a type system that is checked at compile time by the implementation (a compiler or interpreter). The type check rejects some programs, and programs that pass the check usually come with some guarantees; for example, the compiler guarantees not to use integer arithmetic instructions on floating-point numbers.
There is no real agreement on what "strongly typed" means, although the most widely used definition in the professional literature is that in a "strongly typed" language, it is not possible for the programmer to work around the restrictions imposed by the type system. This term is almost always used to describe statically typed languages.
Static vs dynamic
The opposite of statically typed is "dynamically typed", which means that
Values used at run time are classified into types.
There are restrictions on how such values can be used.
When those restrictions are violated, the violation is reported as a (dynamic) type error.
For example, Lua, a dynamically typed language, has a string type, a number type, and a Boolean type, among others. In Lua every value belongs to exactly one type, but this is not a requirement for all dynamically typed languages. In Lua, it is permissible to concatenate two strings, but it is not permissible to concatenate a string and a Boolean.
Strong vs weak
The opposite of "strongly typed" is "weakly typed", which means you can work around the type system. C is notoriously weakly typed because any pointer type is convertible to any other pointer type simply by casting. Pascal was intended to be strongly typed, but an oversight in the design (untagged variant records) introduced a loophole into the type system, so technically it is weakly typed.
Examples of truly strongly typed languages include CLU, Standard ML, and Haskell. Standard ML has in fact undergone several revisions to remove loopholes in the type system that were discovered after the language was widely deployed.
What's really going on here?
Overall, it turns out to be not that useful to talk about "strong" and "weak". Whether a type system has a loophole is less important than the exact number and nature of the loopholes, how likely they are to come up in practice, and what are the consequences of exploiting a loophole. In practice, it's best to avoid the terms "strong" and "weak" altogether, because
Amateurs often conflate them with "static" and "dynamic".
Apparently "weak typing" is used by some persons to talk about the relative prevalance or absence of implicit conversions.
Professionals can't agree on exactly what the terms mean.
Overall you are unlikely to inform or enlighten your audience.
The sad truth is that when it comes to type systems, "strong" and "weak" don't have a universally agreed on technical meaning. If you want to discuss the relative strength of type systems, it is better to discuss exactly what guarantees are and are not provided.
For example, a good question to ask is this: "is every value of a given type (or class) guaranteed to have been created by calling one of that type's constructors?" In C the answer is no. In CLU, F#, and Haskell it is yes. For C++ I am not sure—I would like to know.
By contrast, static typing means that programs are checked before being executed, and a program might be rejected before it starts. Dynamic typing means that the types of values are checked during execution, and a poorly typed operation might cause the program to halt or otherwise signal an error at run time. A primary reason for static typing is to rule out programs that might have such "dynamic type errors".
Does one imply the other?
On a pedantic level, no, because the word "strong" doesn't really mean anything. But in practice, people almost always do one of two things:
They (incorrectly) use "strong" and "weak" to mean "static" and "dynamic", in which case they (incorrectly) are using "strongly typed" and "statically typed" interchangeably.
They use "strong" and "weak" to compare properties of static type systems. It is very rare to hear someone talk about a "strong" or "weak" dynamic type system. Except for FORTH, which doesn't really have any sort of a type system, I can't think of a dynamically typed language where the type system can be subverted. Sort of by definition, those checks are bulit into the execution engine, and every operation gets checked for sanity before being executed.
Either way, if a person calls a language "strongly typed", that person is very likely to be talking about a statically typed language.
This is often misunderstood so let me clear it up.
Static/Dynamic Typing
Static typing is where the type is bound to the variable. Types are checked at compile time.
Dynamic typing is where the type is bound to the value. Types are checked at run time.
So in Java for example:
String s = "abcd";
s will "forever" be a String. During its life it may point to different Strings (since s is a reference in Java). It may have a null value but it will never refer to an Integer or a List. That's static typing.
$s = "abcd"; // $s is a string
$s = 123; // $s is now an integer
$s = array(1, 2, 3); // $s is now an array
$s = new DOMDocument; // $s is an instance of the DOMDocument class
That's dynamic typing.
Strong/Weak Typing
(Edit alert!)
Strong typing is a phrase with no widely agreed upon meaning. Most programmers who use this term to mean something other than static typing use it to imply that there is a type discipline that is enforced by the compiler. For example, CLU has a strong type system that does not allow client code to create a value of abstract type except by using the constructors provided by the type. C has a somewhat strong type system, but it can be "subverted" to a degree because a program can always cast a value of one pointer type to a value of another pointer type. So for example, in C you can take a value returned by malloc() and cheerfully cast it to FILE*, and the compiler won't try to stop you—or even warn you that you are doing anything dodgy.
(The original answer said something about a value "not changing type at run time". I have known many language designers and compiler writers and have not known one that talked about values changing type at run time, except possibly some very advanced research in type systems, where this is known as the "strong update problem".)
Weak typing implies that the compiler does not enforce a typing discpline, or perhaps that enforcement can easily be subverted.
The original of this answer conflated weak typing with implicit conversion (sometimes also called "implicit promotion"). For example, in Java:
String s = "abc" + 123; // "abc123";
This is code is an example of implicit promotion: 123 is implicitly converted to a string before being concatenated with "abc". It can be argued the Java compiler rewrites that code as:
String s = "abc" + new Integer(123).toString();
Consider a classic PHP "starts with" problem:
if (strpos('abcdef', 'abc') == false) {
// not found
The error here is that strpos() returns the index of the match, being 0. 0 is coerced into boolean false and thus the condition is actually true. The solution is to use === instead of == to avoid implicit conversion.
This example illustrates how a combination of implicit conversion and dynamic typing can lead programmers astray.
Compare that to Ruby:
val = "abc" + 123
which is a runtime error because in Ruby the object 123 is not implicitly converted just because it happens to be passed to a + method. In Ruby the programmer must make the conversion explicit:
val = "abc" + 123.to_s
Comparing PHP and Ruby is a good illustration here. Both are dynamically typed languages but PHP has lots of implicit conversions and Ruby (perhaps surprisingly if you're unfamiliar with it) doesn't.
Static/Dynamic vs Strong/Weak
The point here is that the static/dynamic axis is independent of the strong/weak axis. People confuse them probably in part because strong vs weak typing is not only less clearly defined, there is no real consensus on exactly what is meant by strong and weak. For this reason strong/weak typing is far more of a shade of grey rather than black or white.
So to answer your question: another way to look at this that's mostly correct is to say that static typing is compile-time type safety and strong typing is runtime type safety.
The reason for this is that variables in a statically typed language have a type that must be declared and can be checked at compile time. A strongly-typed language has values that have a type at run time, and it's difficult for the programmer to subvert the type system without a dynamic check.
But it's important to understand that a language can be Static/Strong, Static/Weak, Dynamic/Strong or Dynamic/Weak.
Both are poles on two different axis:
strongly typed vs. weakly typed
statically typed vs. dynamically typed
Strongly typed means, a will not be automatically converted from one type to another. Weakly typed is the opposite: Perl can use a string like "123" in a numeric context, by automatically converting it into the int 123. A strongly typed language like python will not do this.
Statically typed means, the compiler figures out the type of each variable at compile time. Dynamically typed languages only figure out the types of variables at runtime.
Strongly typed means that there are restrictions between conversions between types. Statically typed means that the types are not dynamic - you can not change the type of a variable once it has been created.
Data Coercion does not necessarily mean weakly typed because sometimes its syntacical sugar:
The example above of Java being weakly typed because of
String s = "abc" + 123;
Is not weakly typed example because its really doing:
String s = "abc" + new Integer(123).toString()
Data coercion is also not weakly typed if you are constructing a new object.
Java is a very bad example of weakly typed (and any language that has good reflection will most likely not be weakly typed). Because the runtime of the language always knows what the type is (the exception might be native types).
This is unlike C. C is the one of the best examples of weakly typed. The runtime has no idea if 4 bytes is an integer, a struct, a pointer or a 4 characters.
The runtime of the language really defines whether or not its weakly typed otherwise its really just opinion.
After further thought this is not necessarily true as the runtime does not have to have all the types reified in the runtime system to be a Strongly Typed system.
Haskell and ML have such complete static analysis that they can potential ommit type information from the runtime.
Strong typing probably means that variables have a well-defined type and that there are strict rules about combining variables of different types in expressions. For example, if A is an integer and B is a float, then the strict rule about A+B might be that A is cast to a float and the result returned as a float. If A is an integer and B is a string, then the strict rule might be that A+B is not valid.
Static typing probably means that types are assigned at compile time (or its equivalent for non-compiled languages) and cannot change during program execution.
Note that these classifications are not mutually exclusive, indeed I would expect them to occur together frequently. Many strongly-typed languages are also statically-typed.
And note that when I use the word 'probably' it is because there are no universally accepted definitions of these terms. As you will already have seen from the answers so far.
One does not imply the other. For a language to be statically typed it means that the types of all variables are known or inferred at compile time.
A strongly typed language does not allow you to use one type as another. C is a weakly typed language and is a good example of what strongly typed languages don't allow. In C you can pass a data element of the wrong type and it will not complain. In strongly typed languages you cannot.
Answer is already given above. Trying to differentiate between strong vs week and static vs dynamic concept.
What is Strongly typed VS Weakly typed?
Strongly Typed: Will not be automatically converted from one type to another
In Go or Python like strongly typed languages "2" + 8 will raise a type error, because they don't allow for "type coercion".
Weakly (loosely) Typed: Will be automatically converted to one type to another:
Weakly typed languages like JavaScript or Perl won't throw an error and in this case javascript will results '28' and perl will result 10.
Perl Example:
my $a = "2" + 8;
print $a,"\n";
Save it to main.pl and run perl main.pl and you will get output 10.
What is Static VS Dyamic type?
In programming, progammer define static typing and dynamic typing with respect to the point at which the variable types are checked. Static typed languages are those in which type checking is done at compile-time, whereas dynamic typed languages are those in which type checking is done at run-time.
Static: Types checked before run-time
Dynamic: Types checked on the fly, during execution
What is this means?
In Go it checks typed before run-time (static check). This mean it not only translates and type-checks code it’s executing, but it will scan through all the code and type error would be thrown before the code is even run. For example,
package main
import "fmt"
func foo(a int) {
if (a > 0) {
fmt.Println("I am feeling lucky (maybe).")
} else {
fmt.Println("2" + 8)
func main() {
Save this file in main.go and run it, you will get compilation failed message for this.
go run main.go
# command-line-arguments
./main.go:9:25: cannot convert "2" (type untyped string) to type int
./main.go:9:25: invalid operation: "2" + 8 (mismatched types string and int)
But this case is not valid for Python. For example following block of code will execute for first foo(2) call and will fail for second foo(0) call. It's because Python is dynamically typed, it only translates and type-checks code it’s executing on. The else block never executes for foo(2), so "2" + 8 is never even looked at and for foo(0) call it will try to execute that block and failed.
def foo(a):
if a > 0:
print 'I am feeling lucky.'
print "2" + 8
You will see following output
python main.py
I am feeling lucky.
Traceback (most recent call last):
File "pyth.py", line 7, in <module>
File "pyth.py", line 5, in foo
print "2" + 8
TypeError: cannot concatenate 'str' and 'int' objects

Helper Casting Functions — Is it a code smell?

I recently began to start using functions to make casting easier on my fingers for one instance I had something like this
and converted it to a tiny little helper function so I can do this
Is this a code smell?
Also, what about simpler examples? For example in my scripting engine I've considered making things like this
I really just dislike how inorder to cast a value and instantly get a property from it you must enclose it in double parentheses. I have a feeling the last one is much worse than the first one though
(also I've marked this language agnostic even though my example is C#)
Ignoring for a moment that you may actually need to do this casting - which I personally doubt - if you really just want to "save your fingers", you can use a using statement to shorten the name of your generic types.
At the top of your file, with all the other usings:
using ShorterType = Dictionary<string, Dictionary<int, List<Dictionary<OtherType, ThisIsRidiculous>>>>;
I don't think so. You've also done something nice in that it's a bit easier to read and see what's going on. Glib (in C) provides casting macros for their classes, so this isn't a new concept. Just don't go overkill trying to save your fingers.
In general, I would consider this to be code smell. In most situations where the type of casting you describe is necessary, you could get the same behavior by proper use of interfaces (Java) or virtual inheritance (C++) in addition to generics/templates. It is much safer to leave that responsibility of managing types to the compiler than attempting to manage it yourself.
Without additional context, it is hard to say about the example you have included. There are certainly situations in which the type of casting you describe is unavoidable; but they're the exception rather than the rule. For example, the type of casting (and the associated helper functions/macros) you're describing extremely common-place in generic C libraries.

Is the quality of a language where it's not required to declare a variables type an example of weak typing or dynamic typing

Is the quality of a language where it's not required to declare a variables type (such as PHP and Perl) known as weak typing or dynamic typing? I'm having trouble getting my head around the two terms.
Am I right that dynamic/static typing pertains to type conversion whereas weak/strong typing pertains to the deceleration of a variable?
According to: http://en.wikipedia.org/wiki/Type_system#Static_and_dynamic_type_checking_in_practice
Weak typing means that a language implicitly converts (or casts) types when used.
A programming language is said to use static typing when type checking is performed during compile-time as opposed to run-time.
So, strong/weak and static/dynamic are two different dimensions. A language will be one of strong/weak, and also one of static dynamic. For instance, Ruby and Javascript are both dynamically typed, but Ruby is strongly typed while Javascript is weakly typed. That is, in Ruby the following code with give an error:
1.9.2p290 :001 > 'a'+1
TypeError: can't convert Fixnum into String
whereas in JavaScript, you get:
> 'a'+1
>> 'a1'
So, a strongly typed language requires you to convert two variables to the same type in order to combine them (eg. using 1.to_s), while a weakly typed language will attempt to coerce the two variables into the same type using some extra built-in language logic - in JavaScript's case, combining anything with a String will convert it into a String value.
See: http://www.artima.com/weblogs/viewpost.jsp?thread=7590 for a further explanation.
In simple terms, strong typing has more to do with how objects are bound (essentially early binding vs. late binding) than they have to do with how they are declared.
Let's say in C# I have this class:
public class Foo
public int Bar;
public double Baz;
And I declare a variable of type foo:
var myFoo = new Foo();
When I reference Foo, like this:
Visual Studio will display a list containing Bar and Baz when I type the ., because it already knows that myFoo contains those members; it is of type Foo. This is strong typing; it means that if I misspell Bar or Baz, my program won't even compile.
However, let's say I declare a variable of type dynamic This causes object binding to be deferred until the program is executed:
dynamic myFoo = new Foo();
myFoo.Grapes = 6;
This will compile. I won't get an error until the program is run; a runtime exception will be thrown because Grapes doesn't exist on Foo.
This is an old question, but for future readers this great article may clear things up: http://blogs.perl.org/users/ovid/2010/08/what-to-know-before-debating-type-systems.html
It's kind of long but it definitively worth it.
Strong and Weak Typing:
Probably the most common way type systems are classified is "strong"
or "weak." This is unfortunate, since these words have nearly no
meaning at all. It is, to a limited extent, possible to compare two
languages with very similar type systems, and designate one as having
the stronger of those two systems. Beyond that, the words mean nothing
at all.
Static and Dynamic Types
This is very nearly the only common classification of type systems
that has real meaning. As a matter of fact, it's significance is
frequently under-estimated [...] Dynamic and static type systems are
two completely different things, whose goals happen to partially
A static type system is a mechanism by which a compiler examines
source code and assigns labels (called "types") to pieces of the
syntax, and then uses them to infer something about the program's
behavior. A dynamic type system is a mechanism by which a compiler
generates code to keep track of the sort of data (coincidentally, also
called its "type") used by the program. The use of the same word
"type" in each of these two systems is, of course, not really entirely
coincidental; yet it is best understood as having a sort of weak
historical significance. Great confusion results from trying to find a
world view in which "type" really means the same thing in both
systems. It doesn't.
Explicit/Implicit Types:
When these terms are used, they refer to the extent to which a
compiler will reason about the static types of parts of a program. All
programming languages have some form of reasoning about types. Some
have more than others. ML and Haskell have implicit types, in that no
(or very few, depending on the language and extensions in use) type
declarations are needed. Java and Ada have very explicit types, and
one is constantly declaring the types of things. All of the above have
(relatively, compared to C and C++, for example) strong static type

Option Infer On or Off? [duplicate]

Possible Duplicate:
Best Practices: Option Infer
What is the best way to mix VB.NET's Option Strict and the new Option Infer directives?
I am developing a old solution, that was translated from VB6 to the VB.NET.
Actually, the default options in files are
Option Strict On
Option Explicit On
I want to use LINQ, and found that is easier to use also the Option Infer On.
Less to write, less (so, easier) to read.
However, a (conservative, from my point of view) part of the team keeps the Option Infer Off and insist do not use it at all, without explicitly explain the causes.
In your opinion, what are the "dangers" of using Option Infer On, along with other two options (Strict and Explicit, both On)?
Code written with Option Infer on is no different in performance or type safety than code written with the same types explicitly declared. With that in mind, the arguments I could come up with against Option Infer are:
Inconsistency between cases when the type must be specified and when it can be inferred.
Class fields for one cannot be inferred, even if initialized inline.
Variables holding lambdas (Dim f = Function(x) ...) do not always infer types.
Variables that are not initialized must be given a type
The strength of this argument is directly proportional to the consistency of style in your existing codebase. (For example, I sometimes still use underscores to continue lines when working with older code even when the newer compiler does not require them, if the rest of the code around it uses them.)
Sometimes the type is not immediately obvious when looking through code.
Dim temp = Foo() 'temp is type of Foo's return, which is...
Workaround: Declare the variable's type when you feel the need.
This is not a "danger" as much as a potential inconvenience. More so if you are not working in an environment where Intellisense cannot tell you the inferred type.
The inferred type may end up being more specific than you really want in that case.
Workaround: Specifically declare the type you want in that case.
As the compiler catches cases when this is an issue, I wouldn't call it a "danger" per se. The only time I can think of where this would be an issue the compiler doesn't catch would be if you have different overloads of a method for the base and derived types or are shadowing methods in a derived type. I would argue that either of those cases are problems with the existing code and not with Option Infer.
The usage of anonymous types that come up in LINQ queries could lead to larger methods than normal as they cannot be passed between methods.
Workaround: Define named types when this occurs and break up methods as normal.
This is more of a danger in as much as long methods are dangerous. The usual "how long is too long" discussions apply.
It makes me look less productive because there are fewer KB in my code files from all those type names I don't have to type. (OK, this one is a joke.)
More and more languages infer the type of its variables. Consider C#, F# and possibly a whole host of non-.NET languages as well.
You keep type safety with option infer on. People that like to specify their variables can still do so. But sometimes it is next to impossible and definitely makes reading code harder, with those cryptic names you end up when using LINQ.
I used to be old-school. But when inferring entered the C# world, after a while I simply had to admit it: it improves coding speed, readability, and thus quality and makes it easier to maintain your code. This doesn't mean that you should stop specifying all your variables. In many cases it is still better to specify the types, regardless whether infer is on or off. Again: for readability's sake.
Explain to the old-school people why you'd want it on by default and that they can still type there typenames if they want to.
In my case I prefer having all ON
but having Infer OFF is "ok", you just need to type MORE ;-)

Is my understanding of type systems correct?

The following statements represent my understanding of type systems (which suffers from too little hands-on experience outside the Java world); please correct any errors.
The static/dynamic distinction seems pretty clear-cut:
Statically typed langauges assign each variable, field and parameter a type and the compiler prevents assignments between incompatible types. Examples: C, Java, Pascal.
Dynamically typed languages treat variables as generic bins that can hold anything you want - types are checked (if at all) only at runtime when you actually perform operations on the values, not when you assign them. Examples: Smalltalk, Python, JavaScript.
Type inference allows statically typed languages to look like (and have some of the advantages of) dynamically typed ones, by inferring types from the context so that you don't have to declare them most of the time - but unlike in dynamic languages, you cannot e.g. use a variable to hold a string initially and then assign an integer to it. Examples: Haskell, Scala
I am much less certain about the strong/weak distinction, and I suspect that it's not very clearly defined:
Strongly typed languages assign each runtime value a type and only allow operations to be performed that are defined for that type, otherwise there is an explicit type error.
Weakly typed languages don't have runtime type checks - if you try to perform an operation on a value that it does not support, the results are unpredictable. It may actually do something useful, but more likely you'll get corrupted data, a crash, or some undecipherable secondary error.
There seems to be at least two different kinds of weakly typed languages (or perhaps a continuum):
In C and assembler, values are basically buckets of bits, so anything is possible and if you get the compiler to dereference the first 4 bytes of a null-terminated string, you better hope it leads somewhere that does not contain legal machine code.
PHP and JavaScript are also generally considered weakly typed, but do not consider values to be opaque bit buckets; they will, however, perform implicit type conversions.
But these implicit conversions seem to apply mainly to string/integer/float variables - does that really warrant the classification as weakly typed? Or are there other issues where these languages's type system may obfuscate errors?
I am much less certain about the strong/weak distinction, and I suspect that it's not very clearly defined.
You are right: it isn't.
This is what Benjamin C. Pierce, author of Types and Programming Languages and Advanced Types and Programming Languages has to say:
I spent a few weeks... trying to sort out the terminology of "strongly typed," "statically typed," "safe," etc., and found it amazingly difficult.... The usage of these terms is so various as to render them almost useless.
Luca Cardelli, in his Typeful Programming article, defines it as the absence of unchecked run-time type errors. Tony Hoare calls that exact same property "security". Other papers call it "type safety" or simply "safety".
Mark-Jason Dominus wrote a classic rant about this a couple of years ago on the comp.lang.perl.moderated newsgroup, in a discussion about whether or not Perl was strongly typed. In this rant he states that within just a few hours of research, he was able to find 8 different, sometimes contradictory definitions, mostly from respected sources like college textbooks or peer-reviewed papers. In particular, those texts contained examples that were meant to help the students distinguish between strongly and weakly typed languages, and according to those examples, C is strongly typed, C is weakly typed, C++ is strongly typed, C++ is weakly typed, Lisp is strongly typed, Lisp is weakly typed, Perl is strongly typed, Perl is weakly typed. (Does that clear up any confusion?)
The only definition that I have seen consistently applied is:
strongly typed: my programming language
weakly typed: your programming language
Regarding static and dynamic typing you are dead on the money. Static typing means that programs are checked before being executed, and a program might be rejected before it starts. Dynamic typing means that the types of values are checked during execution, and a poorly typed operation might cause the program to halt or otherwise signal an error at run time. A primary reason for static typing is to rule out programs that might have such "dynamic type errors".
Bob Harper has argued that a dynamically typed language can (and should) be considered to be a statically typed language with a single type, which Bob calls "value". This view is fair, but it's helpful only in limited contexts, such as trying to be precise about the type theory of languages.
Although I think you grasp the concept, your bullets do not make it clear that type inference is simply a special case of static typing. In most languages with type inference, type annotations are optional, but not necessarily in all contexts. (Example: signatures in ML.) Advanced static type systems often give you a tradeoff between annotations and inference; for example, in Haskell you can type polymorphic functions of higher rank (forall to the left of an arrow) but only with an annotations. So, if you are willing to add an annotation, you can get the compiler to accept a program that would be rejected without the annotation. I think this is the wave of the future in type inference.
The ideas of "strong" and "weak" typing I would characterize as not useful, because they don't have a universally agreed on technical meaning. Strong typing generally means that there are no loopholes in the type system, whereas weak typing means the type system can be subverted (invalidating any guarantees). The terms are often used incorrectly to mean static and dynamic typing. To see the difference, think of C: the language is type-checked at compile time (static typing), but there are plenty of loopholes; you can pretty much cast a value of any type to another type of the same size—in particular, you can cast pointer types freely. Pascal was a language that was intended to be strongly typed but famously had an unforeseen loophole: a variant record with no tag.
Implementations of strongly typed languages often acquire loopholes over time, usually so that part of the run-time system can be implemented in the high-level language. For example, Objective Caml has a function called Obj.magic which has the run-time effect of simply returning its argument, but at compile time it converts a value of any type to one of any other type. My favorite example is Modula-3, whose designers called their type-casting construct LOOPHOLE.
I encourage you to avoid the terms "strong" and "weak" with regard to type systems, and instead say precisely what you mean, e.g., "the type system guarantees that the following class of errors cannot occur at run time" (strong), "the static type system does not protect against certain run-time errors" (weak), or "the type system has a loophole" (weak). Just calling a type system "strong" or "weak" by itself does not communicate very much.
This is a pretty accurate reflection of my own understanding of the topic of the static/dynamic, strong/weak typing discussion. In addition, you can consider those other languages:
In languages such as TCL and Bourne Shell, the "main" value type is the string. Numeric operators are available that implicitly coerce input values from string representation and result values to string representation. They can be considered examples of dynamic, weakly typed languages.
Forth may be an example of a static, weakly typed language. The language performs no type checking of its own, and the main stack may interchangeably contain pointers, integers, strings (conventionally represented as two cells, start and length). Inconsistent use of operators can lead to either interesting, or unspecified behavior. Typical Forth implementations provide a separate stack for floating point numbers.
Maybe this Book can help. Be prepared for some math though. If I remember correctly, a "non-math" statement was: "Strongly typed: A language that I feel safe to program with".
There seems to be at least two different kinds of weakly typed languages (or perhaps a continuum):
In C and assembler, values are basically buckets of bits, so anything is possible and if you get the compiler to dereference the first 4 bytes of a null-terminated string, you better hope it leads somewhere that does not contain legal machine code.
I would disagree with this statement, at least in C. You can manipulate the type system in C in such a way that you can treat any given memory location as a bucket of bits, but a variable most definitely has a type and that type has specific properties. The fact that there are no runtime checks (unless you consider floating point exceptions or segmentation faults to be runtime checks) isn't really relevant. C can be considered "weakly typed" in the sense that the compiler will perform some implicit type conversion for you, but it doesn't go very far with it.
I consider strong/weak to be the concept of implicit conversion and a good example is addition of a string and a number. In a strongly typed language the conversion won't happen (at least in all languages I can think of) and you'll get an error. Weakly typed languages like VB (with Option Explicit Off) and Javascript will try to cast one of the operands to the other type.
In VB.Net with Option Strict Off:
Dim A As String = "5"
Dim B As Integer = 5
Trace.WriteLine(A + B) 'returns 10
With Option Strict On (turning VB into a strongly typed language) you'll get a compiler error.
In Javascript:
var A = '5';
var B = 5;
alert(A + B);//returns 55
Some people will say that the results are not predictable but they actually do follow a set of rules.
Hmm, don't know much more either, but I wanted to mention C++ and its implicit converstions(implicit constructors). This might be as well an example of weak typing.
I agree with the others who say "there doesn't seem to be a hard and fast definition here." My answer tends to be based on how much rope the language gives you WRT types. If you can pretty much fake anything you want, then it's weak. If it really doesn't let you get yourself into trouble, even if you want to, it's strong.
I really haven't seen too many languages that skirt this border, so I can't say that I've ever needed a better definition that that...