dfinity / motoko

Simple high-level language for writing Internet Computer canisters
Apache License 2.0
507 stars 98 forks source link

Eliminate multiple name spaces #455

Open rossberg opened 5 years ago

rossberg commented 5 years ago

Type and value identifiers currently live in separate name spaces. Since we started allowing types as members of objects and modules, this has become quite annoying, the implementation is a hack, and it starts metastasising, e.g., the syntax of imports in #452 (or pattern matching on modules more generally) would require special syntax etc.

Since we already have capitalisation conventions distinguishing these identifiers it would be easy to merge the two name spaces. The only problem are classes, which define both a type and a value. I propose to solve this as follows:

While slightly special, I think this solves all the problems without being very intrusive. In the type representation we would merely reintroduce the "class" mode on functions and tweak a couple of subtyping rules. The rest of the compiler should be able to pretty much ignore this mode, as the difference to regular functions is only relevant for elaborating types in the type checker, after that they are no different from other con types.

WDYT?

nomeata commented 5 years ago

Sounds good to me.

Out of curiosity: Besides being more specialized to the use case that we need (and maybe thus producing better error messages), how does this differ from the previously discussed type constructor kinded t k that is a subtype of t and of Typ k? The proposed class T -> U could the be expressed as kinded (T -> U) (Typ U)?

rossberg commented 5 years ago

Not sure I remember that discussion. Can you refresh my memory?

nomeata commented 5 years ago

Well, that’s essentially all there is to it. There is a type constructor kinded t k (where both t and k are types). On the value level, it is just t (hence kinded t k <: t), but it can also be used as type k (hence kinded t k <: Typ k)

So really, it’s precisely the mechanism that you describe above, just not tied to classes.

rossberg commented 5 years ago

Ah, I see, so that's basically a pair of a type and a value.

That would work as well, but maybe is more general than we want (and thus perhaps even weirder?). The notion of a constructor function at least is somewhat natural. Though the subtyping rule to Typ U is a hack as well.

nomeata commented 5 years ago

It’s essentially the hack in isolation :-)

But since the error messages will be better with less generality, and we don’t need that feature elsewhere, no need to worry about the general variant.

matthewhammer commented 5 years ago

WDYT?

LGTM.

Though I don't have a lot of personal context about the problem that it's addressing in the compiler, I think I understand the problem and the solution is non-intrusive and satisying enough. The connection that @nomeata identified is interesting; I vaguely recall that other discussion, but wouldn't have made the connection myself.

crusso commented 5 years ago

Sorry, never noticed this issue, perhaps we should get into the habbit of assigning people. I'm ok with something like this in principal, but would like to hold off until I get the implementation of open types in (real soon now).

ggreif commented 5 years ago

I see a lot of capitalised imports in stdlib/, along the lines of

import Hash "hash.as";

And then followed by

public type Hash = Hash.Hash;

This would issue, when resolved as proposed, would create a slight conflict there. @matthewhammer might want to comment.

matthewhammer commented 5 years ago

This pattern (or something like it) shows up in the Rust standard library too. I can try to look for a good example, which I don't have handy.

How would you want to avoid it, @ggreif? Specifically, what syntax or design pattern would you prefer in place of this one?

matthewhammer commented 5 years ago

My understanding is that, in our ontology, types and modules should live in distinct namespaces (typing contexts) since they have distinct roles and kinds in the language design and theory, respectively.

Yet, since they are often related (one within the other), and each one needs to be abbreviated in a systematic way sometimes in the same code, we often name them with common names, as with the module Hash and the type Hash in your snippet above.

What's the alternative?