Micro Pre-RFC: todo! macro


#1

Rename unimplemented! macro to todo!

Basically, the title says it all: deprecate the current unimplemented! macro, and replace it with equivalent todo!(). unimplemented is a pain to type, and usually is used in situations when you want to type code fast. Snippets/completion do not work equally well everywhere.

What do you think about this?

Case study: Kotlin has a similar function, TODO: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-t-o-d-o.html


#2

As the person who gave unimplemented! it’s unfortunate name, I very much support this!


#3

We cannot rename stable things, because that would be a breaking change.

I missed the word deprecate :upside_down_face:


This would be nice


#4

That’s why the first sentence after the title says deprecate :wink:


#5

Interesting observation: normally, we can’t deprecate widely used things for the fear of warnings flood. And unimplemented! is widely used, but not in the committed code, usually, so we can get away with hard deprecation (as opposed to edition based soft deprecation).


#6

:bike: :houses:

What about fixme!();

//begin-hyper-overanalysis

It has the advantage that the ! is close to the e at the end so your finger is closer to it;

//end-hyper-overanalysis


#7

Fixme is longer, and e is on the same hand and other finger then !, which makes it harder to type. Words that alternate hands are usually easier to touch type.

Intrestingly enough, both todo and fixme alone are alternating.


#8

I suppose; I tried typing both in rapid succession and there was more or less no difference :wink:

Whether you go with fixme or todo, I think you can add to the list of motivations:

  • unimplemented is not a common word whereas the latter two are; this helps with grep.

#9

fixme has different meaning than todo

while todo is unimplemented, fixme is implemented but broken


#10

Yeah that’s a good point;

Consider fixme! retracted. :wink:


#11

unimplemented!() being hard to type is the encouragement that you need to not put it into your program.

Rust is always guiding you towards success!


#12

I don’t agree with this reasoning (also, I am not sure where this is a serious argument or a joke :slight_smile: ): unimplemented is hugely useful for exploratory programming, where discouragement does not make sense. The best way to discourage the use of unimplemented would be to just not have it in the std in the first place, but it is in std, exactly because it is useful to put unimplemented in your program while you figuring the stuff out


#13

Thinking about it more, perhaps we need to add todo! without deprecating unimplemented!()? That way, we change the meaning of unimplemented!() to “intentionally not implemented, don’t call this function at runtime”, sort of like caller-checked unreachable!().

See an example of this use-case here: https://github.com/rust-lang/rust/blob/0c1dc62c1ec3c23dcb5e90500a2b3b25817ad03a/src/bootstrap/builder.rs#L89-L95


#14

Ostensibly, a typed hole ? (as Agda et. al have) might be better for prototyping:

let x = ?;

foo(?);

let x: ? = 1;

In this case, the type system (and IDEs) can reason about what you are trying to do (unlike todo!() which will expand to some diverging expression).

Thus, you can do things like:

fn foo() -> Result<MyType, impl MyTrait> {
    // This wouldn't work if you had `x : !` because `! :/ impl MyTrait` in general
    Err(?)
}

#15

For the IDE todo! macro and a special ? syntax are exactly equivalent. Also, we’ve completed the whole circle here, because this proposal was born from this holes discussion: https://salsa.zulipchat.com/#narrow/stream/145099-general/subject/structured.20editing. It also discusses the point that, for IDEs, having holes in the surface syntax is not really necessary, because it’s not that hard to treat literally nothing as a hole.

Don’t have an opinion about type-system side of things, but adding holes to a language seems to be a massive change which is unlikely to happen any time soon.


#16

e and ! are 9 columns and 2 rows apart for me… Actually trying both out todo is far far far easier to type in dvorak than fixme (right-middle, left-ring, right-index-inner-column, left-ring; all on the home row—vs—right-index-inner-column-upper-row, left-index-inner-column, left-index-inner-column-bottom-row, right-index-bottom-row, left-middle). That index-finger dance for fix is torture.


#17

We resolved fixme! already; let’s move on. :slight_smile:

Sure; the todo! macro could expand to the same construct in the language and thus be equivalent.

However, you’ll need to have that construct in the type system to be able to say things like let x: todo!() = expr;

Perhaps not necessary no; but it does make it clearer where the holes are and it makes less things legal syntax in the grammar (and this is significant due to macros). My experience with holes in Idris and Agda have been nothing but positive.

Elaborate? Why would it be so massive (and do you mean implementation complexity or user facing complexity?)?

From the side of the grammar you’d need to add the token ? (or something else, but this one seemed apt…) to 3 places: the term grammar, the type grammar, the trait grammar.

For the term grammar ? becomes a diverging expression for an unknown empty type which happens to coerce to everything else and also implements all traits (~ modulo some questions around what traits this is sound for; we can start with a conservative subset…).

For the type side of things, ? becomes a type error that tells you what the inferencer found; Most of this machinery should already exist; we mostly need to tailor the error messages.

For the bound side of things, you can now write where T: ? and inference should tell you what the most permissive bound ? could be to make the body typeck.

None of this strikes me as massively complex to implement; On the user complexity side of things ? is a fairly intuitive syntax for “I don’t know, please tell me”; it’s also uniformly the same syntax for incomplete programs everywhere so that seems simple enough to explain to people.


#18

That’s true, yeah. Though, I don’t think IDEs need that hole to be explicit? They certainly can do useful stuff with the missing type: https://www.youtube.com/watch?v=5atAmTxbGBk&feature=youtu.be

My experience with holes in Idris and Agda have been nothing but positive.

I don’t have to much of experience with these languages, but, as a Rust IDE developer, I’ve never felt a need to have missing stuff explicit in the source language. I suspect that has to do with the fact that Rust has comparatively weaker type system than Idris & Agda, so there’s comparatively fewer useful things you can do with a hole (fill the type of the type-hole, complete the expression-hole, generate function definition from a call), and they all can be achieved perfectly without any surface syntax for holes.

Elaborate? Why would it be so massive (and do you mean implementation complexity or user facing complexity?)?

Implementation mostly: ? seems strictly more complicated then !, and we’ve been waiting for the never type for years.

In general, I feel that it’s probably a bad idea to invent a language feature for what can be achieved at 90% by a library/macro. <rant>I personally feel that we have such a laundry list of language features to implement to match C++ in expressiveness (in terms of the generated asm) and a significant technical debt in the “Quality Of Implementation” (ides, alternative implementations, compilation performance), so discussing non-trivial language “niceties”, at this time, seems not that productive </rant>


#19

(Aside: That’s quite nice, maybe I should switch to IntelliJ from VSCode…)

That is mostly enough; but it works less well with .collect::<?>(); here, I don’t want to use _ because I want to tell the compiler explicitly to stop compiling. I also wonder how IDEs can cope with holes inside macros…?

(Aside: try them out sometime if you get some free extra time (it’s hard, I know ^.^); they are a good learning experience / good fun)

That’s mostly true; however, with const generics, Rust is closing the gap to Idris and Agda so there are suddenly more places where holes become useful due to improved inference.

It is strictly more complicated than ! yes; but also more useful. Furthermore, some of the issues to get ? working would also need to be solved to get ! working better. As for waiting for !, we were blocked on type inference doing the wrong thing and we might possibly be unblocked soon: https://github.com/rust-lang/rust/pull/56219. Another semi-blocking factor was around questions re. uninhabitedness in general and pattern matching; we’ll need to resolve those as well.

Tastes differ of course, but I find macros to be unsatisfying as compared to the language feature wrt. the commonality of the use and the user friendliness; for example, https://github.com/rust-lang/rfcs/pull/2497 seems strictly better than if_chain. Macros offer a good way to keep the pressure on language design smaller so that we may prototype; that’s really nice.

Alright; I will support the addition of todo! at this time as a strict improvement over unimplemented!() and we can consider typed holes later.


#20

Ok, here we go: https://github.com/rust-lang/rust/pull/56348