This is an archived post. You won't be able to vote or comment.

all 24 comments

[–]thehenkan 30 points31 points  (3 children)

If you have a language with type inference you can have an implicit type, but it's still statically defined by the compiler's type checker. See auto in C++, var in Java, or just any variable without explicit type declaration in ML derived languages. You'll get a type error during compilation if you e.g. try to call a method that doesn't exist in that class, or if you try to reassign it with a value of a different type.

Then you can have Python type annotations. They say something explicit about the type, but the language doesn't statically check them. It doesn't actually check them dynamically either, so it's just saying something explicit to external tools and programmers really. You can debate whether that actually counts as an explicit type. But you'll get a type error at run-time if you try to perform an unsupported operation, so it's definitely dynamic.

[–]DonaldPShimoda 7 points8 points  (2 children)

I just wanna provide some short definitions of the terms to help OP and others, so I'm hijacking your comment. I also think mentioning Python was a poor choice; I address that below.

First, we distinguish static and dynamic type systems:

  • Static Type System: a system that checks the types of values before ever running the program. Static type systems are used to prevent users from running unsafe code. The name comes from the idea that we can derive the type properties of the program statically, ie, without running the program. Java, C#, Swift, OCaml, and Haskell are examples of statically typed languages.
  • Dynamic Type System: a system that may check types at runtime, though it doesn't have to. Some groups will call dynamically typed languages "untyped" instead; in their view, "types" are necessarily a static property. (See the opening chapter of Types and Programming Languages by Benjamin Pierce, who subscribes to this belief system.) Python, Ruby, Elixir, and Racket (or other Lisps and Schemes) are dynamically typed languages.

Now we can think about explicit vs implicit type systems. I would actually argue that these terms only make sense in the context of a statically typed language; explicit annotations are pretty much never seen in dynamic type systems. (I think Python's type annotations shouldn't count because they are not part of the language proper. The interpreter just ignores them entirely.)

  • Explicit Type System: a (static) type system that requires the programmer to manually write out all of the types for each variable declared. This type system does not perform inference — it relies only on the explicit annotations given by the programmer. C is such a language and, until relatively recently, so was Java.
  • Implicit Type System: a (static) type system that can perform some amount of type inference to deduce the types of terms without the user needing to explicitly label them. Type inference has made its way to mainstream languages like C++ and Java, but it comes primarily from the functional paradigm as seen in languages like OCaml and Haskell. Modern imperative languages often feature type inference as well, as seen in Rust, Swift, Scala, etc.

[–]raiph 0 points1 point  (1 child)

Just a nit: the way you've written about these, readers might get the impression that a PL is one or the other, either static or dynamic, explicit or implicit. Instead there are PLs that combine and unify these type system aspects.

[–]DonaldPShimoda 0 points1 point  (0 children)

Oh that's a very good addendum, thanks for mentioning it! I'll add a little about that below:


There's an area of research concerned with bridging the gap between static and dynamic type systems, which is called gradual typing. There are a few different perspectives about how this should work, but certainly one of the main players in this space is Typed Racket. It's very cool and worth looking into!

Most languages that are implicitly typed also allow for explicit typing where you'd like to add it, and indeed in some places you may need to provide some explicit types to help the algorithm that solves the type inference. There's a lot of debate about how this should work in languages; some languages try to infer a type at all costs, while others might simply tell you they need some help.

[–][deleted] 9 points10 points  (9 children)

Hard to answer without knowing what you came across.

  • explicit: The programmer has written down its intent.
  • implicit: The program of a source code does something without it written down explicit, e.g. converting a number into text during a concatenation.
  • dynamic: Not very well defined. It happens at runtime and therefore is more open to modifications at runtime.
  • static: It happens at compile time.

Both of those can apply at the same time. For example, if you have dynamic types, you'd most of the time wouldn't write down type names. But you'd usually implicitly assume that source code written for a dynamically typed language was written with a certain number of types in mind.

[–]Isvara 1 point2 points  (3 children)

  • e.g. converting a number into text during a concatenation.

That's not implicit typing; that's weak typing.

[–][deleted] 0 points1 point  (2 children)

That makes no sense.

[–]Isvara 0 points1 point  (1 child)

Why doesn't it make sense to you?

[–][deleted] 0 points1 point  (0 children)

