-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the todo
function
#260
Comments
I use pattern TODO :: HasCallStack => () => a
pattern TODO <- _unused
where TODO = error "TODO"
{-# COMPLETE TODO #-} it allows you to use case TODO of
Nothing -> print "nothing to do"
TODO -> TODO -- I'm too lazy to cover the rest of the cases for now Also, CAPS MAKE IT STAND OUT :) Warning is a nice touch. I think the custom warning could also be added to the functions in |
On Thu, 7 Mar 2024, Mango The Fourth wrote:
All of these have problems:
1. Problems of the functions currently in base:
* undefined:
+ doesn't give you a warning if it remains in code
+ doesn't read as "this is open, I will definitely implement it soon"
+ a lot to type for "please compiler, be quiet for the second, I will come back to this"
* error:
+ also doesn't give you a warning if it remains in code
+ may read as "this is open, I will definitely implement it soon" but this is a lot to type
+ even more to type for "please compiler, be quiet for the second, I will come back to this",
sometimes even needs paranthesis
You can generate such warnings with HLint.
That's why propose a function todo that has the following properties:
What about Lentil with Todo/Fixme comments or custom comment tags?
|
Why |
Probably just copying the definition of undefined :: forall (r :: RuntimeRep). forall (a :: TYPE r).
HasCallStack => a
-- This used to be
-- undefined = error "Prelude.undefined"
-- but that would add an extra call stack entry that is not actually helpful
-- nor wanted (see #19886). We’d like to use withFrozenCallStack, but that
-- is not available in this module yet, and making it so is hard. So let’s just
-- use raise# directly.
undefined = raise# (errorCallWithCallStackException "Prelude.undefined" ?callStack) |
@thielema: @treeowl: |
On Thu, 7 Mar 2024, Mango The Fourth wrote:
@thielema:
yes you can generate these warnings with hlint and yes there are
external tools that may allow these things in some more convoluted way;
however, adding warnings to all undefined or error does defeat the
purpose in the same way as having to add an external dependency; the
whole point of this is to make it as low barrier to use as possible and
have as few external tools/ dependencies necessary as possible.
Additionally, both words, error and undefined, convey different meanings
than todo does.
You can also define "todo = error" and add a HLint warning to your "todo".
|
Why |
@rhendric I can answer this question from a supply chain perspective: Because dependencies are not free. A dependency graph is something that will ask time, sometimes money, and overall energy to maintain. One more dependency to vet is one more maintainer to trust and, should things go wrong, support. |
I think it belongs in base because it's extremely common to mark parts of your code as todo. |
A lot of good proposals here for small, easily-implemented-elsewhere functions get shot down because they don't meet the Fairbairn threshold. I don't really have a problem with the CLC deciding that this one is valid but I don't yet see the reasoning. If ‘dependencies aren't free’ and ‘many people want this’ are the only considerations, |
Additionally to the named above reasons there's also a technical reason that just came to my mind: add a library to cabal that just adds something that you need for debugging, remove the debugging part, now you have a cabal warning about unused packages; are you going to
every time you want to indicate code as not done? I don't think that's feasible. |
On Thu, 7 Mar 2024, Mango The Fourth wrote:
Additionally to the named above reasons there's also a technical reason
that just came to my mind: add a library to cabal that just adds
something that you need for debugging, remove the debugging part, now
you have a cabal warning about unused packages; are you going to
* add a new package to cabal
* reload your language server
* wait until it's up
* import Debug.Todo (or do cabal mixins, same deal)
* write down todo
every time you want to indicate code as not done? I don't think that's feasible.
You can see it as an advantage: By removing the 'todo' dependency, you
know for sure, that your code is free of 'todo's.
|
This is your monthly public service reminder that adding stuff to |
I imagine adding something like this to https://hackage-search.serokell.io/?q=%5Etodo+%3A%3A Lots of packages seem to use |
I don't think comments like this are constructive, it comes off as passive aggressive. @MangoIV has listed a number of reasons why they think |
On Thu, 7 Mar 2024, Morrow wrote:
This is your monthly public service reminder that adding stuff to base is not free either
(although it may be a cost paid by someone other than you).
I don't think comments like this are constructive, it comes off as passive aggressive.
I understood it as humorously told counterargument to "adding a new
package is not free" and wanted to give the same things to consider.
|
What often goes unsaid in appeals to the Fairbairn threshold is that there is a natural tension with another useful feature: discoverability. Many functions in Providing an "obvious" function can have great value when it shepherds users into Doing the Right Thing ™️ . Bonus points if the function in question is canonical, in some sense. Of course this has to be balanced with the downsides of increasing I was skeptical when I read the title as I was picturing the trivial |
From a POSIWID perspective, the purpose of Should we make incremental improvements to |
I appreciate the well-written and detailed proposal and I largely agree with the direction, but I'm very hesitant to add anything to |
Thank you, yes, that was exactly my intention. |
@Bodigrim I absolutely understand your issue with this and agree on the basic gripes with the idea of adding something “completely new” to However, consider that there’s nothing new here, really, except the warning pragma, the exact same implementation has been part of Additionally we really can only get the most out of this change if people actually use this. If the barrier to using it is any higher than the functions we don’t want users to use ( |
In what way are typed holes slow? |
I personally oppose adding this to |
Maybe, but we can evolve to this state gradually. If there are early adopters keen to use such function even if it is not in Changing the set of entities exported by default in every Haskell program is not a decision to be taken lightly. |
Great idea, in spirit! However I must agree with others who raise the "supply chain" concern. How about we add compiler warnings to |
Weak argument to me. Haskell is about reusing code. Long running haskell projects accumulate sometimes hundreds of direct dependencies. And now they crumble by adding one more? What are the arguments to add things to base? From what I see:
The proposed function is a neat idea and I can see some use for it in education. But in the end it just feels like a synonym for |
I have justified above (but I should probably also add it to the proposal itself, pending) why I don’t think adding warnings to undefined and error themselves is a good idea. Adding warnings to these long used functions is probably an infinitely more breaking change than adding a new function that is used only 20 times in hackage, often for the same reason as this proposal intends. |
@hasufell |
I don't think so. The Warnings can and already do change. And we have mechanisms to introduce new warnings. See -Wcompat. I do not consider those breaking changes. Recent discussions in GHC SC proposals such as 625 have refined the understanding of these warning categories and transition procedures. |
I don't think we have any numbers. What purpose would that data serve? |
Dear CLC members, let's vote on the proposal to add a new module @tomjaguarpaw @hasufell @mixphix @angerman @parsonsmatt @velveteer +1 from me, I think it strikes right balance. |
-1, typed holes provide more information, only slightly slow down the language server, and are shorter to type. |
I think this is a well put together proposal. And the main reason I find it interesting is And yet, I'm unsure how this fits in the existing realm of typed holes (should it maybe be an implicit typed hole with I also realized both methods suffer from frequent "Ambiguous type variable" errors. Try to use https://gitlab.haskell.org/ghc/ghc/-/issues/25115#note_577782 suggests that this type of functionality needs ad-hoc logic in GHC too. So I'm wondering whether this should be turned into a language extension instead or just become a part of existing typed holes. Remember: once we added it to base, it can't really be removed easily. As such I suggest to give this more iterations, maybe add the current implementation to -1 (for now) |
thank you for your elaboration @hasufell. @BinderDavid has already made a nice suggestion wrt this idea:
This is a link to the corresponding comment
wrt this:
whether adding custom logic is necessary is not too clear to me, see this: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12934#note_578239 |
-1 I would like to credit @MangoIV for putting together an excellent proposal with a very clear motivation. My personal bar for adding new things However, the proposal falls down in one major respect: the claim that typed holes are not good enough. @merijn summarizes my thoughts about this precisely:
I do not currently see why it's better to use import Develop.Placeholder (todo)
...
TODO -> todo than {-# OPTIONS_GHC -fdefer-typed-holes -Wwarn=typed-holes #-}
...
_ -> _ The claim is that "typed holes are slow". I asked in what way they are slow and received no response. Therefore I have no evidence on which to base a judgement that this proposal is better than typed holes. (FWIW, if typed holes really are slow then that seems like something that should be fixed in GHC rather than by adding a library workaround.) |
Those things aren't really the same.
Compare with case foo of
Constr1 -> True
Constr2 -> False
TODO -> f == x
_ -> False Here the |
I agree they are not the same. I did not say they were the same! FWIW you can do {-# OPTIONS_GHC -fdefer-typed-holes -Wwarn=typed-holes -Wwarn=overlapping-patterns #-}
case foo of
Constr1 -> True
Constr2 -> False
_TODO -> f == x
_ -> False |
How do typed holes work in the following sort of case vs TODO? case foo of
Constr1 -> True
Constr2 -> False
TODO -> f == x There isn't a redundant pattern match so that wouldn't be warned against. I would contend that TODO is more ergonomic than typed holes in development because with typed holes you have to remember to turn on and off the related warnings and such, meaning forgetting to do so results in bad code being left in, meanwhile a todo will continue warning unless you're explicitly turning it off, in which case you accept the consequences there. |
Maybe I'm just unreasonably lazy, but imo shuffling warnings is pretty unergonomic. If adding/removing warnings to modules/.cabal files is part of a dev tool's workflow, I'm just not going to bother. I'd rather stick to |
It really does seem like it would be ideal for this to be in Prelude, to eliminate trivial inconveniences impeding its use. It makes sense that that shouldn't be done yet for backwards compat reasons, but getting it into base could be a step towards that one day. So it seems worth also thinking about about how {-# OPTIONS_GHC -fdefer-typed-holes -Wwarn=typed-holes #-}
...
_ -> _ might one day compare against TODO -> todo (ie, no import required) |
IME it's not the holes themselves, but GHC's attempts to find valid hole fits that what cause the slowdown. On codebases which use advanced features, I almost always set |
Exactly this. GHC finding valid hole fits or valid refinement holes is really slow, depending on the code base. IME they’re slow in almost all cases except the trivial ones. |
I agree!
I see, so there is a mitigation. These are all fair points against the typed holes alternative, but they should have been made before the vote was called. I asked five months ago. |
Please excuse me, @tomjaguarpaw, this is obviously on me, I should have answered this on time, I might have overlooked or forgotten to come back to this question. |
@tbidne I don't think that's unreasonably lazy, but I would like to point out that you can use So, the way I've been using it for years now is to append |
It's OK @MangoIV, you don't have anything to apologise for. It's your proposal and you're welcome to conduct it as you wish. There was plenty of discussion of typed holes. My question about slowness was only one component of that. When the vote was called I reread the entire discussion and I was not convinced by the arguments that the proposal was sufficiently better than typed holes to warrant inclusion in Still, I commend you for putting together an excellent proposal and I would be pleased to see someone put it into a stand-alone package. As I said, I take the point that having it in a separate package somewhat defeats the utility, but nonetheless I think it would be worth trialling "in the wild". At the very least I would encourage maintainers of "alternative preludes" to consider adopting it if it fits with their design ethos. There is clearly a lot of scope for improving the ergonomics in this area. |
+1 to this. Additionally, adding the Of course, this is really minor and workflow dependent, but I feel that it essentially encapsulates what I personally perceive the intent and semantics behind these two features to be, as well as pointing out one small limitation with |
After re-reading this a few times, I admit, I like the motivation a lot, but I'm just not convinced about including this in base. I'm not sure if the current base-split will help with finding alternative/less-permanent options. Hence, I'm -1 on this one in its current form. This is another one score for re-installable base, sigh. |
I'm +1. This is a nicer I can see a benefit to it landing in |
As far as I understood that's it then, thank you all for your consideration, discussion and finally the vote. I hope this can be revamped when making something like I don't think I will want to introduce something like the special cased
Generally, afaiu, it just increases complexity and shifts responsibility from the core libraries to the compiler which is even less agile. Again, thank you so much for the discussion and feedback and most importantly, your time. |
o7 @MangoIV Can I put a big +1 on "make a standalone package"? I think it would be cool to see how it goes in alternate preludes, so there's usage data when the issue is next raised? |
There is already a standalone package on ekmett/placeholder. It just needs to be published on hackage. |
This. That's why I never considered typed holes as an alternative for |
@MangoIV thanks for the proposal, I greatly appreciate all work and effort you put into it.
I'm not quite sure what you refer by "the While the fact that CLC votes against vox populi might be perceived by some as worrying, I strongly believe that it's not. Let me remind that everyone wishing to affect CLC decisions is most welcome to nominate themselves at the next CLC elections (which is January 2025) and help us together make the world a better place to write Haskell. |
It has now been published: https://hackage.haskell.org/package/placeholder Thanks to @MangoIV and @ekmett for making this idea into a package |
Dear Haskell core library committee.
Currently there are multiple ways of describing something as unimplemented
Relude
placeholders or the todo packageproblems of the above solutions
base
:undefined
:error
:todo
, it doesn't seem worth adding an alternativePrelude
or even add a dependency just for the sake of thisThat's why propose a function
todo
that has the following properties:"todo remains in code"
in groupx-todo
so that it can be used withWError
for e.g. CIundefined
base:Prelude
implementation of the solution
try it out in a separate module, as such:
This implementation will work from
>= ghc981
impact
on hoogle I currently find 4 functions which this would break, two of which have similar semantics to this one, making it improbable that they will be found in code out there.
Another advantage of this function is that there will be no need of a
*-compat
package because code that does contain this function is not supposed to live anywhere or compile with more than the compiler than thebase
version this is going to be shipped with supports.I will obviously run a proper impact assessment though.
why I think this belongs in
Prelude
The main reason I think this belongs into
Prelude
is because it is not possible to replace this with the same level of simplicity with any other solutionmixins
or add new imports; Additionally already having to add an additional dependency to use this, would, I think, be too much effort for many people to use itGHC
, they are not supported first class byGHC
, with the advent of the{-# WARNING -#}
we have first class support for these kinds of things that don't add any additional overheaderror
andundefined
don't haveWARNING
s attached to them and I don't think they should have; they have been as they are for a long time, it would impose a great cost to change their semantics; even though it is debatable whether you should, people useundefined
in their code to signify unreachable or permanently unimplemented parts of their code, if we want to add a warning, I think it should be on something that clearly tells the programmer and the reader of the code "this is not done yet"I think this will also have the positive impact of offering a real alternative to dangerous uses of
undefined
also look at
rust std's
todo!
andunimplemented!
macrosrendered docs
some more screenshots
how it looks, loading a module using
todo
intoGHCi
:how it looks when trying to load a module using
todo
with{-# OPTIONS_GHC -Werror=x-todo #-}
intoGHCi
:Amendment to this proposal (28 Mar 2024)
After what I consider a very productive and diverse discussion, I think the most prominent conclusion is that this might not make the jump into
Prelude
all at once. I still want to proceed with this proposal for the following two reasons:base
Prelude
eventually; this seems to be only possible when something has been inbase
for a long time and one has to start at some point ;)a new module,
Debug.Placeholder
Develop.Placeholder
There will be a new module
Debug.Placeholder
Develop.Placeholder
that will contain both thetodo
function as described above and implemented in ekmett'splaceholder
library (thank you for throwing this together) (except some improvements I will propose wrt type applications)TODO
pattern synonym as is implemented in theplaceholder
library (except some improvements I will propose wrt type applications)These implementations include many useful improvements to the function described above, which is really awesome.
The name of the module is justified as follows:
Control
or the like because it's only temporarily, you do something with the definitions in the process of programming, but don't really intend to keep the definitionsDebug
is the namespace that I think most fits the semantics of these functions intuitively, after all,todo
is something you insert in the code while "writing code" which is closest to "debugging", I'd say@tbidne has proposed the namespace
Develop
which I think is the most fitting until nowNote: if people think that the proposed namespace is incorrect, I would like to hear convincing arguments and am happy to adjust accordingly.
out of scope for this proposal
While there were some really nice suggestions to make the proposal "more complete", I will consider the following out of scope for this proposal while expressing the strong intention to later add them in a follow-up proposal:
{u,U}nimplemented
function and patterns: the reason why I want to avoid them for now is that they will spark additional discussions on whether you really need both of these and what the semantics of them should really be, whether they require warnings, etc.{unimplemented,todo}IO
variations of these: the reason why I want to avoid these is that I think it will spark discussion on whether we will need these in the case of onlytodo
existing, as it may never remain in codeDevelop
that will export many useful functions that you need only for work-in-progress code, such as thetodo
family of functions as well as thetrace*
family of functionsThe text was updated successfully, but these errors were encountered: