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
WIP: Case bind #327
base: master
Are you sure you want to change the base?
WIP: Case bind #327
Conversation
Wasn’t there a way to be able to do
so that indentation doesn't grow? (Not equivalent when the order of pattern matters) But maybe I am mistaken… or maybe that’s only the case in GHC-style code with explicit |
This is currently motivated in terms of handling "failure cases", but do I understand correctly that it's a solution to the very common annoyance of having to bind the result of a monadic action to a name before you can That is, where our current alternatives are: result <- checkThing
case result of
True -> yesItIs
False -> noItIsn't or checkThing >>= \case
True -> yesItIs
False -> noItIsn't this would add the possibility of: case <- checkThing of
True -> yesItIs
False -> noItIsn't If so, I like it. It suggests the analogous thing for if <- checkThing
then yesItIs
else noItIsn't |
Is this the same as Agda's do notation pattern matching? They use:
I think it might be worth referencing that. https://agda.readthedocs.io/en/v2.5.4.1/language/syntactic-sugar.html If it's not this, then please ignore this comment! |
@glaebhoerl raised a good point which alerted me to my example being wrong.
@nomeata There is There is also the similar extension which makes monsters like I dislike both of those for complicating layout, but @glaebhoerl Ah thanks, you made me realize my example was wrong. Fixed now. I will include the [If anything I would advocated for a @ocharles Yes it is! I vaguely Idris did something like this, but couldn't find the docs so and moved on. I guess it was Agda I was mis-remembering. |
@glaebhoerl Oh but you do need the pattern before the |
John made me aware of this proposal after I came up with something similar in #319 (comment). I like it! Maybe it would serve as further motivation to add the particular
I'm afraid that won't work as expected, as you would still need to add an (albeit unreachable) continuation after the blah = do
case _ <- checkThing of
True -> yesItIs
False -> noItIsn't
error "unreachable" The proposed extension is strictly about do blocks and morally like a Since the semantics we want is pretty clear, what follows is bikeshedding the syntax.
Interesting, I didn't know about that! The syntax seems a bit cleaner than the proposed one, although I'm not sure if we can implement it unambiguously.
Me too. And now I found it: http://docs.idris-lang.org/en/latest/tutorial/interfaces.html#pattern-matching-bind. Maybe we can implement something similar? Out of all the alternatives so far, I like that syntax the most. E.g. do
Good0... <- action0
| Bad0_0... -> ...
| Bad0_1... -> ...
Good1... <- action1
| Bad1_0... -> ...
| Bad1_1... -> ...
... But do
Good0... <- action0
| Bad0_0... | even blah -> ...
Good1... <- action1
| Bad1_0... -> ...
| Bad1_1... -> ...
... No, it seems that we should keep Summary:
|
It's probably not possible, but if could pick names out of a hat, I'd pick do
guard Good0 <- action0
where
Bad0_0 ... -> |
I really like this proposal and am thinking about adopting it. The only real open question to me is the syntax. I think I accurately summarised the different ideas in #327 (comment). I'm quite attached to the terse Unfortunately, I don't think @chreekat's |
Is it clear what one should write if there are two "good" constructors? I can't see how I would proceed. |
In that case I'd simply use the constructs available today, e.g. a |
I believe that this proposal is suggested as a more general alternative to failable patterns in do notation. That's great! I'm strongly in favour of replacing syntax with more general syntax as long as the old syntax is thoroughly deprecated. If we keep accreting syntax then Haskell gets more and more complicated. It's a big headache for anyone who doesn't live in the language every day. Alternatively, have we thoroughly fleshed out the library level solutions to this particular problem? For example, suppose we had import Optics
match prism handler v = case matching prism v of
Right r -> pure r
Left l -> handler l Then we could write do
...
v <- fetchData >>= match _Just \case
Just j -> absurd j
-- ^ Annoying that the type checker
-- can't prove this impossible
_ -> error "Not in map"
pure (v + 1) (requires No new syntax needed! Still, there are a few downsides of this "library" version when compared to the "syntax" version:
But I think it's worth asking the question: are we thoroughly convinced that there is no solution to this problem unless we introduce new syntax? |
@tomjaguarpaw Prisms don't work with patterns which expose the existential type variables, which is a motivation for failable patterns. So your approach doesn't solve the problem with |
@tomjaguarpaw Yes @sgraf812 and I both dislike today's fallible patterns in do notation, and #319 is a proposal to make an (anti-)extension to get rid of it, that is certainly deemed block on there being no replacement like that. I firmly agree we must not not keep on adding more complexity without cleaning things up, too. |
By the way, @tomjaguarpaw, the current "best practice" on error handling (with debatable looks) in do blocks within GHC is to use explicit layout + do { ...
; read_result <- case (wantHiBootFile home_unit eps mod from) of
Failed err -> return (Failed err)
Succeeded hi_boot_file -> do
hsc_env <- getTopEnv
liftIO $ computeInterface hsc_env doc_str hi_boot_file mod
; case read_result of {
Failed err -> do
{ let fake_iface = emptyFullModIface mod
; updateEps_ $ \eps ->
eps { eps_PIT = extendModuleEnv (eps_PIT eps) (mi_module fake_iface) fake_iface }
-- Not found, so add an empty iface to
-- the EPS map so that we don't look again
; return (Failed err) } ;
Succeeded (iface, loc) ->
let
loc_doc = text loc
in
initIfaceLcl (mi_semantic_module iface) loc_doc (mi_boot iface) $ It becomes really hard to see where the early return is happening. For example the first Edit: I agree we need to put more of these examples in the proposal. So maybe we should polish it first before asking for any buy-in from users/steering committee. |
Well, I guess the next step after #319 is rejected is to finish up this one. |
I honestly find this syntax very confusing. I had to read the example multiple times to see what's going on. Normal On first sight, it seems like one should just use something like |
@konsumlamm, I believe that your objection is addressed in the second example of the Motivation isn't it? Still, like you I am skeptical that this new syntax pulls its weight. |
I am happy to change it, very not attached to how it's written today.
If people used monad transformers in a very fine grained way, I would agree. But people tend to lug around big stacks in a very coarse-grained way, so I am not sure it is good. In particular stuff like MTL's |
@tomjaguarpaw I am not super excited about adding more syntax either, but it appears to be this is the way way anything like |
If I am understanding this proposal correctly it entangles two separate concerns:
I don't see why these two concerns should be mixed. @glaebhoerl seems to be talking in favour of 2 without mentioning 1 as important. Yet 1 could very well be useful in pure code and this proposal doesn't support it, as far as I can tell. For a concrete example of 1, consider the hypothetical syntax
desugared as
Now this is a generic way of avoiding awkward nesting problems in all code. It does not have the unfortunate weakness of being restricted to |
I'm also not very enthusiastic about this feature yet, but that won't stop me from throwing out shower thoughts about possible syntax. How about “trailing else”
(vaguely inspired by python |
@tomjaguarpaw I see what you mean, but I guess subjectively I think nesting is not a problem for "normal expressions", and only |
I can see why that would be a popular point of view, but personally I'm moving more towards using Haskell in the "best imperative language" style, using "sequences of let expressions", rather than where clauses that jump control flow all around the page. See, for example, my Python-to-Haskell translation at https://discourse.haskell.org/t/help-with-purses-problem/3883/7. If this proposal worked for "regular case", rather than only "case inside |
@tomjaguarpaw perhaps you and @sgraf812 should take this proposal from me? @sgraf812 was already more enthusiastic about the idea. And if there is a variant that makes you enthusiastic that would be good, too. I don't see myself being super enthuasiastic about any variation so I am probably better of supporting someone who is. And if these was something you both were enthusiastic about that would be very good indeed. |
Sorry if my comments are motivation-reducing, that wasn't my intention. Personally I'm more supportive of no proposal than any particular proposal. I just can't envisage Haskell's syntax getting better piecemeal. I think we need a ground-up rethink of all the syntax we value, the syntax we don't, and the best way to fit everything we value together. But that's a multi-year effort, probably. |
@tomjaguarpaw I am quite sympathetic to that. I wrote #442 in hopes that we can accumulate a longer and more comprehensive plan than we can with isolated proposals, combined as a difficult mentally exercise. I suppose I would be curious to hear your thoughts on that over that. |
Provide a more concise why to handle failure patterns in do notation.
To be honest, I am writing this mainly to put #319 in context. I would certainly use this sugar all the time, but I am just more excited about shrinking the language via deprecation than growing it with via sugar.
Rendered