In a dynamically typed system, values have types at runtime but variables and functions do not. In a statically typed system, variables and functions have types known and checked at compile-time. E.g. in Python x
can be anything; at runtime, if it is 1
it's a number and if it is "foo"
, it's a string. You would only know which type x
was at runtime, and it could be different each time you ran the program. In a language like Java, you would write int x
if x
was to be a number, and you would know at compile time that x
always has to be an int
.
"Explicit" and "implicit" types both refer to static type systems. The defining characteristic of a static system is that the types are known at compile time, but not necessarily that they have to be written out. In Java, types are explicit--you have to write them out. So in Java, a method might look something like:
public int foo(String bar, Object baz) { ... }
The types are both known at compile time (static) and written out (explicit). However, there are also languages that do not force you to write the type out. They can infer the type of a function from its body and how it is used. An example would be OCaml, where you can write something like:
let foo x = x + 1
Since you used +
, OCaml can figure out that x
has to be an int
all on its own. So the type of foo
(foo : int -> int
) is known at compile time, just like the Java example. It is entirely static. However, since the compiler can figure out what the types have to be on its own, you do not have to write them out yourself: they're implicit.
In short: whether a type system is explicit or implicit is a property of static systems. It is a completely different question from whether a type system is dynamic or static.
Often, you have type systems that are at times explicit and at times implicit.
For example, I believe C# lets you infer types using the var
keyword. So instead of writing int x = 10
, you can write var x = 10
and the compiler figures out that x
has to be an int
. C++ does something similar with auto
. These systems are usually explicit but have some inference.
On the flipside, there are systems that are usually implicit but sometimes force you to write out a type signature. Haskell is a great example. Most of the time, Haskell can infer the types for you. However, sometimes you can write code that is ambiguous like show . read
, where Haskell cannot figure out the types on its own. In this case, you would be forced to explicitly specify the type of either show
or read
. Additionally, some more advanced features of the type system (like rank-n polymorphism) make inference undecidable--that is, it is not guaranteed to halt. This means that code using this feature often needs explicit type signatures.
The SAM approach is actually somewhat similar to what Scala (and C++11) do with anonymous functions (created with Scala's =>
operator or C++11's []()
(lambda) syntax).
The first question to answer on the Java side is whether to have the return type of a lambda statement be a new primitve type, like int
or byte
, or an object type of some sort. In Scala, there are no primitive types -- even an integer is an object of class Int
-- and functions are no different, being objects of class Function1
, Function2
, and so forth, depending on the number of arguments the function takes.
C++11, ruby and python similarly have lambda expressions which return an object which is callable in explicit or implicit way. The returned object has some standard method (such as #call
which can be used to call it as a function. C++11, for instance, uses a std::function
type which overloads operator()
so that calls of the object's call method even look like function calls, textually. :-)
Where the new proposal for Java gets messy is in the use of structural typing to allow assigning such a method to another object, such as a Comparator
which has a single main method with a different name. While this is conceptually icky in some ways, it does mean that the resulting object can be passed to existing functions which take an object to represent a callback, a comparator, and so on, and expect to be able to call a well-defined single method such as #compare
or #callback
. C++'s trick of overriding operator()
neatly dodged this issue even before C++11, since all such callback options were callable the same way, and thus the STL required no adjustment to allow C++11 lambdas to be used with sort
and so on. Java, not having used a standard naming for such objects in the past (perhaps because no trick like operator overloading made a single approach obvious), isn't so lucky, so this hack prevents them from having to change a whole lot of existing APIs.
Best Answer
Actually, path-dependent types are orthogonal to structural vs nominal typing. It's not really clear what an inner class means in the context of a simple structurally-typed language. It is, however, very possible to define this. If you were to define inner classes in a structurally typed context, you would need to ensure that cases like the one you listed would be rejected (for precisely the same reasons that Scala rejects them).
You would reject such cases by doing the same thing that Scala does: model the path-dependent type as an existential type. The same pack/unpack procedure surrounding object access would hold, and the results would look almost identical to what Scala does. The results may seem like a nominal type equality, but it would still be a structural type system since the question of type compatibility will still be decided on interface rather than name.
Structural typing does have a lot of implications, but (perhaps surprisingly) most of the same concepts we all know and love from nominal type systems do carry over into structural. Structural typing is nothing more than a different way of defining type compatibility.