Feedback request: Should Mojo adopt "specialization" for what we currently call "parameters"?

Most developers know what arguments and parameters are, even if some use the terms interchangeably. Mojo uses parameter to mean something that’s neither of those. This unique use of parameter can create real teaching and learning friction for new adopters.

We’ve recently been moving the word parameter out of Mojo in favor of comptime. It says what goes in those square brackets and doesn’t collide with existing mental models, unlike parameter. As March draws to a close, this is likely the last window to reconsider what we call the contents of those square brackets.

C++ and Rust call compile-time refinement specialization. That term covers everything Mojo parameters currently do, from concrete types to compile-time values. It gives us a natural noun: spec. A spec clearly signals what goes in those square brackets and avoids the mental-model collision that parameter creates.

I realize “specialization” can refer more narrowly to selecting a more specific implementation. I’m proposing a broader teaching term here. A spec is the thing in square brackets that makes a declaration more specific at compile time.

So I have a question, and I’m asking for help. I’m not really asking whether you struggled with this term. I’m asking whether the next hundred thousand developers will.

What do you think of this change? Would it make Mojo easier to enter, learn, and teach?

Most of the impact would be in error messages, docs pages, docstrings, and code comments—not in Mojo code itself, aside from updating the docstring keyword.

I value your feedback and want to run this by language users who have a stake in how Mojo is taught, discussed, and encountered for the first time. Thank you.

3 Likes

At this point, like many using Mojo since the beginning, I am pretty much desensitised to the terminology churn. Still, we need to think very hard about the second-order usage patterns of these terms (much as Wittgenstein would have warned us), which actually operate more like functors. Under this new scheme, what do we call a parameterised function—a “spec generic function”? And what happens to the parameter domain, in contrast to the argument domain? We could ask many such questions, and without a more complete account of the various use cases, it remains difficult to make a sound judgment.

2 Likes

“spec” seems like it would fairly heavily collide with the name for the abstract description of what a function does. Additionally, “spec” is used very heavily to mean a program which formally verifies particular properties in formal verification, which is something Mojo may interact with as its type system grows to full dependent typing. I also think that, if the term “specialization” is used in close connection with generics, a substantial number of C++ developers will assume it’s similar to template specialization. As someone who has spent a lot of time with Rust, I haven’t heard specialization used in the context of generics except to refer to an equivalent feature to C++ template specialization.

I think it would be better to instead always refer to parameters as “generic parameters” or “comptime parameters” in documentation. In my opinion, we should try fairly hard to make people connect them to things like Java/C# generics, which have a fairly large audience and are taught in classrooms around the world, and then explain that Mojo has taken that idea and expanded it. I think the first thing that needs explanation is why Mojo uses [] for parameters, since I think that’s confusing many people. It would be a decent amount of work, but writing multiple explanations that assume familiarity with popular language families or languages to help here would be nice. For example, python and javascript may need “how types help compilers” before even getting into Mojo’s more advanced type system features, but I’d expect a Rust developer to be able to jump almost directly to “linear types and the stuff Mojo can have that Rust can’t because of them”, “Rust features Mojo is missing for now”, and the idea of passing around structs in generics and the MLIR interpreter as an expanded version of Rust’s const. I also think that some of the confusion is coming from missing features that people expect, like parametric traits, trait objects, etc.

4 Likes

what do we call a parameterised function—a “spec generic function”? And what happens to the parameter domain, in contrast to the argument domain?

Good distinction. I’d keep the existing phrasing for the function itself and call it a generic function. The proposal doesn’t change that framing; it only changes what we call the thing inside the brackets.

For the second point, I think the distinction is compile-time domain vs run-time call domain. Specs live in the compile-time domain, while parameters and arguments live in the run-time domain. The syntax already reflects that ([] vs ()), so the terminology should reinforce it rather than blur it.

On “specialization” being narrower than how I’m using it: that’s fair. I’m proposing it primarily as a teaching term. The thing in brackets that makes a declaration more specific at compile time. If that still feels overloaded, the shorter term spec carries most of the meaning with less baggage.

1 Like

Additionally, “spec” is used very heavily to mean a program which formally verifies particular properties in formal verification…I also think that, if the term “specialization” is used in close connection with generics, a substantial number of C++ developers will assume it’s similar to template specialization.

You’re not wrong. And this isn’t just for generics, as Mojo has value parameters and not just type parameters.

Ideally, we should have types as values at some point as reflection gets better and “compiler magic” gets peeled back. I don’t think that it’s going to be productive in the long term to make a distinction between a struct which has all of the information needed to construct a type and a type, especially if we get the ability to do something like type_of(T).members[0], or if the “type type” has a constructor that lets us create new struct types out of thin air. At that point, I think any distinction between types and values falls away. This is why I’ll say things like “generic over length/rank” in other discussions.