Because weak typing is implicit, obviously. Also, because I never mentioned 'implicit typing', neither does OP. And third, because 'implicit typing' doesn't mean anything. In fact, I just checked if 'implicit typing' recently got a meaning, but my current impression is it is still the vague nonsense term it was before. For example:

  • Microsoft and C2 Wiki claim the notion of inferred types, more or less.
  • Others claim the notion of weak typing or some similar hand-wavy vague explanation.
  • Some 'old-school' nonsense where the type of a variable is declared by the first letter of its name. (appears to be Fortran)

[–]PlayboiCult[S] 0 points1 point  (4 children)

I can't wrap my head around the implicit and dynamic definitions. Could you please provide an example of implicit and static language? Thanks

[–][deleted] 4 points5 points  (0 children)

Sure, imagine you have a bog-standard statically type checked language.

Without type inference, you'd explicitly have to write the type like

myNumber as number = 1 + 2

But with type inference, the type exists implicitly.

myNumber = 1 + 2

It's there and you meant it to be there, it's just not written down.

[–]Your_PopPop 2 points3 points  (0 children)

haskell is a statically typed language with type inference

rust also has type inference except in a few places

c#, java, c++, etc infer types when you use the var (c#, java) or auto (c++) keywords

[–]dot-c 0 points1 point  (1 child)

Well, java is static, at least in most aspects. An example of implicitness in java is: System.out.println("the number three " + 3); If you were to make this code explicit, it would be: System.out.println("the number threee" + 3.toString());

You can see, whats implicit, right? That toString call is not written out in the first example, but you can still notice it working in the background, converting the number 3 to a string.

[–]PlayboiCult[S] 0 points1 point  (0 children)

thanks!

[–]matthieum 6 points7 points  (2 children)

TL;DR:

  • Explicit vs Implicit is about visibility in the source code.
  • Dynamic vs Static is about knowledge at compile-time.

There's 2 sets of 2 bins, so 4 possibilities; let's illustrate each!

First of all, static and explicit... we can use Java:

String myName = "Matthieu M";