2 Likes

I think technically types are values, but I’d check with an engineer to be sure

I would agree with “comptime parameters” and add “comptime arguments”. Just lean into the compile-time domain side of things.

This is also, as far as I can tell, how Zig refers to their analog.

5 Likes

I think making too hard a distinction here is a mistake. For instance.

def fib(var n: Int) -> Int: # n is a run-time argument, right?
    ...

def allocate_fib_n_sized_array[T: AnyType, n: Int]() -> InlineArray[T, fib(n)]: # n has to be a compile-time argument here
    return InlineArray[T, fib(n)](uninitialized=True) # runtime code, but n must be compile-time here, despite going into a "run-time" argument slot. 

I am concerned that attempting to take shortcuts here will leave people confused later on, so it may be better to try to give people a deeper understanding of how the type system works.

They are, but they are somewhat poorly integrated so they feel like a separate thing right now. Over time, that gap should close, especially with some of the work Joe has done on reflection.

1 Like

I think the usage needs to be qualified, i.e. avoid using “parameters” or “specialization” by itself.

If it’s about something where the distinction is about compile-time versus run-time, call it “compile-time parameter” or “compile-time specialization”.

In my view it’s not about using one word or the other - it’s about providing sufficient context such that users (on average) have a chance to understand.

To me this is the essence of Mojo as a language: It’s about being “annoyingly” specific (to enable highly specialized optimizations)

2 Likes

Why not call it „Type parameters" like Python does?

I see no strong reason to deviate from (typed) Python here.

I agree with @owenhilyard that „spec“ is rater an abbreviation for „specification“ (documentation, verification) than „specialization“.

„type param“, „comptime param“ or just „param“ seems a natural fit to me.

If in some context it is important to emphasize whether something is evaluated at runtime or comptime I agree with @jbemmel that writing this out is a good option: „comptime param“ and „runtime arg“.

1 Like

Strong agree

1 Like

Hi, no idea yet for me! (thinking and reading the post)
Just would like to share one way of how i see parameters:
“something that increase the dimensions” in the code/types.

1 Like

Honestly, I think the easiest distinction is between “compile-time parameters/arguments” and “runtime parameters/arguments”.

Because at the end of the day, that’s the only meaningful distinction between the two. Compile-time refinement, or template specialization, is conceptually just a function that runs at compile time and evaluates to an entity knowable at compile time.

Put another way, what we have in the square brackets and what we have in the parentheses are all function arguments, it’s just helpful as a programmer to have the clear visual distinction between which are required to be known at compile time and which aren’t. But a regular function, with just runtime arguments, can (or should) be able to be evaluated at compile time so long as it’s pure and the value of all its arguments are also known at compile time.

3 Likes

You and @erica_sadun may well already be familiar with this, but worth taking a glance at Idris and how it handles types (and functions that take types as arguments).

1 Like

@Nick

Related (from Nick):

1 Like

I agree with others that “comptime parameter” feels more appropriate than “spec”.

My preferred term is actually “static parameter” because we are talking about the distinction between the static and dynamic parts of a program. However, that term would best be paired with the keyword static and Modular didn’t like my suggestion to use static in place of comptime.

So “comptime parameter” is the next best thing.

5 Likes

I think I’m with you on that – I would certainly appreciate static if and static for, given the prior art. But I must have missed the discussion at the time. :frowning:

1 Like

After thinking for an while, i like the word “generic”,
for example:

generic for i in range(10)
generic DT = DType.int64

def MyFunc(arg: DT):
    foo()

generic if type_is_eq[DT, Int8]():
   ...
else:
   ...

But to be honnest, even if generic give this an different feel,
i liked the @parameter since decorators was something python users could map to.
We do have to move out of the decorator to simplify things so that’s okay (and necessary).

If this is an change needed for 1.0, my suggestion would be to think a lot,
and contemplate all the possible changes (spec, generic, others) thouroully.

To do that, the easiest way would be to generate an template of code for all features,
replace each generation with candidate word and compare them all.

For example: comptime if type_is_eq[T, T2](), spec if type_is_eq[T, T2](), comptime for..

Then with all of theses results that show how mojo would look like for each candidate,
it should become easier to decide and check.

I do like comptime too, because it remind that is is not an runtime thing,
and spec is correct, because it means an specialization.
Static is also correct, because it has to be known, but it sounds like not-dynamic,
and generics/templates/specialization are to create many, with an sense of adaptability (lol).

1 Like

In formal documentation "compile time parameters“ (comptime params) seems a good fit because comptime is already used as a keyboard in Mojo.

"compile time parametrization“ is unambiguous and a good description of what it does IMO.

But in informal discussions and for people that are using Mojo a lot I really like todays usage of „params“ and „args“ to distinguish between comptime parameters and function arguments. It is short, simple and easy to get used to.

2 Likes