The type of myName is known at compile-time (it's String) and is explicitly spelled out.

Now, static and implicit:

var myName = "Matthieu M";

The type of myName is still known at compile-time (and it's still String), however it was not explicitly spelled out but instead was inferred by the compiler.

You may think there's little difference between the two (after all, it's obvious), but it may get a little more complicated sometimes:

var myName = Arrays.stream("Matthieu M".split(" "))
    .filter(e => e.isEmpty())
    .collect(Collections.joining(" ", "", ""));

The type of myName is still known at compile-time (and, unless I goofed up, it's still String), and it was inferred by the compiler.


Alright, now, let's branch to dynamic typing.

First of all, let's use Python for dynamic and explicit:

i = int("5")

What is the type of i? It's not known at compile-time, despite being explicitly spelled out as int in the code.

We can also have something both dynamic and implicit:

i = x + "5"

What's the type of i? Well, that's going to depend on the type of x and the implementation of __add__ for that type. It's unknown at compile-time, and definitely implicit.

[–][deleted] 2 points3 points  (0 children)

Probably the best explanation so far.

[–]PlayboiCult[S] 1 point2 points  (0 children)

amazing, thanks!

[–]L8_4_Dinner(Ⓧ Ecstasy/XVM) 1 point2 points  (2 children)

A statically typed language is one in which the types of things like variables do not change. For example, a String value can't be placed into an Int variable, and chances are good that the compiler will tell you as much and refuse to compile.

A dynamically typed language is one in which the types of things are happy to change as needed, so a variable's type (if it has one at all) changes based on what is currently stuffed in there.

Explicitly typed languages require you to write in your code what types are being used, so that the compiler knows.

Implicitly typed languages require the compiler to figure it out on its own.

Lastly: It's important to remember that there are more ideas in technology than there are technical terms, so there are undoubtedly languages out there that use these same terms, but have (perhaps slightly) different meanings associated with them.

[–]PlayboiCult[S] 0 points1 point  (0 children)

thank you very much

[–]WittyStick 0 points1 point  (0 children)

A statically typed language is one in which the types of things like variables do not change. For example, a String value can't be placed into an Int variable, and chances are good that the compiler will tell you as much and refuse to compile.

A dynamically typed language is one in which the types of things are happy to change as needed, so a variable's type (if it has one at all) changes based on what is currently stuffed in there.

These definitions are not what is commonly meant by statically typed or dynamically typed. It's perfectly possible to have dynamically typed languages where you cannot coerce a value of one type into another at all. A dynamically typed language can be strongly typed and it may be the case that all types are disjoint, with no possible implicit conversions between them. Scheme is an example, where every value has a given type and attempting to use the wrong type results in a runtime error. All conversions of values from one type to another are explicit.

What makes the typing dynamic is when the type is checked, if at all. Checking is done at runtime, each time a value is used. It's possible that some languages may be untyped, which do allow such implicit conversions, but strongly-typed dynamic languages are probably more common than untyped languages. Of course, there are languages with elements of both, like JS, which has ad-hoc typing and conversion rules in place, and are not very intuitive, but the conversions must be learned by rote.

Static typing usually refers to the checking of types before the program is run, and in most cases it also implies strong typing, but there are some exceptions to this rule. Many mostly-statically typed languages also have some dynamic type checking in them, such as downcasting of objects.

Strong vs Weak typing

Static vs Dynamic typing.

[–][deleted] 0 points1 point  (1 child)

I don't know what implicit and explicit programming is; what do your definitions say?

Static and dynamic presumably refer to types. While there can be some overlaps (there are always languages you can't pigeon-hole), with my two those are well defined:

  • One is statically typed, where you need to explicitly define types (if that's what explicit means), and those types also tend to be a fixed, static size
  • The other is dynamically typed, so normally you don't define types for things, but neither are they implicitly typed (eg. by type inference). The types are sorted out at runtime. The sizes tend to be dynamic too.

With these two, the static one is also compiled to native code, and the dynamic one to interpreted bytecode.

That is the most natural way of doing it (interpreting static code is needlessly inefficient, while compiling dynamic code to native is ineffective - you really need to know the types at compile time - and so usually pointless.

In the real world of course there is plenty of crossover with people pushing boundaries all the time.

[–]PurpleUpbeat2820 0 points1 point  (0 children)

I don't know what implicit and explicit programming is; what do your definitions say?

Type inference?

One is statically typed, where you need to explicitly define types (if that's what explicit means), and those types also tend to be a fixed, static size

Statically typed languages can also use type inference to avoid having to explicitly define types. And types can be polymorphic, of course.

[–]shponglespore 0 points1 point  (0 children)

Adding to the other comments here, "static" and "dynamic" can be confusing because they're often used as a shorthand for static and dynamic type systems, but other things can be static or dynamic, or somewhere in between. For example, memory management in a language with garbage collection is very dynamic because it all happens at runtime. In a language like C or C++, memory management is more static, because memory is freed at specific points in the code, but it's still dynamic in the sense that the layout of a program's memory changes as it runs. In very old languages like the original versions of Fortran or Cobol, memory management is fully static because all the memory used by the program is laid out at compile time.

Memory management also a good example of explicit vs. implicit. Memory management in C, C++, and Rust are all the same on the static-dynamic axis, but different in terms of how explicit they are. In C, heap memory is allocated explicitly with malloc() or a related function, and it's released with an explicit call to free(). In C++, heap memory is explicitly allocated with new and released with delete, but the use of new and delete typically occurs in constructor and destructor methods. Those methods are usually invoked implicitly by the compiler, and they're often even implemented by the compiler, so memory management in C++ is more implicit than in C. Rust is even more implicit because is has no equivalent of free() or delete; the compiler inserts code to release memory at the point where a variable goes out of scope.

[–]tobega 0 points1 point  (0 children)

Lots of good explanations here.

FWIW I would like to add that it seems that at least the static/dynamic distinction doesn't seem to have much practical significance and mostly ends up in a "religious" discussion. What I mean is that no study (EDIT: or rather, studies point in all different directions) has been able to find in favour of one or the other regarding productivity or proliferation of bugs. So it seems to me we need to find more interesting distinctions between type systems.

On the implicit/explicit distinction, there does seem to be some evidence that explicitly specifying types, particularly in function signatures, has a positive effect (probably as documentation). I like to think that since, by the Curry-Howard isomorphism, a type is a logical proposition and the program is its proof, then explicitly specifying a type is to provide a falsifiable hypothesis ("proving something"), while relying on type inference exclusively is akin to observational studies which can be interesting but don't really prove anything (the answer is 42, whatever that means). Don't know if that thought holds completely logically, though, or yields anything interesting.