From 464836d217c23fe2cf6970389e1335cc2c52e74e Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 3 Sep 2018 15:12:33 +0100 Subject: [PATCH 001/255] Add file with my personal log --- scratch/docs/Log.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 scratch/docs/Log.md diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md new file mode 100644 index 0000000000..0756cbfacb --- /dev/null +++ b/scratch/docs/Log.md @@ -0,0 +1,15 @@ +# F# Contribtions Log + +## 2018-09-03 +Got set up with Toby's help. I put together his key points below. There are a few different getting started guides that I can find, but Toby pointed me to [DEVGUIDE.md](https://github.com/Microsoft/visualfsharp/blob/master/DEVGUIDE.md) which seems to be the most helpful. + +### Notes for Getting Started +1. git clone https://github.com/Microsoft/visualfsharp - There are multiple mirrors, forks, ports, etc, but this is the one you fork and make changes to. There are dev branches, but you probably want to branch from master is this is a new feature. You'll find `DEVGUIDE.md` in the root of that repo. +1. To build, you'll want to run the `build.cmd` script (it sets up some env vars etc, then kicks off msbuild) in the root of the repo, **but** you don't want to run it with `cmd`! You'll want to use the "Developer Command Prompt for VS 2017" and run that as admin. The F# contributors generally seem to refer to this as just "the admin prompt" so don't be confused by that. On my machine, I can find the developer prompt by hitting the Windows key and typing it's name. No doubt you can find it alongside your VS install if Windows search disappoints. +1. Before you can open the F# compiler in Visual Studio, you'll probably to grab a few more SDKs and targeting packs. The F# compiler is multi-target, that is, you can build binaries with the compiler that target a different framework version to that used to build the compiler itself. As such, to build the compiler, you need to pull in a ton of extra framework stuff. It was an additional 3GB or so for me. I used Windows search to find "Visual Studio Installer", hit "Modify" on my VS install, opened the "Individual Components" tab and grabbed _everything_ under the ".NET" heading. +1. Now you can try opening `FSharp.sln`, which you'll find in the root of the repo. You'll also see `VisualFSharp.sln` file in there, but that includes the Visual Studio integrations etc, I think, and is much bigger. You probably won't need it if you're working on core compiler stuff. +1. To test making changes to the compiler and explore what it does, Toby recommended the following: + * Create an example `.fsx` file to build whatever you are insterested in + * Set `Fsc` as the start-up project, with your `.fsx`'s path as its only argument + * Run `Fsc` from inside VS, with will build only the dependencies you require, then will kick off compilation of your script. + * Debug your version of the compiler or whatever to see what's going on (in my case, I have a small computation expression builder which includes various custom operators, etc) From 0627b6e3a2aa5e08b2a7538149235948fe460450 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 3 Sep 2018 15:49:02 +0100 Subject: [PATCH 002/255] Add list of relevant leads to follow --- scratch/docs/Log.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 0756cbfacb..2f8765357f 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -13,3 +13,9 @@ Got set up with Toby's help. I put together his key points below. There are a fe * Set `Fsc` as the start-up project, with your `.fsx`'s path as its only argument * Run `Fsc` from inside VS, with will build only the dependencies you require, then will kick off compilation of your script. * Debug your version of the compiler or whatever to see what's going on (in my case, I have a small computation expression builder which includes various custom operators, etc) + +### Interesting Jumping Off Points +* The `MatchBang` implementation pull request +* `TcComputationExpression` in `TypeChecker.fs` +* `LetOrUseBang` usages +* `fsharpqafiles` - how does this all get picked up for running the tests? \ No newline at end of file From c9e22bac0cf39306bb5e02bb2e5d9b1733842b89 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 3 Sep 2018 15:55:10 +0100 Subject: [PATCH 003/255] Link to relevant docs for CE contribs --- scratch/docs/Log.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 2f8765357f..52fff2b0e9 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -15,7 +15,14 @@ Got set up with Toby's help. I put together his key points below. There are a fe * Debug your version of the compiler or whatever to see what's going on (in my case, I have a small computation expression builder which includes various custom operators, etc) ### Interesting Jumping Off Points -* The `MatchBang` implementation pull request +* Docs (in no particular order) + * Toby's thesis + * https://github.com/Microsoft/visualfsharp/blob/master/DEVGUIDE.md + * https://github.com/fsharp/fslang-suggestions/issues/579 + * https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions + * https://fsharp.github.io/2015/09/29/fsharp-compiler-guide.html + * https://fsharp.github.io/2016/09/26/fsharp-rfc-process.html +* The `MatchBang` implementation pull request - https://github.com/Microsoft/visualfsharp/pull/4427 * `TcComputationExpression` in `TypeChecker.fs` * `LetOrUseBang` usages * `fsharpqafiles` - how does this all get picked up for running the tests? \ No newline at end of file From d99f45a979eaecf8a81361f4e44df4d6575b06e5 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 12:10:56 +0100 Subject: [PATCH 004/255] Start log for 2018-09-04, mainly recording prior art atm --- scratch/docs/Log.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 52fff2b0e9..2662fb8e67 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -15,14 +15,27 @@ Got set up with Toby's help. I put together his key points below. There are a fe * Debug your version of the compiler or whatever to see what's going on (in my case, I have a small computation expression builder which includes various custom operators, etc) ### Interesting Jumping Off Points -* Docs (in no particular order) +* Docs (in no particular order): * Toby's thesis * https://github.com/Microsoft/visualfsharp/blob/master/DEVGUIDE.md * https://github.com/fsharp/fslang-suggestions/issues/579 + * https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/computation-zoo.pdf + * http://www.staff.city.ac.uk/~ross/papers/Applicative.pdf * https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions * https://fsharp.github.io/2015/09/29/fsharp-compiler-guide.html * https://fsharp.github.io/2016/09/26/fsharp-rfc-process.html * The `MatchBang` implementation pull request - https://github.com/Microsoft/visualfsharp/pull/4427 * `TcComputationExpression` in `TypeChecker.fs` * `LetOrUseBang` usages -* `fsharpqafiles` - how does this all get picked up for running the tests? \ No newline at end of file +* `fsharpqafiles` - how does this all get picked up for running the tests? + +## 2018-09-04 +Explored the compiler, documentation and surrounding literature. + +[Tomas Petricek's "The F# Computation Expression Zoo"](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/computation-zoo.pdf), linked by Don Syme in the `let! ... and! ...` fslang suggestion is very helpful because it defines much of the core of the change very clearly (see part 3, "Semantics of computation expressions", and part 4.4). Interestingly, Petricek seems to prefer the less common "Monoidal" definition of applicatives (see Part 4 of McBride & Patersons's ["Applicative programming with effects"](http://www.staff.city.ac.uk/~ross/papers/Applicative.pdf) and subsequent [explanations by people on the internet](https://argumatronic.com/posts/2017-03-08-applicative-instances.html)) that defines `map` and `merge` rather than `pure` and `apply`. I am starting to wonder now... should we allow `let! ... and! ...` when the user has only defined bind? I suppose it'd allow for future implementations of `pure` & `apply` / `map` & `merge` to make it more efficient (providing the relvant laws relating monads and applicatives hold). + +So now we have prior art for some of both the theory (papers from Petricek and McBride & Patterson on applicatives) and the practice (`match!` pull request, Petricek's - rejected! - earlier work on joinads). + +Questions: +* Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? +* Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? \ No newline at end of file From 4d4bffa70fe5283a741a6b1db6112ca6cf567b92 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 13:03:04 +0100 Subject: [PATCH 005/255] Expand on questions around monoidal applicative definition --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 2662fb8e67..c6809ecda4 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -37,5 +37,5 @@ Explored the compiler, documentation and surrounding literature. So now we have prior art for some of both the theory (papers from Petricek and McBride & Patterson on applicatives) and the practice (`match!` pull request, Petricek's - rejected! - earlier work on joinads). Questions: -* Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? +* Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? What is the signature of `merge` and how can I implement `apply` using that definition? The monoidal aspect is relevant because with the Option applicative, we need to define how we collapse `Some (Some x)`, the question, I suppose, is whether to make that aspect the explicit one (in my experience, it is the clearer way to do things). * Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? \ No newline at end of file From 5eac3e857c5ed8cf6a526d6d0d87623f2ab543d2 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 13:15:06 +0100 Subject: [PATCH 006/255] Guess at what the signature of merge is --- scratch/docs/Log.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index c6809ecda4..e5d1bb8c57 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -37,5 +37,6 @@ Explored the compiler, documentation and surrounding literature. So now we have prior art for some of both the theory (papers from Petricek and McBride & Patterson on applicatives) and the practice (`match!` pull request, Petricek's - rejected! - earlier work on joinads). Questions: -* Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? What is the signature of `merge` and how can I implement `apply` using that definition? The monoidal aspect is relevant because with the Option applicative, we need to define how we collapse `Some (Some x)`, the question, I suppose, is whether to make that aspect the explicit one (in my experience, it is the clearer way to do things). +* Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? + * What is the signature of `merge` (maybe `(a -> b -> c) -> f a -> f b -> f c`? Although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition) and how can I implement `apply` using that definition? The monoidal aspect is relevant because with the Option applicative, we need to define how we collapse `Some (Some x)`, the question, I suppose, is whether to make that aspect the explicit one (in my experience, it is the clearer way to do things). * Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? \ No newline at end of file From 2d9457f897fbb4c8a5994e98196a4a0123ee8958 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 13:18:59 +0100 Subject: [PATCH 007/255] Consider how merge/apply is still a useful addition even when bind is defined --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index e5d1bb8c57..a02e4d6c98 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -38,5 +38,5 @@ So now we have prior art for some of both the theory (papers from Petricek and M Questions: * Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? - * What is the signature of `merge` (maybe `(a -> b -> c) -> f a -> f b -> f c`? Although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition) and how can I implement `apply` using that definition? The monoidal aspect is relevant because with the Option applicative, we need to define how we collapse `Some (Some x)`, the question, I suppose, is whether to make that aspect the explicit one (in my experience, it is the clearer way to do things). + * What is the signature of `merge` (maybe `(a -> b -> c) -> f a -> f b -> f c`? Although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`) and how can I implement `apply` using that definition? * Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? \ No newline at end of file From 499139bc514f7d6b09c04b09df466fa4661d967d Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 13:20:40 +0100 Subject: [PATCH 008/255] Remove crufty note that I intended to delete earlier --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index a02e4d6c98..40dabf38c1 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -38,5 +38,5 @@ So now we have prior art for some of both the theory (papers from Petricek and M Questions: * Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? - * What is the signature of `merge` (maybe `(a -> b -> c) -> f a -> f b -> f c`? Although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`) and how can I implement `apply` using that definition? + * What is the signature of `merge` as Petricek intends it? (maybe `(a -> b -> c) -> f a -> f b -> f c`? Although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`) * Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? \ No newline at end of file From 33751fca0d35c8791fb229d99e9f0f58c535df67 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 13:22:16 +0100 Subject: [PATCH 009/255] Add .fsx with an builder in it as an example --- scratch/data/SimpleBuilder.fsx | 125 +++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 scratch/data/SimpleBuilder.fsx diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx new file mode 100644 index 0000000000..3fe083356c --- /dev/null +++ b/scratch/data/SimpleBuilder.fsx @@ -0,0 +1,125 @@ +// TODO Work towards defining an option applicative builder here + +// Computations that can be run step by step +type Eventually<'T> = + | Done of 'T + | NotYetDone of (unit -> Eventually<'T>) + +module Eventually = + // The bind for the computations. Append 'func' to the + // computation. + let rec bind func expr = + match expr with + | Done value -> NotYetDone (fun () -> func value) + | NotYetDone work -> NotYetDone (fun () -> bind func (work())) + + // Return the final value wrapped in the Eventually type. + let result value = Done value + + type OkOrException<'T> = + | Ok of 'T + | Exception of System.Exception + + // The catch for the computations. Stitch try/with throughout + // the computation, and return the overall result as an OkOrException. + let rec catch expr = + match expr with + | Done value -> result (Ok value) + | NotYetDone work -> + NotYetDone (fun () -> + let res = try Ok(work()) with | exn -> Exception exn + match res with + | Ok cont -> catch cont // note, a tailcall + | Exception exn -> result (Exception exn)) + + // The delay operator. + let delay func = NotYetDone (fun () -> func()) + + // The stepping action for the computations. + let step expr = + match expr with + | Done _ -> expr + | NotYetDone func -> func () + + // The rest of the operations are boilerplate. + // The tryFinally operator. + // This is boilerplate in terms of "result", "catch", and "bind". + let tryFinally expr compensation = + catch (expr) + |> bind (fun res -> + compensation(); + match res with + | Ok value -> result value + | Exception exn -> raise exn) + + // The tryWith operator. + // This is boilerplate in terms of "result", "catch", and "bind". + let tryWith exn handler = + catch exn + |> bind (function Ok value -> result value | Exception exn -> handler exn) + + // The whileLoop operator. + // This is boilerplate in terms of "result" and "bind". + let rec whileLoop pred body = + if pred() then body |> bind (fun _ -> whileLoop pred body) + else result () + + // The sequential composition operator. + // This is boilerplate in terms of "result" and "bind". + let combine expr1 expr2 = + expr1 |> bind (fun () -> expr2) + + // The using operator. + let using (resource: #System.IDisposable) func = + tryFinally (func resource) (fun () -> resource.Dispose()) + + // The forLoop operator. + // This is boilerplate in terms of "catch", "result", and "bind". + let forLoop (collection:seq<_>) func = + let ie = collection.GetEnumerator() + tryFinally + (whileLoop + (fun () -> ie.MoveNext()) + (delay (fun () -> let value = ie.Current in func value))) + (fun () -> ie.Dispose()) + +// The builder class. +type EventuallyBuilder() = + member x.Bind(comp, func) = Eventually.bind func comp + member x.Return(value) = Eventually.result value + member x.ReturnFrom(value) = value + member x.Combine(expr1, expr2) = Eventually.combine expr1 expr2 + member x.Delay(func) = Eventually.delay func + member x.Zero() = Eventually.result () + member x.TryWith(expr, handler) = Eventually.tryWith expr handler + member x.TryFinally(expr, compensation) = Eventually.tryFinally expr compensation + member x.For(coll:seq<_>, func) = Eventually.forLoop coll func + member x.Using(resource, expr) = Eventually.using resource expr + +let eventually = new EventuallyBuilder() + +let comp = eventually { + for x in 1..2 do + printfn " x = %d" x + return 3 + 4 } + +// Try the remaining lines in F# interactive to see how this +// computation expression works in practice. +let step x = Eventually.step x + +// returns "NotYetDone " +comp |> step + +// prints "x = 1" +// returns "NotYetDone " +comp |> step |> step + +// prints "x = 1" +// prints "x = 2" +// returns "NotYetDone " +comp |> step |> step |> step |> step |> step |> step + +// prints "x = 1" +// prints "x = 2" +// returns "Done 7" +comp |> step |> step |> step |> step |> step |> step |> step |> step \ No newline at end of file From c12b1307def3fc729d8e72e624ad86252e5a1cc8 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 13:34:01 +0100 Subject: [PATCH 010/255] Record Don Syme's definition of merge from the fslag suggestion --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 40dabf38c1..389673c713 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -38,5 +38,5 @@ So now we have prior art for some of both the theory (papers from Petricek and M Questions: * Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? - * What is the signature of `merge` as Petricek intends it? (maybe `(a -> b -> c) -> f a -> f b -> f c`? Although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`) + * `merge : f a * f b -> f (a*b)` (previously I was wondering about `liftA2 : (a -> b -> c) -> f a -> f b -> f c`, although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`) * Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? \ No newline at end of file From eb7a701cb831e07de39df90bf6c27df50d6b8441 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 13:36:14 +0100 Subject: [PATCH 011/255] Moan about tuples being inelegant --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 389673c713..367d8d26eb 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -38,5 +38,5 @@ So now we have prior art for some of both the theory (papers from Petricek and M Questions: * Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? - * `merge : f a * f b -> f (a*b)` (previously I was wondering about `liftA2 : (a -> b -> c) -> f a -> f b -> f c`, although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`) + * `merge : f a * f b -> f (a*b)` which is a bit odd because it involves creating a tuple, which doesn't seem very elegant (previously I was wondering about `liftA2 : (a -> b -> c) -> f a -> f b -> f c`, although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`) * Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? \ No newline at end of file From 4cabb6b4015a7dc6ad82c1984561985fec20fd2b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 13:49:51 +0100 Subject: [PATCH 012/255] Performance implications of merge --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 367d8d26eb..024987748e 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -38,5 +38,5 @@ So now we have prior art for some of both the theory (papers from Petricek and M Questions: * Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? - * `merge : f a * f b -> f (a*b)` which is a bit odd because it involves creating a tuple, which doesn't seem very elegant (previously I was wondering about `liftA2 : (a -> b -> c) -> f a -> f b -> f c`, although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`) + * `merge : f a * f b -> f (a*b)` which is a bit odd because it involves creating a tuple, which doesn't seem very elegant (does it imply allocating a tuple for each application of a value?!). Previously I was wondering about `liftA2 : (a -> b -> c) -> f a -> f b -> f c`, although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`. I guess we don't have curried functions when defining builders? I'll have to check. * Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? \ No newline at end of file From e8d82692fa9ca210ef877e6374d65c188f89e503 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 15:13:55 +0100 Subject: [PATCH 013/255] Clarify some of my thoughts on applicative CEs --- scratch/docs/Log.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 024987748e..e49cf330ce 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -19,8 +19,10 @@ Got set up with Toby's help. I put together his key points below. There are a fe * Toby's thesis * https://github.com/Microsoft/visualfsharp/blob/master/DEVGUIDE.md * https://github.com/fsharp/fslang-suggestions/issues/579 + * https://github.com/fsharp/fslang-suggestions/issues/36 * https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/computation-zoo.pdf * http://www.staff.city.ac.uk/~ross/papers/Applicative.pdf + * http://delivery.acm.org/10.1145/2980000/2976007/p92-marlow.pdf * https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions * https://fsharp.github.io/2015/09/29/fsharp-compiler-guide.html * https://fsharp.github.io/2016/09/26/fsharp-rfc-process.html @@ -34,9 +36,10 @@ Explored the compiler, documentation and surrounding literature. [Tomas Petricek's "The F# Computation Expression Zoo"](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/computation-zoo.pdf), linked by Don Syme in the `let! ... and! ...` fslang suggestion is very helpful because it defines much of the core of the change very clearly (see part 3, "Semantics of computation expressions", and part 4.4). Interestingly, Petricek seems to prefer the less common "Monoidal" definition of applicatives (see Part 4 of McBride & Patersons's ["Applicative programming with effects"](http://www.staff.city.ac.uk/~ross/papers/Applicative.pdf) and subsequent [explanations by people on the internet](https://argumatronic.com/posts/2017-03-08-applicative-instances.html)) that defines `map` and `merge` rather than `pure` and `apply`. I am starting to wonder now... should we allow `let! ... and! ...` when the user has only defined bind? I suppose it'd allow for future implementations of `pure` & `apply` / `map` & `merge` to make it more efficient (providing the relvant laws relating monads and applicatives hold). -So now we have prior art for some of both the theory (papers from Petricek and McBride & Patterson on applicatives) and the practice (`match!` pull request, Petricek's - rejected! - earlier work on joinads). +So now we have prior art for some of both the theory (papers from Petricek and McBride & Patterson on applicatives) and the practice (`match!` pull request, existing `zip` custom operation, Petricek's - rejected! - earlier work on joinads). Questions: * Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? * `merge : f a * f b -> f (a*b)` which is a bit odd because it involves creating a tuple, which doesn't seem very elegant (does it imply allocating a tuple for each application of a value?!). Previously I was wondering about `liftA2 : (a -> b -> c) -> f a -> f b -> f c`, although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`. I guess we don't have curried functions when defining builders? I'll have to check. -* Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? \ No newline at end of file +* Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? +* Should we allow mixing applicatives and monads, as long as the rules that no RHS of an apply is the LHS of a `let!`? \ No newline at end of file From 702fce265f9be2408b804f310a7ed1e0693f356a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 15:24:02 +0100 Subject: [PATCH 014/255] Sketch a new example builder based on options --- scratch/data/SimpleBuilder.fsx | 157 +++++++-------------------------- 1 file changed, 34 insertions(+), 123 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 3fe083356c..182d949431 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -1,125 +1,36 @@ // TODO Work towards defining an option applicative builder here -// Computations that can be run step by step -type Eventually<'T> = - | Done of 'T - | NotYetDone of (unit -> Eventually<'T>) - -module Eventually = - // The bind for the computations. Append 'func' to the - // computation. - let rec bind func expr = - match expr with - | Done value -> NotYetDone (fun () -> func value) - | NotYetDone work -> NotYetDone (fun () -> bind func (work())) - - // Return the final value wrapped in the Eventually type. - let result value = Done value - - type OkOrException<'T> = - | Ok of 'T - | Exception of System.Exception - - // The catch for the computations. Stitch try/with throughout - // the computation, and return the overall result as an OkOrException. - let rec catch expr = - match expr with - | Done value -> result (Ok value) - | NotYetDone work -> - NotYetDone (fun () -> - let res = try Ok(work()) with | exn -> Exception exn - match res with - | Ok cont -> catch cont // note, a tailcall - | Exception exn -> result (Exception exn)) - - // The delay operator. - let delay func = NotYetDone (fun () -> func()) - - // The stepping action for the computations. - let step expr = - match expr with - | Done _ -> expr - | NotYetDone func -> func () - - // The rest of the operations are boilerplate. - // The tryFinally operator. - // This is boilerplate in terms of "result", "catch", and "bind". - let tryFinally expr compensation = - catch (expr) - |> bind (fun res -> - compensation(); - match res with - | Ok value -> result value - | Exception exn -> raise exn) - - // The tryWith operator. - // This is boilerplate in terms of "result", "catch", and "bind". - let tryWith exn handler = - catch exn - |> bind (function Ok value -> result value | Exception exn -> handler exn) - - // The whileLoop operator. - // This is boilerplate in terms of "result" and "bind". - let rec whileLoop pred body = - if pred() then body |> bind (fun _ -> whileLoop pred body) - else result () - - // The sequential composition operator. - // This is boilerplate in terms of "result" and "bind". - let combine expr1 expr2 = - expr1 |> bind (fun () -> expr2) - - // The using operator. - let using (resource: #System.IDisposable) func = - tryFinally (func resource) (fun () -> resource.Dispose()) - - // The forLoop operator. - // This is boilerplate in terms of "catch", "result", and "bind". - let forLoop (collection:seq<_>) func = - let ie = collection.GetEnumerator() - tryFinally - (whileLoop - (fun () -> ie.MoveNext()) - (delay (fun () -> let value = ie.Current in func value))) - (fun () -> ie.Dispose()) - -// The builder class. -type EventuallyBuilder() = - member x.Bind(comp, func) = Eventually.bind func comp - member x.Return(value) = Eventually.result value - member x.ReturnFrom(value) = value - member x.Combine(expr1, expr2) = Eventually.combine expr1 expr2 - member x.Delay(func) = Eventually.delay func - member x.Zero() = Eventually.result () - member x.TryWith(expr, handler) = Eventually.tryWith expr handler - member x.TryFinally(expr, compensation) = Eventually.tryFinally expr compensation - member x.For(coll:seq<_>, func) = Eventually.forLoop coll func - member x.Using(resource, expr) = Eventually.using resource expr - -let eventually = new EventuallyBuilder() - -let comp = eventually { - for x in 1..2 do - printfn " x = %d" x - return 3 + 4 } - -// Try the remaining lines in F# interactive to see how this -// computation expression works in practice. -let step x = Eventually.step x - -// returns "NotYetDone " -comp |> step - -// prints "x = 1" -// returns "NotYetDone " -comp |> step |> step - -// prints "x = 1" -// prints "x = 2" -// returns "NotYetDone " -comp |> step |> step |> step |> step |> step |> step - -// prints "x = 1" -// prints "x = 2" -// returns "Done 7" -comp |> step |> step |> step |> step |> step |> step |> step |> step \ No newline at end of file +[] +type OptionalBuilder = + member __.Bind(opt, binder) = + match opt with + | Some value -> binder value + | None -> None + member __.Return(value) = + Some value + +let optional = OptionalBuilder() + +let x = Some 1 +let y = Some "A" +let z : float option = None + +let foo = + optional + { + let! x' = x + let! y' = y + let! z' = z + return sprintf "x = %d, y = %s, z = %f" x y z + } + +(* +let foo' = + optional + { + let! x' = x + and! y' = y + and! z' = z + return sprintf "x = %d, y = %s, z = %f" x y z + } +*) \ No newline at end of file From f7a3ca723889abd37e0252234e43347c9c13c72b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 15:24:21 +0100 Subject: [PATCH 015/255] Add more questions about semantics --- scratch/docs/Log.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index e49cf330ce..678cdc3b20 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -42,4 +42,6 @@ Questions: * Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? * `merge : f a * f b -> f (a*b)` which is a bit odd because it involves creating a tuple, which doesn't seem very elegant (does it imply allocating a tuple for each application of a value?!). Previously I was wondering about `liftA2 : (a -> b -> c) -> f a -> f b -> f c`, although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`. I guess we don't have curried functions when defining builders? I'll have to check. * Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? -* Should we allow mixing applicatives and monads, as long as the rules that no RHS of an apply is the LHS of a `let!`? \ No newline at end of file +* Should we allow mixing applicatives and monads, as long as the rules that no RHS of an apply is the LHS of a `let!`? +* What should we do if someone tries to use `let! ... and! ...` if the builder only defines `Bind`? +* Is using `map` and `merge` perferable because we get `map` which gives an optimisation for some usages of existing monadic CEs? \ No newline at end of file From 96b6f0c498776748b334c5bff679bc7aaf972374 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 15:31:38 +0100 Subject: [PATCH 016/255] Sketch out example Map and Apply impls --- scratch/data/SimpleBuilder.fsx | 54 +++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 182d949431..1925b5f6e6 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -2,35 +2,47 @@ [] type OptionalBuilder = - member __.Bind(opt, binder) = - match opt with - | Some value -> binder value - | None -> None - member __.Return(value) = - Some value + + member __.Bind(opt, f) = + match opt with + | Some x -> f x + | None -> None + + // TODO Make this actually get called when `let! ... and! ...` syntax is used (and automagically if RHSs allow it?) + member __.Apply(x : 'a option, f : ('a -> 'b) option) : 'b option = + match f, x with + | Some f, Some x -> f x + | _ -> None + + // TODO Not needed, but for maximum efficiency, we want to use this if it is defined + member __.Map(x : 'a option, f : 'a -> 'b) : 'b option = + match x with + | Some x -> f x + | None -> None + + member __.Return(x) = + Some x -let optional = OptionalBuilder() +let opt = OptionalBuilder() let x = Some 1 let y = Some "A" let z : float option = None let foo = - optional - { - let! x' = x - let! y' = y - let! z' = z - return sprintf "x = %d, y = %s, z = %f" x y z - } + opt { + let! x' = x + let! y' = y + let! z' = z + return sprintf "x = %d, y = %s, z = %f" x y z + } (* let foo' = - optional - { - let! x' = x - and! y' = y - and! z' = z - return sprintf "x = %d, y = %s, z = %f" x y z - } + opt { + let! x' = x + and! y' = y + and! z' = z + return sprintf "x = %d, y = %s, z = %f" x y z + } *) \ No newline at end of file From bdd25cf48caa8e75a11855fc628af566fe6466e3 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 16:21:14 +0100 Subject: [PATCH 017/255] Clarify my current threads of research --- scratch/docs/Log.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 678cdc3b20..47b98cacc1 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -44,4 +44,8 @@ Questions: * Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? * Should we allow mixing applicatives and monads, as long as the rules that no RHS of an apply is the LHS of a `let!`? * What should we do if someone tries to use `let! ... and! ...` if the builder only defines `Bind`? -* Is using `map` and `merge` perferable because we get `map` which gives an optimisation for some usages of existing monadic CEs? \ No newline at end of file +* Is using `map` and `merge` perferable because we get `map` which gives an optimisation for some usages of existing monadic CEs? +* `map` & `merge` / `apply` vs. zip? + +Standard place for tests (see `match!` PR) seems to be: https://github.com/Microsoft/visualfsharp/tree/master/tests/fsharp/core +`match!` commit: https://github.com/Microsoft/visualfsharp/commit/e33831a596bc2858c9311280212b559318272ee4 From 085ceadf3c4fbe5170fab6cb0164e3dbc2b10aaf Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 4 Sep 2018 16:45:42 +0100 Subject: [PATCH 018/255] List next steps --- scratch/docs/Log.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 47b98cacc1..a771d8949c 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -45,7 +45,12 @@ Questions: * Should we allow mixing applicatives and monads, as long as the rules that no RHS of an apply is the LHS of a `let!`? * What should we do if someone tries to use `let! ... and! ...` if the builder only defines `Bind`? * Is using `map` and `merge` perferable because we get `map` which gives an optimisation for some usages of existing monadic CEs? -* `map` & `merge` / `apply` vs. zip? +* `map` & `merge` / `apply` vs. `zip`? Standard place for tests (see `match!` PR) seems to be: https://github.com/Microsoft/visualfsharp/tree/master/tests/fsharp/core `match!` commit: https://github.com/Microsoft/visualfsharp/commit/e33831a596bc2858c9311280212b559318272ee4 + +### Next Up +* Answer questions from today (I've already made a post to #langdesign) +* Try to understand the `match!` commit by reading/debugging +* Trying to change the `let!` syntax, e.g. to `foo!` and see it work \ No newline at end of file From fb0e56712e1db051da136d860b614b2ff8c67425 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 09:45:47 +0100 Subject: [PATCH 019/255] Start new day's log --- scratch/docs/Log.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index a771d8949c..143be98a65 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -53,4 +53,7 @@ Standard place for tests (see `match!` PR) seems to be: https://github.com/Micro ### Next Up * Answer questions from today (I've already made a post to #langdesign) * Try to understand the `match!` commit by reading/debugging -* Trying to change the `let!` syntax, e.g. to `foo!` and see it work \ No newline at end of file +* Trying to change the `let!` syntax, e.g. to `foo!` and see it work + +## 2018-09-04 +Still waiting on a response on #langdesign. \ No newline at end of file From bd2f21afa42c1d1be505c3fba31d0d930eacd577 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 10:00:25 +0100 Subject: [PATCH 020/255] List interesting files following match! PR --- scratch/docs/Log.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 143be98a65..8c5f3bde74 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -56,4 +56,6 @@ Standard place for tests (see `match!` PR) seems to be: https://github.com/Micro * Trying to change the `let!` syntax, e.g. to `foo!` and see it work ## 2018-09-04 -Still waiting on a response on #langdesign. \ No newline at end of file +Still waiting on a response on #langdesign. + +After reading the [Extend ComputationExpression builders with Map](https://github.com/fsharp/fslang-design/issues/258) discussion, I am wondering how `pure` will work. \ No newline at end of file From 9fdd2755d1bbd575a181f3f69709c1cb1b1faead Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 11:04:41 +0100 Subject: [PATCH 021/255] Add findings from match! and RFC process --- scratch/docs/Log.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 8c5f3bde74..351b7a1c9e 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -58,4 +58,16 @@ Standard place for tests (see `match!` PR) seems to be: https://github.com/Micro ## 2018-09-04 Still waiting on a response on #langdesign. -After reading the [Extend ComputationExpression builders with Map](https://github.com/fsharp/fslang-design/issues/258) discussion, I am wondering how `pure` will work. \ No newline at end of file +After reading the [Extend ComputationExpression builders with Map](https://github.com/fsharp/fslang-design/issues/258) discussion, I am wondering how `pure` will work. + +Parts of compiler to explore following on from the `match!` PR: +* src/fsharp/LexFilter.fs +* src/fsharp/TypeChecker.fs +* src/fsharp/ast.fs +* src/fsharp/lex.fsl +* src/fsharp/pars.fsy +* src/fsharp/service/ServiceLexing.fs +* src/fsharp/service/ServiceParseTreeWalk.fs +* src/fsharp/service/ServiceUntypedParse.fs + +Good news! The [RFC process](https://fsharp.github.io/2016/09/26/fsharp-rfc-process.html) isn't very heavyweight. \ No newline at end of file From e82ba1e0c1f4037fa80f996630d56a6845c520e0 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 12:17:57 +0100 Subject: [PATCH 022/255] Clean up notes about 'letmatch!' --- scratch/docs/Log.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 351b7a1c9e..b2288bc96e 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -58,6 +58,8 @@ Standard place for tests (see `match!` PR) seems to be: https://github.com/Micro ## 2018-09-04 Still waiting on a response on #langdesign. +Good news! The [RFC process](https://fsharp.github.io/2016/09/26/fsharp-rfc-process.html) isn't very heavyweight. + After reading the [Extend ComputationExpression builders with Map](https://github.com/fsharp/fslang-design/issues/258) discussion, I am wondering how `pure` will work. Parts of compiler to explore following on from the `match!` PR: @@ -70,4 +72,27 @@ Parts of compiler to explore following on from the `match!` PR: * src/fsharp/service/ServiceParseTreeWalk.fs * src/fsharp/service/ServiceUntypedParse.fs -Good news! The [RFC process](https://fsharp.github.io/2016/09/26/fsharp-rfc-process.html) isn't very heavyweight. \ No newline at end of file +### Messing with the `match!` syntax + +Interesting existing annoyance, no idea if it's easily fixable: I tried to use a `match!` to do some `printf`-ing, but got the "You haven't defined `Zero`" error. Okay, so I implement that, then I get the same for `Combine`. I'd have liked to have had all of the errors at once to avoid multiple build & run iterations. + +...Now I need a `Delay` too! 😄 + +Managed to mangle the new `match!` syntax to use `letmatch!` on a branch. + +```F# +let x = Some 1 +let y = opt { return "A" } +let z = Some 3.5 + +let foo : string option = + opt { + let! x' = x + letmatch! y with + | yValue -> printfn "y was set to %s!" yValue + let! z' = z + return sprintf "x = %d, z = %f" x' z' + } +``` + +The semantics are iff `y` is `None` then bomb out of the CE with `None` else continue down the CE, printing the "y was set..." stuff to `stdout`. \ No newline at end of file From 00f7f2efcfea00620a453b0581d206998657c039 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 14:55:53 +0100 Subject: [PATCH 023/255] Add list of things to explore for let! ... and! ... --- scratch/docs/Log.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index b2288bc96e..835098a795 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -95,4 +95,12 @@ let foo : string option = } ``` -The semantics are iff `y` is `None` then bomb out of the CE with `None` else continue down the CE, printing the "y was set..." stuff to `stdout`. \ No newline at end of file +The semantics are iff `y` is `None` then bomb out of the CE with `None` else continue down the CE, printing the "y was set..." stuff to `stdout`. + +### Have a go at adding a shonky `let! ... and! ...` + +Arbitrary list of things to do / remember: +* Update AST (not sure how the AST/TAST handle custom operators and built-in CE methods yet) +* Update TAST +* Add `and!` to the frontend +* Add logic for grabbing `.Apply` off the given builder and validating its signature etc (won't both with `.Map` or `.Merge` at this point because that's yet more functions to have to wire up, validate, etc) \ No newline at end of file From 92df296b6fd5952d94383cb16c328e92167652d1 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 15:07:13 +0100 Subject: [PATCH 024/255] Ponder `andUse!`-like construct --- scratch/docs/Log.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 835098a795..0290e965e2 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -103,4 +103,5 @@ Arbitrary list of things to do / remember: * Update AST (not sure how the AST/TAST handle custom operators and built-in CE methods yet) * Update TAST * Add `and!` to the frontend -* Add logic for grabbing `.Apply` off the given builder and validating its signature etc (won't both with `.Map` or `.Merge` at this point because that's yet more functions to have to wire up, validate, etc) \ No newline at end of file +* Add logic for grabbing `.Apply` off the given builder and validating its signature etc (won't both with `.Map` or `.Merge` at this point because that's yet more functions to have to wire up, validate, etc) +* Should there be an `andUse!` or something? What usecases could there be? What would that look like? \ No newline at end of file From 5753505805154ae9ac0d452bb3eabc8c27fb4bec Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 15:18:51 +0100 Subject: [PATCH 025/255] Sketch shonky and! addition to AST --- src/fsharp/ast.fs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index 1f0941eea4..d7f8e6cdf6 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -480,6 +480,14 @@ and +and + [] + SynAndBangExpr = + /// AndBang(isUse, bindings, body, wholeRange) + /// + /// F# syntax: and! pat = expr in expr (Must follow on directly from a let! / and!) + | AndBang of isUse:bool * bindings:SynBinding list * body:SynAndBangExpr * range:range + | EndOfAndBangChain of SynExpr and [] SynExpr = @@ -690,10 +698,11 @@ and /// SynExpr.LetOrUseBang(spBind, isUse, isFromSource, pat, rhsExpr, bodyExpr, mWholeExpr). /// - /// F# syntax: let! pat = expr in expr - /// F# syntax: use! pat = expr in expr + /// F# syntax: let! pat = expr in expr' + /// F# syntax: use! pat = expr in expr' + /// where expr' admits an immediate and! /// Computation expressions only - | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynExpr * range:range + | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range /// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN | MatchBang of matchSeqPoint:SequencePointInfoForBinding * expr:SynExpr * clauses:SynMatchClause list * isExnMatch:bool * range:range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *) From dc097e212222e5777ec34a194f8e869e87abbdd1 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 17:04:12 +0100 Subject: [PATCH 026/255] Sketch shonky lexing and parsing for and! --- src/fsharp/ast.fs | 4 ++-- src/fsharp/lex.fsl | 2 +- src/fsharp/pars.fsy | 14 ++++++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index d7f8e6cdf6..ec5baf4d78 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -486,8 +486,8 @@ and /// AndBang(isUse, bindings, body, wholeRange) /// /// F# syntax: and! pat = expr in expr (Must follow on directly from a let! / and!) - | AndBang of isUse:bool * bindings:SynBinding list * body:SynAndBangExpr * range:range - | EndOfAndBangChain of SynExpr + | AndBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range + | NotAndBang of SynExpr and [] SynExpr = diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index f3842f908e..392db3602b 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -243,7 +243,7 @@ rule token args skip = parse | ident '!' { let tok = Keywords.KeywordOrIdentifierToken args lexbuf (lexemeTrimRight lexbuf 1) match tok with - | LET _ -> BINDER (lexemeTrimRight lexbuf 1) + | LET _ | AND -> BINDER (lexemeTrimRight lexbuf 1) | _ -> fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("!")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) } | ident ('#') { fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("#")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) } diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 4d3cf24720..297179fcdc 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3297,20 +3297,26 @@ declExpr: | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) let m = unionRanges (rhs parseState 1) $7.Range - SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m) } + match $1 with + | "and" -> SynAndBangExpr.AndBang (spBind,false,true,$2,$4,$7,m) + | _ -> SynAndBangExpr.NotAndBang (SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m)) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP typedSeqExprBlock %prec expr_let - { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error + { $5 (match $1 with | "use" -> "use!" | "let" -> "let!" | "and" -> "and!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let m = unionRanges (rhs parseState 1) $7.Range - SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m) } + match $1 with + | "and" -> SynAndBangExpr.AndBang (spBind,false,true,$2,$4,$7,m) + | _ -> SynAndBangExpr.NotAndBang (SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m)) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) let m = $4.Range.EndRange // zero-width range - SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4, SynExpr.ImplicitZero m, mAll) } + match $1 with + | "and" -> SynAndBangExpr.AndBang (spBind,false,true,$2,$4, SynExpr.ImplicitZero m, mAll) + | _ -> SynAndBangExpr.NotAndBang (SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4, SynExpr.ImplicitZero m, mAll)) } | DO_BANG typedSeqExpr IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { let spBind = NoSequencePointAtDoBinding From 5125e444bc82e180eda1e7e17daa23e9c472f07a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 17:17:41 +0100 Subject: [PATCH 027/255] Comment on and! being its own expression type --- scratch/docs/Log.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 0290e965e2..f7dd954b9f 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -104,4 +104,30 @@ Arbitrary list of things to do / remember: * Update TAST * Add `and!` to the frontend * Add logic for grabbing `.Apply` off the given builder and validating its signature etc (won't both with `.Map` or `.Merge` at this point because that's yet more functions to have to wire up, validate, etc) -* Should there be an `andUse!` or something? What usecases could there be? What would that look like? \ No newline at end of file +* Should there be an `andUse!` or something? What usecases could there be? What would that look like? + +What I've done so far makes `and!` only valid within a `let!`: +```F# + [] + SynAndBangExpr = + /// AndBang(isUse, bindings, body, wholeRange) + /// + /// F# syntax: and! pat = expr in expr (Must follow on directly from a let! / and!) + | AndBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range + | NotAndBang of SynExpr +and + [] + SynExpr = + +(* ... *) + + /// SynExpr.LetOrUseBang(spBind, isUse, isFromSource, pat, rhsExpr, bodyExpr, mWholeExpr). + /// + /// F# syntax: let! pat = expr in expr' + /// F# syntax: use! pat = expr in expr' + /// where expr' admits an immediate and! + /// Computation expressions only + | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range +``` + +This is kind of what we want, but its type safety is actually a bit annoying - now `and!`s are a different expression type, and the normal functions, e.g. to test whether something is a control-flow expression, doesn't directly apply to it. I feel like this will need revisiting. \ No newline at end of file From e718b124157f97d801aea41fdfe03e51f8ae1ef1 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 17:24:03 +0100 Subject: [PATCH 028/255] Note that and!-sequences are like lists --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index f7dd954b9f..46d94d4c8e 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -130,4 +130,4 @@ and | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range ``` -This is kind of what we want, but its type safety is actually a bit annoying - now `and!`s are a different expression type, and the normal functions, e.g. to test whether something is a control-flow expression, doesn't directly apply to it. I feel like this will need revisiting. \ No newline at end of file +This is kind of what we want, but its type safety is actually a bit annoying - now `and!`s are a different expression type, and the normal functions, e.g. to test whether something is a control-flow expression, doesn't directly apply to it. I feel like this will need revisiting - will we want a `let!` followed by a list of `and!`s (see `listPatternElements` in `pars.fsy`)? \ No newline at end of file From 670c97aa0eb23d9e6c669edffade7c993738c4c9 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 5 Sep 2018 17:31:34 +0100 Subject: [PATCH 029/255] Add detail about modeling and!s as a list --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 46d94d4c8e..d98338ed2a 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -130,4 +130,4 @@ and | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range ``` -This is kind of what we want, but its type safety is actually a bit annoying - now `and!`s are a different expression type, and the normal functions, e.g. to test whether something is a control-flow expression, doesn't directly apply to it. I feel like this will need revisiting - will we want a `let!` followed by a list of `and!`s (see `listPatternElements` in `pars.fsy`)? \ No newline at end of file +This is kind of what we want, but its type safety is actually a bit annoying - now `and!`s are a different expression type, and the normal functions, e.g. to test whether something is a control-flow expression, doesn't directly apply to it. I feel like this will need revisiting - will we want a `let!` followed by a list of `and!`s (, i.e. the string "and!" could be like an expression list separator - see `listPatternElements` in `pars.fsy`, started with a "let!" and terminated with a "return". If the list is non-empty => its an applicative expression)? \ No newline at end of file From bf38ca2b280376b2b1f41e085a2c20fa7ab6da5b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 12:43:54 +0100 Subject: [PATCH 030/255] Revert "Sketch shonky lexing and parsing for and!" This reverts commit dc097e212222e5777ec34a194f8e869e87abbdd1. --- src/fsharp/ast.fs | 4 ++-- src/fsharp/lex.fsl | 2 +- src/fsharp/pars.fsy | 14 ++++---------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index ec5baf4d78..d7f8e6cdf6 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -486,8 +486,8 @@ and /// AndBang(isUse, bindings, body, wholeRange) /// /// F# syntax: and! pat = expr in expr (Must follow on directly from a let! / and!) - | AndBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range - | NotAndBang of SynExpr + | AndBang of isUse:bool * bindings:SynBinding list * body:SynAndBangExpr * range:range + | EndOfAndBangChain of SynExpr and [] SynExpr = diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index 392db3602b..f3842f908e 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -243,7 +243,7 @@ rule token args skip = parse | ident '!' { let tok = Keywords.KeywordOrIdentifierToken args lexbuf (lexemeTrimRight lexbuf 1) match tok with - | LET _ | AND -> BINDER (lexemeTrimRight lexbuf 1) + | LET _ -> BINDER (lexemeTrimRight lexbuf 1) | _ -> fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("!")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) } | ident ('#') { fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("#")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) } diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 297179fcdc..4d3cf24720 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3297,26 +3297,20 @@ declExpr: | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) let m = unionRanges (rhs parseState 1) $7.Range - match $1 with - | "and" -> SynAndBangExpr.AndBang (spBind,false,true,$2,$4,$7,m) - | _ -> SynAndBangExpr.NotAndBang (SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m)) } + SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP typedSeqExprBlock %prec expr_let - { $5 (match $1 with | "use" -> "use!" | "let" -> "let!" | "and" -> "and!") (rhs parseState 1) // report unterminated error + { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let m = unionRanges (rhs parseState 1) $7.Range - match $1 with - | "and" -> SynAndBangExpr.AndBang (spBind,false,true,$2,$4,$7,m) - | _ -> SynAndBangExpr.NotAndBang (SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m)) } + SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) let m = $4.Range.EndRange // zero-width range - match $1 with - | "and" -> SynAndBangExpr.AndBang (spBind,false,true,$2,$4, SynExpr.ImplicitZero m, mAll) - | _ -> SynAndBangExpr.NotAndBang (SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4, SynExpr.ImplicitZero m, mAll)) } + SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4, SynExpr.ImplicitZero m, mAll) } | DO_BANG typedSeqExpr IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { let spBind = NoSequencePointAtDoBinding From e59e9c3cd1581d2c2d39d7b50392112c8e710ba4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 12:43:59 +0100 Subject: [PATCH 031/255] Revert "Sketch shonky and! addition to AST" This reverts commit 5753505805154ae9ac0d452bb3eabc8c27fb4bec. --- src/fsharp/ast.fs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index d7f8e6cdf6..1f0941eea4 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -480,14 +480,6 @@ and -and - [] - SynAndBangExpr = - /// AndBang(isUse, bindings, body, wholeRange) - /// - /// F# syntax: and! pat = expr in expr (Must follow on directly from a let! / and!) - | AndBang of isUse:bool * bindings:SynBinding list * body:SynAndBangExpr * range:range - | EndOfAndBangChain of SynExpr and [] SynExpr = @@ -698,11 +690,10 @@ and /// SynExpr.LetOrUseBang(spBind, isUse, isFromSource, pat, rhsExpr, bodyExpr, mWholeExpr). /// - /// F# syntax: let! pat = expr in expr' - /// F# syntax: use! pat = expr in expr' - /// where expr' admits an immediate and! + /// F# syntax: let! pat = expr in expr + /// F# syntax: use! pat = expr in expr /// Computation expressions only - | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range + | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynExpr * range:range /// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN | MatchBang of matchSeqPoint:SequencePointInfoForBinding * expr:SynExpr * clauses:SynMatchClause list * isExnMatch:bool * range:range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *) From b58b743678a71839ab01fb1ca739702b92b2aad1 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 13:01:46 +0100 Subject: [PATCH 032/255] Add alternative and! model in AST --- src/fsharp/ast.fs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index 1f0941eea4..e2d30a66cb 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -695,6 +695,14 @@ and /// Computation expressions only | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynExpr * range:range + /// SynExpr.AndBangChain(spBind, [(spBind, isUse, isFromSource, pat, rhsExpr, mWholeAndBangExpr)], bodyExpr, mWholeChainExpr). + /// + /// F# syntax: let! pat = expr and! ... and! ... and! pat = expr in expr + /// F# syntax: use! pat = expr and! ... and! ... and! pat = expr in expr + /// F# syntax: use! pat = expr and! ... anduse! ... and! pat = expr in expr + /// Computation expressions only + | AndBangChain of bindSeqPoint:SequencePointInfoForBinding * (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * SynExpr * range:range + /// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN | MatchBang of matchSeqPoint:SequencePointInfoForBinding * expr:SynExpr * clauses:SynMatchClause list * isExnMatch:bool * range:range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *) From f26b4993fd799af012366df53e40c536e235837a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 13:45:01 +0100 Subject: [PATCH 033/255] Fix typo in LexFilter comments --- src/fsharp/LexFilter.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index f8b7f951c2..a0985a41f1 100755 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. /// LexFilter - process the token stream prior to parsing. -/// Implements the offside rule and a copule of other lexical transformations. +/// Implements the offside rule and a couple of other lexical transformations. module internal Microsoft.FSharp.Compiler.LexFilter open Internal.Utilities.Text.Lexing From 98e8ff42e9dccb5bc9b0aa5f6f0a72897d7199c3 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 13:51:55 +0100 Subject: [PATCH 034/255] Mark LexFilter as a key thing to undderstand --- scratch/docs/Log.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index d98338ed2a..016abab4c5 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -130,4 +130,8 @@ and | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range ``` -This is kind of what we want, but its type safety is actually a bit annoying - now `and!`s are a different expression type, and the normal functions, e.g. to test whether something is a control-flow expression, doesn't directly apply to it. I feel like this will need revisiting - will we want a `let!` followed by a list of `and!`s (, i.e. the string "and!" could be like an expression list separator - see `listPatternElements` in `pars.fsy`, started with a "let!" and terminated with a "return". If the list is non-empty => its an applicative expression)? \ No newline at end of file +This is kind of what we want, but its type safety is actually a bit annoying - now `and!`s are a different expression type, and the normal functions, e.g. to test whether something is a control-flow expression, doesn't directly apply to it. I feel like this will need revisiting - will we want a `let!` followed by a list of `and!`s (, i.e. the string "and!" could be like an expression list separator - see `listPatternElements` in `pars.fsy`, started with a "let!" and terminated with a "return". If the list is non-empty => its an applicative expression)? + +## 2018-09-05 + +I am trying to work off `type ... and ...` and `let ... and ...` to work out how to make this fit `let!`. Looks like there some logic in `lexfilter.fs` for mapping from light to verbose syntax that I'll want to understand well. \ No newline at end of file From f787badf04923322ecd3a5fb016ae07bc60bd104 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 14:11:18 +0100 Subject: [PATCH 035/255] Make note to explore role of BINDER after application of lexfilter --- scratch/docs/Log.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 016abab4c5..4728cc312c 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -134,4 +134,6 @@ This is kind of what we want, but its type safety is actually a bit annoying - n ## 2018-09-05 -I am trying to work off `type ... and ...` and `let ... and ...` to work out how to make this fit `let!`. Looks like there some logic in `lexfilter.fs` for mapping from light to verbose syntax that I'll want to understand well. \ No newline at end of file +I am trying to work off `type ... and ...` and `let ... and ...` to work out how to make this fit `let!`. Looks like there some logic in `lexfilter.fs` for mapping from light to verbose syntax that I'll want to understand well. + +Why, CE section of the parser, do we have a rule for `BINDER` when `BINDER`s should have been mapped to `OBINDER`s by `lexfilter.fs` \ No newline at end of file From 1fb6c766da8daeb0409b4fae95da6d1c55f5d5f6 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 14:28:40 +0100 Subject: [PATCH 036/255] Add dsymes response about BINDER vs OBINER --- scratch/docs/Log.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 4728cc312c..d375118cca 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -136,4 +136,19 @@ This is kind of what we want, but its type safety is actually a bit annoying - n I am trying to work off `type ... and ...` and `let ... and ...` to work out how to make this fit `let!`. Looks like there some logic in `lexfilter.fs` for mapping from light to verbose syntax that I'll want to understand well. -Why, CE section of the parser, do we have a rule for `BINDER` when `BINDER`s should have been mapped to `OBINDER`s by `lexfilter.fs` \ No newline at end of file +### Light And Verbose Syntax + +Why, CE section of the parser, do we have a rule for `BINDER` when `BINDER`s should have been mapped to `OBINDER`s by `lexfilter.fs` + +> tomd [2:12 PM] +Question about parsing `let!`: +Is `lexfilter.fs` mean to transform all instaces of `BINDER` to `OBINDER`? I would have naively assumed light syntax works by mapping first to verbose syntax. Yet, `pars.fsy` still seems to have rules for `BINDER` - why is this? + +> dsyme [2:13 PM] +@tomd use of light syntax is optional, so BINDER is used for `#light "off"` + +> tomd [2:15 PM] +Okay, so in `lexfilter.fs` why do we map some `BINDERs` to `OBINDER`s? Is the light syntax the canonical one, in some sense? + +> dsyme [2:16 PM] +Yes, the syntaxes are effectively separate. e.g. we could split pars.fsy into two parsers - one for #light on (the default, containing OBINDER) and one for #light off (containing BINDER) \ No newline at end of file From c80fe727749fdc67d1dc2b634464b2d9f88bccb4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 14:42:13 +0100 Subject: [PATCH 037/255] Make and! part of the existing let!/use! expr --- src/fsharp/ast.fs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index e2d30a66cb..3d6f5cb9fd 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -688,20 +688,16 @@ and /// Computation expressions only | YieldOrReturnFrom of (bool * bool) * expr:SynExpr * range:range - /// SynExpr.LetOrUseBang(spBind, isUse, isFromSource, pat, rhsExpr, bodyExpr, mWholeExpr). + /// SynExpr.LetOrUseAndBang(spBind, [(spBind, isUse, isFromSource, pat, rhsExpr, mWholeAndBangExpr)], bodyExpr, mWholeChainExpr). /// /// F# syntax: let! pat = expr in expr /// F# syntax: use! pat = expr in expr - /// Computation expressions only - | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynExpr * range:range - - /// SynExpr.AndBangChain(spBind, [(spBind, isUse, isFromSource, pat, rhsExpr, mWholeAndBangExpr)], bodyExpr, mWholeChainExpr). - /// /// F# syntax: let! pat = expr and! ... and! ... and! pat = expr in expr /// F# syntax: use! pat = expr and! ... and! ... and! pat = expr in expr + /// F# syntax: let! pat = expr anduse! ... and! ... and! pat = expr in expr /// F# syntax: use! pat = expr and! ... anduse! ... and! pat = expr in expr /// Computation expressions only - | AndBangChain of bindSeqPoint:SequencePointInfoForBinding * (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * SynExpr * range:range + | LetOrUseAndBang of bindSeqPoint:SequencePointInfoForBinding * (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * SynExpr * range:range /// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN | MatchBang of matchSeqPoint:SequencePointInfoForBinding * expr:SynExpr * clauses:SynMatchClause list * isExnMatch:bool * range:range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *) From 245b3f3e7c4e2734140bf4ba5985c9cf6ed68413 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 15:32:08 +0100 Subject: [PATCH 038/255] Sketch new parsing and lexing f and! based on existing usages of and --- src/fsharp/FSStrings.resx | 3 +++ src/fsharp/lex.fsl | 6 ++++- src/fsharp/pars.fsy | 56 ++++++++++++++++++++++++++------------- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/fsharp/FSStrings.resx b/src/fsharp/FSStrings.resx index 5a99d5e9e7..0e5b831672 100644 --- a/src/fsharp/FSStrings.resx +++ b/src/fsharp/FSStrings.resx @@ -558,6 +558,9 @@ keyword 'and' + ! + keyword 'and!' + keyword 'as' diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index f3842f908e..aaaf298262 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -240,10 +240,14 @@ rule token args skip = parse { YIELD_BANG(false) } | "match!" { MATCH_BANG } + | "and!" + { AND_BANG(false) } + | "anduse!" + { AND_BANG(true) } | ident '!' { let tok = Keywords.KeywordOrIdentifierToken args lexbuf (lexemeTrimRight lexbuf 1) match tok with - | LET _ -> BINDER (lexemeTrimRight lexbuf 1) + | LET _ -> BINDER (lexemeTrimRight lexbuf 1) | _ -> fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("!")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) } | ident ('#') { fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("#")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) } diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 4d3cf24720..5ab309a604 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -186,7 +186,7 @@ let rangeOfLongIdent(lid:LongIdent) = %token CHAR %token DECIMAL %token <(string * string)> BIGNUM -%token LET YIELD YIELD_BANG +%token LET YIELD YIELD_BANG AND_BANG %token LESS GREATER /* here the bool indicates if the tokens are part of a type application or type parameter declaration, e.g. C, detected by the lex filter */ %token PERCENT_OP BINDER %token LQUOTE RQUOTE RQUOTE_DOT @@ -3001,6 +3001,42 @@ recover: | error { debugPrint("recovering via error"); true } | EOF { debugPrint("recovering via EOF"); false } +/* EXPERIMENT */ + +binders: + | BINDER headBindingPattern EQUALS typedSeqExprBlock IN morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ + { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) + let m = unionRanges (rhs parseState 1) $7.Range + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,$7,m) } + + | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let + { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error + let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) + let m = unionRanges (rhs parseState 1) $7.Range + SynExpr.LetOrUseAndBang(spBind,$6,$8,m) } + + | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP error %prec expr_let + { // error recovery that allows intellisense when writing incomplete computation expressions + let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) + let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) + let m = $4.Range.EndRange // zero-width range + SynExpr.LetOrUseAndBang(spBind,$6, SynExpr.ImplicitZero m, mAll) } + + +morebinders: + /* TODO Handle verbose and! */ + + | AND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders %prec expr_let /* TODO Check precedence */ + { $5 (if $1 then "anduse!" else "and!") (rhs parseState 1) // report unterminated error + let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) + let m = unionRanges (rhs parseState 1) $6.Range + (spBind,$1,true,$2,$4,m) :: $7 } + + | %prec prec_no_more_attr_bindings /* TODO Make a prec_no_more_andbang_bindings or something */ + { [] } + +/* END EXPERIMENT */ + declExpr: | defnBindings IN typedSeqExpr %prec expr_let @@ -3294,23 +3330,7 @@ declExpr: | YIELD_BANG declExpr { SynExpr.YieldOrReturnFrom(($1,not $1), $2, unionRanges (rhs parseState 1) $2.Range) } - | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let - { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) - let m = unionRanges (rhs parseState 1) $7.Range - SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m) } - - | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP typedSeqExprBlock %prec expr_let - { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error - let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) - let m = unionRanges (rhs parseState 1) $7.Range - SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4,$7,m) } - - | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let - { // error recovery that allows intellisense when writing incomplete computation expressions - let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) - let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) - let m = $4.Range.EndRange // zero-width range - SynExpr.LetOrUseBang(spBind,($1 = "use"),true,$2,$4, SynExpr.ImplicitZero m, mAll) } + | binders | DO_BANG typedSeqExpr IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { let spBind = NoSequencePointAtDoBinding From cd11672d52fb1431f767b3848517c40b67182478 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 17:57:00 +0100 Subject: [PATCH 039/255] Link to good guides for fslex and fsyacc --- scratch/docs/Log.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index d375118cca..cb8e45a315 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -140,6 +140,10 @@ I am trying to work off `type ... and ...` and `let ... and ...` to work out how Why, CE section of the parser, do we have a rule for `BINDER` when `BINDER`s should have been mapped to `OBINDER`s by `lexfilter.fs` +I should probably get to grips with fslex and fsyacc in a more principled way: +http://courses.softlab.ntua.gr/compilers/2015a/ocamllex-tutorial.pdf +http://courses.softlab.ntua.gr/compilers/2015a/ocamlyacc-tutorial.pdf + > tomd [2:12 PM] Question about parsing `let!`: Is `lexfilter.fs` mean to transform all instaces of `BINDER` to `OBINDER`? I would have naively assumed light syntax works by mapping first to verbose syntax. Yet, `pars.fsy` still seems to have rules for `BINDER` - why is this? @@ -148,7 +152,7 @@ Is `lexfilter.fs` mean to transform all instaces of `BINDER` to `OBINDER`? I wou @tomd use of light syntax is optional, so BINDER is used for `#light "off"` > tomd [2:15 PM] -Okay, so in `lexfilter.fs` why do we map some `BINDERs` to `OBINDER`s? Is the light syntax the canonical one, in some sense? +Okay, so in `lexfilter.fs` why do we map some `BINDER`s to `OBINDER`s? Is the light syntax the canonical one, in some sense? > dsyme [2:16 PM] -Yes, the syntaxes are effectively separate. e.g. we could split pars.fsy into two parsers - one for #light on (the default, containing OBINDER) and one for #light off (containing BINDER) \ No newline at end of file +Yes, the syntaxes are effectively separate. e.g. we could split pars.fsy into two parsers - one for `#light on` (the default, containing `OBINDER`) and one for #light off (containing `BINDER`) \ No newline at end of file From 0a22a57ecd0fb6ee37d7edaf7fd81fc8305085a5 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 17:57:57 +0100 Subject: [PATCH 040/255] Make pars.fsy build --- src/fsharp/pars.fsy | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 5ab309a604..77ff64501d 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3331,6 +3331,7 @@ declExpr: { SynExpr.YieldOrReturnFrom(($1,not $1), $2, unionRanges (rhs parseState 1) $2.Range) } | binders + { } | DO_BANG typedSeqExpr IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { let spBind = NoSequencePointAtDoBinding From 7b88781675700e1dba3f57ad236c25c602c8e477 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 18:35:26 +0100 Subject: [PATCH 041/255] Fix throwing away of first binding in a let! ... and! .. chain --- src/fsharp/ast.fs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index 3d6f5cb9fd..432db71419 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -688,7 +688,7 @@ and /// Computation expressions only | YieldOrReturnFrom of (bool * bool) * expr:SynExpr * range:range - /// SynExpr.LetOrUseAndBang(spBind, [(spBind, isUse, isFromSource, pat, rhsExpr, mWholeAndBangExpr)], bodyExpr, mWholeChainExpr). + /// SynExpr.LetOrUseAndBang(spBind, isUse, isFromSource, pat, rhsExpr, [(andBangSpBind, andBangIsUse, andBangIsFromSource, andBangPat, andBangRhsExpr, mAndBangExpr)], bodyExpr, mLetBangExpr). /// /// F# syntax: let! pat = expr in expr /// F# syntax: use! pat = expr in expr @@ -697,7 +697,7 @@ and /// F# syntax: let! pat = expr anduse! ... and! ... and! pat = expr in expr /// F# syntax: use! pat = expr and! ... anduse! ... and! pat = expr in expr /// Computation expressions only - | LetOrUseAndBang of bindSeqPoint:SequencePointInfoForBinding * (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * SynExpr * range:range + | LetOrUseAndBang of bindSeqPoint:SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range:range * (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * SynExpr /// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN | MatchBang of matchSeqPoint:SequencePointInfoForBinding * expr:SynExpr * clauses:SynMatchClause list * isExnMatch:bool * range:range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *) @@ -789,7 +789,7 @@ and | SynExpr.ImplicitZero (range=m) | SynExpr.YieldOrReturn (range=m) | SynExpr.YieldOrReturnFrom (range=m) - | SynExpr.LetOrUseBang (range=m) + | SynExpr.LetOrUseAndBang (range=m) | SynExpr.MatchBang (range=m) | SynExpr.DoBang (range=m) | SynExpr.Fixed (range=m) -> m @@ -851,7 +851,7 @@ and | SynExpr.ImplicitZero (range=m) | SynExpr.YieldOrReturn (range=m) | SynExpr.YieldOrReturnFrom (range=m) - | SynExpr.LetOrUseBang (range=m) + | SynExpr.LetOrUseAndBang (range=m) | SynExpr.MatchBang (range=m) | SynExpr.DoBang (range=m) -> m | SynExpr.DotGet (expr,_,lidwd,m) -> if lidwd.ThereIsAnExtraDotAtTheEnd then unionRanges expr.Range lidwd.RangeSansAnyExtraDot else m @@ -915,7 +915,7 @@ and | SynExpr.ImplicitZero (range=m) | SynExpr.YieldOrReturn (range=m) | SynExpr.YieldOrReturnFrom (range=m) - | SynExpr.LetOrUseBang (range=m) + | SynExpr.LetOrUseAndBang (range=m) | SynExpr.MatchBang (range=m) | SynExpr.DoBang (range=m) -> m // these are better than just .Range, and also commonly applicable inside queries @@ -1622,7 +1622,7 @@ let rec IsControlFlowExpression e = // Treat "ident { ... }" as a control flow expression | SynExpr.App (_, _, SynExpr.Ident _, SynExpr.CompExpr _,_) | SynExpr.IfThenElse _ - | SynExpr.LetOrUseBang _ + | SynExpr.LetOrUseAndBang _ | SynExpr.Match _ | SynExpr.TryWith _ | SynExpr.TryFinally _ From 77608d7b0451d014b2d62b138c5fbf3c4132c8ed Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 18:35:59 +0100 Subject: [PATCH 042/255] Try to fix range over and!s I think it's totally broken and the grammer needs to change to make it clear that the final body after a let! ... and! .. chain is nested under ALL bindings in the chain, not just the first let! --- src/fsharp/pars.fsy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 77ff64501d..244387a85b 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3006,13 +3006,13 @@ recover: binders: | BINDER headBindingPattern EQUALS typedSeqExprBlock IN morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) - let m = unionRanges (rhs parseState 1) $7.Range - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,$7,m) } + let m = unionRanges (rhs parseState 1) $8.Range + SynExpr.LetOrUseAndBang(spBind,$6,$8,m) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) - let m = unionRanges (rhs parseState 1) $7.Range + let m = unionRanges (rhs parseState 1) $8.Range SynExpr.LetOrUseAndBang(spBind,$6,$8,m) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP error %prec expr_let @@ -3029,8 +3029,8 @@ morebinders: | AND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders %prec expr_let /* TODO Check precedence */ { $5 (if $1 then "anduse!" else "and!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) - let m = unionRanges (rhs parseState 1) $6.Range - (spBind,$1,true,$2,$4,m) :: $7 } + let m = rhs parseState 1 + (spBind,$1,true,$2,$4,m) :: $6 } | %prec prec_no_more_attr_bindings /* TODO Make a prec_no_more_andbang_bindings or something */ { [] } From 05222f0640046032af982ced6fd809536804f3d7 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 18:49:31 +0100 Subject: [PATCH 043/255] Add note about current high-level questions --- scratch/docs/Log.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index cb8e45a315..3396a12633 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -155,4 +155,9 @@ Is `lexfilter.fs` mean to transform all instaces of `BINDER` to `OBINDER`? I wou Okay, so in `lexfilter.fs` why do we map some `BINDER`s to `OBINDER`s? Is the light syntax the canonical one, in some sense? > dsyme [2:16 PM] -Yes, the syntaxes are effectively separate. e.g. we could split pars.fsy into two parsers - one for `#light on` (the default, containing `OBINDER`) and one for #light off (containing `BINDER`) \ No newline at end of file +Yes, the syntaxes are effectively separate. e.g. we could split pars.fsy into two parsers - one for `#light on` (the default, containing `OBINDER`) and one for #light off (containing `BINDER`) + +### Interesting Questions + +* Should `anduse!` be a thing? If so, what should its final name be? +* How should the ranges work for `let! ... and! ...` groups? For each binding range over the full chain of bindings and the final body? I'd kind of like just the current binding and the body, but ranges have to be continuous - it's baked in pretty deeply. \ No newline at end of file From 3cee2cf48d1428d11fcb46cdda949f8d3a8a6fb1 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 18:52:48 +0100 Subject: [PATCH 044/255] Note that I think I'll do what let does for ranges for now --- scratch/docs/Log.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 3396a12633..3b3652c9c5 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -157,7 +157,7 @@ Okay, so in `lexfilter.fs` why do we map some `BINDER`s to `OBINDER`s? Is the li > dsyme [2:16 PM] Yes, the syntaxes are effectively separate. e.g. we could split pars.fsy into two parsers - one for `#light on` (the default, containing `OBINDER`) and one for #light off (containing `BINDER`) -### Interesting Questions +### Interesting High-Level Questions * Should `anduse!` be a thing? If so, what should its final name be? -* How should the ranges work for `let! ... and! ...` groups? For each binding range over the full chain of bindings and the final body? I'd kind of like just the current binding and the body, but ranges have to be continuous - it's baked in pretty deeply. \ No newline at end of file +* How should the ranges work for `let! ... and! ...` groups? For each binding range over the full chain of bindings and the final body? I'd kind of like just the current binding and the body, but ranges have to be continuous - it's baked in pretty deeply. I think I'll just do what let does and accept that it looks like earlier `and!`'s bindings are in scope of later ones. \ No newline at end of file From f08d8bda7fad87e42df47529a17c30fd9baaa95b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 6 Sep 2018 19:12:41 +0100 Subject: [PATCH 045/255] Structure my EoD thoughts --- scratch/docs/Log.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 3b3652c9c5..8cef9ebb35 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -157,7 +157,36 @@ Okay, so in `lexfilter.fs` why do we map some `BINDER`s to `OBINDER`s? Is the li > dsyme [2:16 PM] Yes, the syntaxes are effectively separate. e.g. we could split pars.fsy into two parsers - one for `#light on` (the default, containing `OBINDER`) and one for #light off (containing `BINDER`) -### Interesting High-Level Questions +### High-Level Questions * Should `anduse!` be a thing? If so, what should its final name be? -* How should the ranges work for `let! ... and! ...` groups? For each binding range over the full chain of bindings and the final body? I'd kind of like just the current binding and the body, but ranges have to be continuous - it's baked in pretty deeply. I think I'll just do what let does and accept that it looks like earlier `and!`'s bindings are in scope of later ones. \ No newline at end of file +* How should the ranges work for `let! ... and! ...` groups? For each binding range over the full chain of bindings and the final body? I'd kind of like just the current binding and the body, but ranges have to be continuous - it's baked in pretty deeply. I think I'll just do what let does and accept that it looks like earlier `and!`'s bindings are in scope of later ones. + +### Low-Level Questions + +#### Parsing complexity vs. AST complexity + +The `binders` non-terminal awkwardly contains the final body with a `let!` rather than waiting until after the zero-or-more `and!`s. This makes creating the range more awkward. On the other hand, that keeps the structure of the AST largely as it is, whilst still preventing more `let!`s half way through. I think keeping the AST as it is so we don't get a new `and!` expression type wins here. + +> Toby Shaw [6:57 PM] +I had naively only thought about the other way, but I like your idea more +catches the error in parsing, which is way nicer + +> tomd [6:57 PM] +Yep! + +> Toby Shaw [6:58 PM] +the only problem is that you might find it harder to give a descriptive error message + +> tomd [6:58 PM] +Shoe horning the latter way into parsing is hard too +Yeah, I am no where near thinking about that too hard though +Why do you say that? + +> Toby Shaw [7:04 PM] +the parser has auto-generated error messages, I think +Whereas if you throw the error in type checking, then you can choose the error message + +#### What is allowed to follow a `let! ... and! ...`? + +It's probably worth giving some thought to what should be allowed to come after a `let! ... and! ...` chain. Only allowing `return` might be one simple way to get started? From a4377c7077fc951cdbcd16ee5891ba76d20e433b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 7 Sep 2018 14:46:16 +0100 Subject: [PATCH 046/255] Add notes about desugaring let! ... and! ... and the prior art --- scratch/docs/Log.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 8cef9ebb35..bb61dac8f5 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -179,14 +179,32 @@ Yep! the only problem is that you might find it harder to give a descriptive error message > tomd [6:58 PM] -Shoe horning the latter way into parsing is hard too -Yeah, I am no where near thinking about that too hard though +Shoe horning the latter way into parsing is hard too +Yeah, I am no where near thinking about that too hard though Why do you say that? > Toby Shaw [7:04 PM] -the parser has auto-generated error messages, I think +the parser has auto-generated error messages, I think Whereas if you throw the error in type checking, then you can choose the error message #### What is allowed to follow a `let! ... and! ...`? It's probably worth giving some thought to what should be allowed to come after a `let! ... and! ...` chain. Only allowing `return` might be one simple way to get started? + +## 2018-09-06 + +[Useful description](https://fsprojects.github.io/FSharpPlus/computation-expressions.html) of the various kinds of `monad`, as F# sees things. + +The first thing I want to have work it the `option` applicative, which I think is a strict (no delaying required), monad-plus (plus in this case means take the first `Some` value) CE. + +Questions to #general: +> tomd [2:43 PM] +When writing a computation expression builder, is the choice in which of `Yield` and `Return` to implement purely down to whether you want the CE to make the `yield` or `return` keyword available. I can't really imagine a world where they're have different implementations (since they'd imply different monad instances, right?) +> +> The context of this question is working out how `pure` would work for `let! ... and! ...` computation expressions (I think `apply` is entirely new, so no prior art to decipher in that case). + +Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions): + +| Expression | Translation | +|-------------------------------|----------------------------------------| +| `let! pattern1 = expr1 in and! pattern2 = expr2 in cexpr` | `builder.apply()` \ No newline at end of file From fb28e7c4db947af34f7e775017e851b4a6437549 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 7 Sep 2018 14:53:32 +0100 Subject: [PATCH 047/255] Make note about return for applicatives and yield for alt. applicatives --- scratch/docs/Log.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index bb61dac8f5..7d063c3039 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -199,10 +199,12 @@ The first thing I want to have work it the `option` applicative, which I think i Questions to #general: > tomd [2:43 PM] -When writing a computation expression builder, is the choice in which of `Yield` and `Return` to implement purely down to whether you want the CE to make the `yield` or `return` keyword available. I can't really imagine a world where they're have different implementations (since they'd imply different monad instances, right?) +When writing a computation expression builder, is the choice in which of `Yield` and `Return` to implement purely down to whether you want the CE to make the `yield` or `return` keyword available? I can't really imagine a world where they're have different implementations (since they'd imply different monad instances, right?) > > The context of this question is working out how `pure` would work for `let! ... and! ...` computation expressions (I think `apply` is entirely new, so no prior art to decipher in that case). +Assuming the difference is in keyword, as person writing a CE, I guess I'd use `return` for plain old applicatives and `yield` for [alternative applicatives](https://hackage.haskell.org/package/base-4.6.0.1/docs/Control-Applicative.html#t:Alternative), and so on that basis, I think my change should use, say, support `return` initially and `yield` later (when the CE builder writer has defined `Combine`, which is really the alternation operator). + Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions): | Expression | Translation | From 082ec6f2649d48d444551517bf4db7283e3e8e61 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 7 Sep 2018 15:07:49 +0100 Subject: [PATCH 048/255] Sketch docs for Apply method on a CE builder --- scratch/docs/Log.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 7d063c3039..d2a23cb8f6 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -204,9 +204,14 @@ When writing a computation expression builder, is the choice in which of `Yield` > The context of this question is working out how `pure` would work for `let! ... and! ...` computation expressions (I think `apply` is entirely new, so no prior art to decipher in that case). Assuming the difference is in keyword, as person writing a CE, I guess I'd use `return` for plain old applicatives and `yield` for [alternative applicatives](https://hackage.haskell.org/package/base-4.6.0.1/docs/Control-Applicative.html#t:Alternative), and so on that basis, I think my change should use, say, support `return` initially and `yield` later (when the CE builder writer has defined `Combine`, which is really the alternation operator). +Alternative applicative support via `let! ... and! ...` and `yield` keywords would make writing things with alternatives, e.g. an argument parser, both efficient (no rebuilding the compuation on every application) and convenient (syntax is lightweight and readable). Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions): +| Method | Typical Signature(s) | Description | +|---------|-----------------------------|-------------| +| `Apply` | M<'T -> 'U> * M -> M<'U> | Called for `let! ... and! ...` and computation expressions. Allows certain operations that do not require the full power of bind to be performed potentially more efficiently, or where they do not even have a sensible bind operation (but to have an apply) to be performed at all. + | Expression | Translation | |-------------------------------|----------------------------------------| -| `let! pattern1 = expr1 in and! pattern2 = expr2 in cexpr` | `builder.apply()` \ No newline at end of file +| `let! pattern1 = expr1 in and! pattern2 = expr2 in cexpr` | `builder.apply(builder.return(expr)` \ No newline at end of file From 3b40a2cd64aef4f94bb601cbf05b16de15fc58be Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 7 Sep 2018 15:09:34 +0100 Subject: [PATCH 049/255] Note a seemingly undocumented usage of Delay --- scratch/docs/Log.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index d2a23cb8f6..5259f563ac 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -206,6 +206,8 @@ When writing a computation expression builder, is the choice in which of `Yield` Assuming the difference is in keyword, as person writing a CE, I guess I'd use `return` for plain old applicatives and `yield` for [alternative applicatives](https://hackage.haskell.org/package/base-4.6.0.1/docs/Control-Applicative.html#t:Alternative), and so on that basis, I think my change should use, say, support `return` initially and `yield` later (when the CE builder writer has defined `Combine`, which is really the alternation operator). Alternative applicative support via `let! ... and! ...` and `yield` keywords would make writing things with alternatives, e.g. an argument parser, both efficient (no rebuilding the compuation on every application) and convenient (syntax is lightweight and readable). +Note from @Nick: `Delay` is useful even in non-side-effectful CE builders as a way to "hide" evaluating a CE behind a thunk. + Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions): | Method | Typical Signature(s) | Description | From 86abfda7201fa9df22db0e8962394254d9220c1c Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 7 Sep 2018 15:13:09 +0100 Subject: [PATCH 050/255] Define desugaring of apply --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 5259f563ac..5ca8347e76 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -216,4 +216,4 @@ Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/ | Expression | Translation | |-------------------------------|----------------------------------------| -| `let! pattern1 = expr1 in and! pattern2 = expr2 in cexpr` | `builder.apply(builder.return(expr)` \ No newline at end of file +| `let! pattern1 = expr1 in and! pattern2 = expr2 in and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(cexpr, expr1), expr2), ...)` \ No newline at end of file From 4f84da4d003523a3bf120dbee1feffdedc0d5a48 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 7 Sep 2018 15:19:14 +0100 Subject: [PATCH 051/255] Add operator notation for applicatives desugaring example --- scratch/docs/Log.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 5ca8347e76..431433ba5d 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -214,6 +214,6 @@ Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/ |---------|-----------------------------|-------------| | `Apply` | M<'T -> 'U> * M -> M<'U> | Called for `let! ... and! ...` and computation expressions. Allows certain operations that do not require the full power of bind to be performed potentially more efficiently, or where they do not even have a sensible bind operation (but to have an apply) to be performed at all. -| Expression | Translation | -|-------------------------------|----------------------------------------| -| `let! pattern1 = expr1 in and! pattern2 = expr2 in and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(cexpr, expr1), expr2), ...)` \ No newline at end of file +| Expression | Translation | And using operators... | +|-------------------------------|-------------|------------------------| +| `let! pattern1 = expr1 in and! pattern2 = expr2 in and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(cexpr, expr1), expr2), ...)` | `cexpr <*> expr1 <*> expr2 <*> ... <*> exprN` \ No newline at end of file From 4061267d7aedc173651218de9e165f2e9fd05d02 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 7 Sep 2018 15:26:57 +0100 Subject: [PATCH 052/255] Note why let! ... and! .. syntax is still useful in a world where the compiler picks the least powerful of map, apply and bind to call --- scratch/docs/Log.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 431433ba5d..aff483dd78 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -216,4 +216,8 @@ Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/ | Expression | Translation | And using operators... | |-------------------------------|-------------|------------------------| -| `let! pattern1 = expr1 in and! pattern2 = expr2 in and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(cexpr, expr1), expr2), ...)` | `cexpr <*> expr1 <*> expr2 <*> ... <*> exprN` \ No newline at end of file +| `let! pattern1 = expr1 in and! pattern2 = expr2 in and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(cexpr, expr1), expr2), ...)` | `cexpr <*> expr1 <*> expr2 <*> ... <*> exprN` + +Given the above, I think it would _not_ require extra work to allow all of the other usual CE keywords to work alongside `let! ... and! ...`, since the desugaring only requires the context of what `and!`s correspond to what `let!`s and otherwise doesn't interact, which (right now) is easily done in the parser/AST. + +This also means we can add optimisations to reduce current desugarings which use `Bind` to use the least powerful that applies of `Map`, `Apply` and `Bind`. Even in a world where these magic optimisations exist (where `let! ... and! ...` syntax wouldn't strictly be necessary for `Apply` to be called, the syntax would be handy because it would be a means for the CE user to assert "I expect this to be possible with only `apply`, I do _not_ want to be using `Bind` here", e.g. because of the runtime cost or because I don't want to care about whether or not `Bind` even exists on this builder). \ No newline at end of file From ec41873945d158c6f8d657d15faefb3dc3be393a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 10 Sep 2018 10:01:37 +0100 Subject: [PATCH 053/255] Clean up last week's notes --- scratch/docs/Log.md | 51 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index aff483dd78..ed8db36505 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -55,7 +55,7 @@ Standard place for tests (see `match!` PR) seems to be: https://github.com/Micro * Try to understand the `match!` commit by reading/debugging * Trying to change the `let!` syntax, e.g. to `foo!` and see it work -## 2018-09-04 +## 2018-09-05 Still waiting on a response on #langdesign. Good news! The [RFC process](https://fsharp.github.io/2016/09/26/fsharp-rfc-process.html) isn't very heavyweight. @@ -132,7 +132,7 @@ and This is kind of what we want, but its type safety is actually a bit annoying - now `and!`s are a different expression type, and the normal functions, e.g. to test whether something is a control-flow expression, doesn't directly apply to it. I feel like this will need revisiting - will we want a `let!` followed by a list of `and!`s (, i.e. the string "and!" could be like an expression list separator - see `listPatternElements` in `pars.fsy`, started with a "let!" and terminated with a "return". If the list is non-empty => its an applicative expression)? -## 2018-09-05 +## 2018-09-06 I am trying to work off `type ... and ...` and `let ... and ...` to work out how to make this fit `let!`. Looks like there some logic in `lexfilter.fs` for mapping from light to verbose syntax that I'll want to understand well. @@ -191,32 +191,71 @@ Whereas if you throw the error in type checking, then you can choose the error m It's probably worth giving some thought to what should be allowed to come after a `let! ... and! ...` chain. Only allowing `return` might be one simple way to get started? -## 2018-09-06 +## 2018-09-07 [Useful description](https://fsprojects.github.io/FSharpPlus/computation-expressions.html) of the various kinds of `monad`, as F# sees things. The first thing I want to have work it the `option` applicative, which I think is a strict (no delaying required), monad-plus (plus in this case means take the first `Some` value) CE. -Questions to #general: +### Question to #general: + > tomd [2:43 PM] When writing a computation expression builder, is the choice in which of `Yield` and `Return` to implement purely down to whether you want the CE to make the `yield` or `return` keyword available? I can't really imagine a world where they're have different implementations (since they'd imply different monad instances, right?) > > The context of this question is working out how `pure` would work for `let! ... and! ...` computation expressions (I think `apply` is entirely new, so no prior art to decipher in that case). +> +> tomasp [11:10 PM] +@tomd I think one reason was that `let! a = x and b = y` maps pretty directly to `Merge`, so the translation is simpler +and it also works nicely if you have something that is also a monad, because then you can translate this: + +```F# +m { + let! a = x + and b = y + let! c = z + return a + b + c } +``` + +> into this: + +```F# +m.Bind(m.Merge(x, y), fun (a, b) -> + m.Bind(z, fun c -> + m.Return(a + b + c) ) ) +``` +> For reference, if you do not have `Bind` and have just an applicative, you will not be able to do the second `let! c = z`. You can only write, say: + +```F# +m { + let! a = x + and b = y + return a + b } +``` + +> which can be translated using just `Map`: + +```F# +m.Map(m.Merge(x, y), fun (a, b) -> a + b) +``` + +### Choosing `return` vs. `yield` Assuming the difference is in keyword, as person writing a CE, I guess I'd use `return` for plain old applicatives and `yield` for [alternative applicatives](https://hackage.haskell.org/package/base-4.6.0.1/docs/Control-Applicative.html#t:Alternative), and so on that basis, I think my change should use, say, support `return` initially and `yield` later (when the CE builder writer has defined `Combine`, which is really the alternation operator). Alternative applicative support via `let! ... and! ...` and `yield` keywords would make writing things with alternatives, e.g. an argument parser, both efficient (no rebuilding the compuation on every application) and convenient (syntax is lightweight and readable). Note from @Nick: `Delay` is useful even in non-side-effectful CE builders as a way to "hide" evaluating a CE behind a thunk. +### Desugaring + Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions): | Method | Typical Signature(s) | Description | |---------|-----------------------------|-------------| -| `Apply` | M<'T -> 'U> * M -> M<'U> | Called for `let! ... and! ...` and computation expressions. Allows certain operations that do not require the full power of bind to be performed potentially more efficiently, or where they do not even have a sensible bind operation (but to have an apply) to be performed at all. +| `Apply` | `M<'T -> 'U> * M<'T> -> M<'U>` | Called for `let! ... and! ...` and computation expressions. Allows certain operations that do not require the full power of bind to be performed potentially more efficiently, or where they do not even have a sensible bind operation (but to have an apply) to be performed at all. | Expression | Translation | And using operators... | |-------------------------------|-------------|------------------------| -| `let! pattern1 = expr1 in and! pattern2 = expr2 in and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(cexpr, expr1), expr2), ...)` | `cexpr <*> expr1 <*> expr2 <*> ... <*> exprN` +| `let! pattern1 = expr1 and! pattern2 = expr2 and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(cexpr, expr1), expr2), ...)` | `cexpr <*> expr1 <*> expr2 <*> ... <*> exprN` Given the above, I think it would _not_ require extra work to allow all of the other usual CE keywords to work alongside `let! ... and! ...`, since the desugaring only requires the context of what `and!`s correspond to what `let!`s and otherwise doesn't interact, which (right now) is easily done in the parser/AST. From 571b29f60a44cafd10af0bf0aabdf4950d7722e2 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 10 Sep 2018 11:26:29 +0100 Subject: [PATCH 054/255] Fill out notes on optimisng desugarings etc --- scratch/docs/Log.md | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index ed8db36505..1298e280f9 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -240,10 +240,14 @@ m.Map(m.Merge(x, y), fun (a, b) -> a + b) ### Choosing `return` vs. `yield` -Assuming the difference is in keyword, as person writing a CE, I guess I'd use `return` for plain old applicatives and `yield` for [alternative applicatives](https://hackage.haskell.org/package/base-4.6.0.1/docs/Control-Applicative.html#t:Alternative), and so on that basis, I think my change should use, say, support `return` initially and `yield` later (when the CE builder writer has defined `Combine`, which is really the alternation operator). +Assuming the difference is in which keyword becomes available inside te CE, as person writing a CE, I supppose I'd use `return` for plain old applicatives and `yield` for [alternative applicatives](https://hackage.haskell.org/package/base-4.6.0.1/docs/Control-Applicative.html#t:Alternative), and so on that basis, I think my change should use, say, support `return` initially and `yield` later (when the CE builder writer has defined `Combine`, which is really the alternation operator). Alternative applicative support via `let! ... and! ...` and `yield` keywords would make writing things with alternatives, e.g. an argument parser, both efficient (no rebuilding the compuation on every application) and convenient (syntax is lightweight and readable). -Note from @Nick: `Delay` is useful even in non-side-effectful CE builders as a way to "hide" evaluating a CE behind a thunk. +### A Note on Alternation +`Combine`, the alternation method, is called when sequencing CE expressions. e.g. for options, we can pick the first in the `Some` case by just listing them inside the CE and providing a left-biased `Combine` method on the builder. + +### Further uses of `Delay` +Note from @Nick: `Delay` is useful even in non-side-effectful CE builders as a way to "hide" evaluating a CE behind a thunk - useful when building the CE itself is expensive. ### Desugaring @@ -255,8 +259,35 @@ Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/ | Expression | Translation | And using operators... | |-------------------------------|-------------|------------------------| -| `let! pattern1 = expr1 and! pattern2 = expr2 and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(cexpr, expr1), expr2), ...)` | `cexpr <*> expr1 <*> expr2 <*> ... <*> exprN` +| `let! pattern1 = expr1 and! pattern2 = expr2 and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(fun expr1 expr2 ... -> cexpr, expr1), expr2), ...)` | `cexpr <*> expr1 <*> expr2 <*> ... <*> exprN` Given the above, I think it would _not_ require extra work to allow all of the other usual CE keywords to work alongside `let! ... and! ...`, since the desugaring only requires the context of what `and!`s correspond to what `let!`s and otherwise doesn't interact, which (right now) is easily done in the parser/AST. -This also means we can add optimisations to reduce current desugarings which use `Bind` to use the least powerful that applies of `Map`, `Apply` and `Bind`. Even in a world where these magic optimisations exist (where `let! ... and! ...` syntax wouldn't strictly be necessary for `Apply` to be called, the syntax would be handy because it would be a means for the CE user to assert "I expect this to be possible with only `apply`, I do _not_ want to be using `Bind` here", e.g. because of the runtime cost or because I don't want to care about whether or not `Bind` even exists on this builder). \ No newline at end of file +This also means we can add optimisations to reduce current desugarings which use `Bind` to use the least powerful that applies of `Map`, `Apply` and `Bind`. Even in a world where these magic optimisations exist (where `let! ... and! ...` syntax wouldn't strictly be necessary for `Apply` to be called, the syntax would be handy because it would be a means for the CE user to assert "I expect this to be possible with only `apply`, I do _not_ want to be using `Bind` here", e.g. because of the runtime cost or because I don't want to care about whether or not `Bind` even exists on this builder). + +## 2018-09-10 + +I don't think we need `in` between `let!`s and `and!`s in the same chain, because any later `and!` in unambiguously part of the chain (a new chain would need to be started with a fresh `let!`). + +### Should we desugar to `Bind`, `Apply` or `Map`? + +Right now, we can only ever desugar `let!` to a `Bind`. This isn't ideal because `Bind` can be expensive and can make statically examining the structure of the full computation impossible (the can't know what will happen "behind a bind" until a value is present for us to use to generate the subsequent computation). + +If `Apply` is available on the builder, we can use it as a (potentially) more efficient alternative for `Bind` where the RHS of a `let!` does not make reference to the LHS of a previous `let!`. In a sense, it allows us to bind multiple names which are not interdependent simultaneously. It also allows us to apply functions on the LHS of a `let!` to values on the LHS of a `let!` without `Bind`. This is useful because implementing `Bind` isn't possible for as many interesting structures as for `Apply`. + +Similarly, if `Map` is available, we can use is as an alternative to `Apply` and `Bind` if we take the value on the LHS of a `let!` and transform it with reference to the LHS of any other `let!` at any point. Again, `Map` is possible to implement for more structures than both `Bind` and `Apply`, and it is potentially more efficient to use too. + +`Bind`, `Apply` and `Map` are related via various laws that govern how they "should" behave - i.e. we expect any "sensible" CE builder writer will produce methods for which these hold. For example, `builder.Bind(fun x -> builder.Return(f x))` and `builder.Apply(builder.Return(f), x)` should both produce the same result as `builder.Map(f, x)`. + +The most minimal change we could make is to just make `let! ... and! ...` desugar to `Apply` as defined earlier. This makes CE builders useful for strutures for which we can't or haven't defined `Bind`. It also allows the more efficient `Apply` to be used even where `Bind` is defined, and potentially allows more static examination of the resulting value. + +Beyond that, we could start more aggressively using the most constrained (and hence generally most efficient) desugaring we can. e.g. even if the CE writer does not explicitly use `let! ... and! ...`, we may notice that no LHS of a `let!` is used on the RHS of another, so we can desugar to `Apply` instead. If the laws hold, this would probably create a more efficient program which behaves identically, however, _it will do something different to the `Bind` desugaring if the laws don't hold_! e.g. If `Apply` calls `System.Environment.Exit()` but `Bind` does not, the program will do something drastically different. As such, the more aggressive, optimising desugarings are potentially dangerous so we probably want to consider these separately to the `let! ... and! ...` proposal. Interestingly, however, if a CE writer was using a builder which defined `Bind` but not `Apply` or `Map`, and later the builder was updated to implement `Apply` and `Map` in a way that held with respect to the laws, then without a change of their source code, they could get some new optimisations after recompiling. + +Even in a world with these auto-magical, optimising desugarings, having the `let! ... and! ...` syntax is still useful, since it offers a way for the CE writer to declare "I expect this should be possible with `Apply` and therefore do not require `Bind`, so please verify that is true and use only `Apply`". This makes it possible to be confident that the desugaring will avoid using `Bind` e.g. because it is expensive. + +Assuming we used the full suite of optimising desugarings (i.e. using `Map` in preference to `Apply`, in preference to `Bind` when possible), we would get this: + +| Expression | Translation | Optimisation | +|-------------------------------|-------------|--------------| +|`let! pat1 = exp1 and! pat2 = exp2 in let! pat3 = exp1 in return pat1 + pat2 + pat3`| `builder.Apply(builder.Apply(builder.Apply(fun pat1 pat2 pat3 -> pat1 + pat2 + pat3), exp1), exp2), exp3)` | Use `Apply` rather than `Bind` despite final binding not being part of preceding `and!` chain. +| `let! x = y in return x + 1` | `builder.Map((fun x -> x + 1), y)` | Use `Map` rather than `Bind` or `Apply` despite `let!` \ No newline at end of file From b4cfa8d233a40c97848098bbadafea7b46ae48f9 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 10 Sep 2018 13:35:23 +0100 Subject: [PATCH 055/255] Consider liftA2 vs apply / merge --- scratch/docs/Log.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 1298e280f9..39623143ca 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -290,4 +290,8 @@ Assuming we used the full suite of optimising desugarings (i.e. using `Map` in p | Expression | Translation | Optimisation | |-------------------------------|-------------|--------------| |`let! pat1 = exp1 and! pat2 = exp2 in let! pat3 = exp1 in return pat1 + pat2 + pat3`| `builder.Apply(builder.Apply(builder.Apply(fun pat1 pat2 pat3 -> pat1 + pat2 + pat3), exp1), exp2), exp3)` | Use `Apply` rather than `Bind` despite final binding not being part of preceding `and!` chain. -| `let! x = y in return x + 1` | `builder.Map((fun x -> x + 1), y)` | Use `Map` rather than `Bind` or `Apply` despite `let!` \ No newline at end of file +| `let! x = y in return x + 1` | `builder.Map((fun x -> x + 1), y)` | Use `Map` rather than `Bind` or `Apply` despite `let!` + +### `liftA2` vs. `apply` vs. merge` + +There's already been plenty of chat about the `apply` vs. `merge` way of doing things, but some people also claim `liftA2` is easier to write - maybe we should consider that (although probably under a different name). \ No newline at end of file From d84c9d52b59e33ece1a058f24b6b9c57ccb649e0 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 10 Sep 2018 17:44:33 +0100 Subject: [PATCH 056/255] Add notes from f2f discussion with dsyme --- scratch/docs/Log.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 39623143ca..b651b8a6ac 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -294,4 +294,14 @@ Assuming we used the full suite of optimising desugarings (i.e. using `Map` in p ### `liftA2` vs. `apply` vs. merge` -There's already been plenty of chat about the `apply` vs. `merge` way of doing things, but some people also claim `liftA2` is easier to write - maybe we should consider that (although probably under a different name). \ No newline at end of file +There's already been plenty of chat about the `apply` vs. `merge` way of doing things, but some people also claim `liftA2` is easier to write - maybe we should consider that (although probably under a different name). + +### Answers from @dsyme + +Why was joinads rejected? It made various things significantly more complicated, e.g. pattern matching. + +`Map` can be added as an efficient desugaring of `let! x = y in return x + 1`, but `Map` will need an attribute on it to indicate the backwards incompatible optimisation is allowed by this builder. + +Smart CE builder optimisations, such as swapping out simple `Apply` usages for `Map` where possible, etc, won't be accepted because it'll make the "simple" desugaring not so simple. + +`Map` & `Merge` vs. `Pure` and `Apply` vs. `Pure` & `LiftA2`: The choice is largely arbitrary, pick whichever. \ No newline at end of file From 2306813914ab4c334b990922bc612debcafb81b4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 10 Sep 2018 17:58:16 +0100 Subject: [PATCH 057/255] Compare apply to liftA2 --- scratch/docs/Log.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index b651b8a6ac..277940a85e 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -292,9 +292,16 @@ Assuming we used the full suite of optimising desugarings (i.e. using `Map` in p |`let! pat1 = exp1 and! pat2 = exp2 in let! pat3 = exp1 in return pat1 + pat2 + pat3`| `builder.Apply(builder.Apply(builder.Apply(fun pat1 pat2 pat3 -> pat1 + pat2 + pat3), exp1), exp2), exp3)` | Use `Apply` rather than `Bind` despite final binding not being part of preceding `and!` chain. | `let! x = y in return x + 1` | `builder.Map((fun x -> x + 1), y)` | Use `Map` rather than `Bind` or `Apply` despite `let!` -### `liftA2` vs. `apply` vs. merge` +### `liftA2` vs. `apply` vs. `merge` -There's already been plenty of chat about the `apply` vs. `merge` way of doing things, but some people also claim `liftA2` is easier to write - maybe we should consider that (although probably under a different name). +There's already been plenty of chat about the `apply` vs. `merge` way of doing things, but some people also claim `liftA2` is easier to write (`apply` often prompts the question "why would I end up with a function in my functor" and then people get hung up on that). + +> From Hackage: +> `liftA2 :: (a -> b -> c) -> f a -> f b -> f c` +> +> Lift a binary function to actions. +> +> Some functors support an implementation of `liftA2` that is more efficient than the default one. In particular, if fmap is an expensive operation, it is likely better to use `liftA2` than to fmap over the structure and then use `apply`. We can also look at `liftA2` are a generalised `Merge` that avoids arbitrarily creating a tuple. With `liftA2` we can nicely introduce it along the lines of "it's like `map`, but where we can have 2 parameters". Or maybe that obscures the essence of the contribution an applicative provides over a functor? ### Answers from @dsyme @@ -304,4 +311,4 @@ Why was joinads rejected? It made various things significantly more complicated, Smart CE builder optimisations, such as swapping out simple `Apply` usages for `Map` where possible, etc, won't be accepted because it'll make the "simple" desugaring not so simple. -`Map` & `Merge` vs. `Pure` and `Apply` vs. `Pure` & `LiftA2`: The choice is largely arbitrary, pick whichever. \ No newline at end of file +`Map` & `Merge` vs. `Pure` & `Apply` vs. `Pure` & `LiftA2`: The choice is largely arbitrary, pick whichever. \ No newline at end of file From 06fd117629eafebc1ebc1284894f34a3e394c456 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 10 Sep 2018 18:21:54 +0100 Subject: [PATCH 058/255] Record decisions from today --- scratch/docs/Log.md | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 277940a85e..fd15aae597 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -289,7 +289,7 @@ Assuming we used the full suite of optimising desugarings (i.e. using `Map` in p | Expression | Translation | Optimisation | |-------------------------------|-------------|--------------| -|`let! pat1 = exp1 and! pat2 = exp2 in let! pat3 = exp1 in return pat1 + pat2 + pat3`| `builder.Apply(builder.Apply(builder.Apply(fun pat1 pat2 pat3 -> pat1 + pat2 + pat3), exp1), exp2), exp3)` | Use `Apply` rather than `Bind` despite final binding not being part of preceding `and!` chain. +|`let! pat1 = exp1 and! pat2 = exp2 in let! pat3 = exp1 in return pat1 + pat2 + pat3`| `builder.Apply(builder.Apply(builder.Apply(builder.Return(fun pat1 pat2 pat3 -> pat1 + pat2 + pat3), exp1), exp2), exp3)` | Use `Apply` rather than `Bind` despite final binding not being part of preceding `and!` chain. | `let! x = y in return x + 1` | `builder.Map((fun x -> x + 1), y)` | Use `Map` rather than `Bind` or `Apply` despite `let!` ### `liftA2` vs. `apply` vs. `merge` @@ -303,6 +303,37 @@ There's already been plenty of chat about the `apply` vs. `merge` way of doing t > > Some functors support an implementation of `liftA2` that is more efficient than the default one. In particular, if fmap is an expensive operation, it is likely better to use `liftA2` than to fmap over the structure and then use `apply`. We can also look at `liftA2` are a generalised `Merge` that avoids arbitrarily creating a tuple. With `liftA2` we can nicely introduce it along the lines of "it's like `map`, but where we can have 2 parameters". Or maybe that obscures the essence of the contribution an applicative provides over a functor? +Comparing `liftA2` and `apply` because `merge` introduces a tuple, which I find arbitrary and inelegant. + +```F# +val liftA2 : ('a -> 'b -> 'c) -> f 'a -> f 'b -> f 'c +``` +```F# +val apply : f ('a -> 'b) -> f 'a -> f 'b +``` + +Imagine a simple but presumably represenative example CE: +```F# +option { + let! a = aExpr + and! b = bExpr + and! c = cExpr + return a + b + c // We're explicitly returning here (i.e. calling `pure`, so this neatly maps into the `apply` desugaring - liftA2 presumes the function is unwrapped, so we'd be passing in (<|) to apply the lambda doing the summation anyway) +} +``` + +```F# +builder.Apply( + builder.Apply( + builder.Apply( + builder.Return(fun a b c -> a + b + c), + aExpr), + bExpr), + cExpr) +``` + +I assert `Apply` is the best way because the desugaring is most natural. The only unintuitive thing, perhaps, is that the first binding goes into the inner most `Apply` and we work outwards from there, which is the reverse of how you read the top-to-bottom CE. + ### Answers from @dsyme Why was joinads rejected? It made various things significantly more complicated, e.g. pattern matching. @@ -311,4 +342,12 @@ Why was joinads rejected? It made various things significantly more complicated, Smart CE builder optimisations, such as swapping out simple `Apply` usages for `Map` where possible, etc, won't be accepted because it'll make the "simple" desugaring not so simple. -`Map` & `Merge` vs. `Pure` & `Apply` vs. `Pure` & `LiftA2`: The choice is largely arbitrary, pick whichever. \ No newline at end of file +`Map` & `Merge` vs. `Pure` & `Apply` vs. `Pure` & `LiftA2`: The choice is largely arbitrary, pick whichever. + +### Decision Record + +* Nothing to worry about from the rejection of the joinads proposal - this change does not introduce the level of complexity that caused the issues in that case. + +* I will use `apply` because I am most familiar with this encoding and I think it desugars very clearly. + +* I will avoid `map` altogether - there is a separate suggestion and RFC for that and given @dsyme's points about not wanting more magic in the desugaring (e.g. using `apply` to implement `map` if `map` is not given explicitly, etc), these two changes should be orthogonal (although my work here is related in the sense that the skills needed to do one are very much like the skills needed to do the other). \ No newline at end of file From c720b5759be1efcaa07d95d0d3e766673c4eb86c Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 10 Sep 2018 18:28:27 +0100 Subject: [PATCH 059/255] Give an example of desugaring with let bindings --- scratch/docs/Log.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index fd15aae597..bd3e5589d8 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -332,6 +332,30 @@ builder.Apply( cExpr) ``` +And with some extra body in the middle: +```F# +option { + let! a = aExpr + and! b = bExpr + and! c = cExpr + let d = 3 + 4 + return (a + b + c) * d +} +``` +I presume it would desugar (have yet to confirm this is how `let`s work yet) as so: +```F# +builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + fun a b c -> + let d = 3 + 4 + (a + b + c) * d), + aExpr), + bExpr), + cExpr) +``` + I assert `Apply` is the best way because the desugaring is most natural. The only unintuitive thing, perhaps, is that the first binding goes into the inner most `Apply` and we work outwards from there, which is the reverse of how you read the top-to-bottom CE. ### Answers from @dsyme From ab3657b183bb1783dd58fcce264ebba084ff248f Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 10 Sep 2018 19:15:01 +0100 Subject: [PATCH 060/255] Try to make up to type checking run and stub everything else out with failwiths --- src/fsharp/TypeChecker.fs | 45 ++++++++++++++++++++++++++++----------- src/fsharp/ast.fs | 2 +- src/fsharp/pars.fsy | 7 +++--- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index badd093e4d..6a359fc0be 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -3470,7 +3470,7 @@ let (|SimpleSemicolonSequence|_|) acceptDeprecated c = | SynExpr.ForEach (_, _, _, _, _, body, _) -> YieldFree body | SynExpr.YieldOrReturnFrom _ | SynExpr.YieldOrReturn _ - | SynExpr.LetOrUseBang _ + | SynExpr.LetOrUseAndBang _ | SynExpr.ImplicitZero _ | SynExpr.Do _ -> false | _ -> true @@ -3489,7 +3489,7 @@ let (|SimpleSemicolonSequence|_|) acceptDeprecated c = | SynExpr.LetOrUse _ | SynExpr.Do _ | SynExpr.MatchBang _ - | SynExpr.LetOrUseBang _ + | SynExpr.LetOrUseAndBang _ | SynExpr.ImplicitZero _ | SynExpr.While _ -> false | _ -> true @@ -6133,7 +6133,7 @@ and TcExprUndelayed cenv overallTy env tpenv (expr: SynExpr) = | SynExpr.ImplicitZero m -> error(Error(FSComp.SR.tcConstructRequiresSequenceOrComputations(), m)) | SynExpr.DoBang (_, m) - | SynExpr.LetOrUseBang (_, _, _, _, _, _, m) -> + | SynExpr.LetOrUseAndBang (range=m) -> error(Error(FSComp.SR.tcConstructRequiresComputationExpression(), m)) | SynExpr.MatchBang (_, _, _, _, m) -> error(Error(FSComp.SR.tcConstructRequiresComputationExpression(), m)) @@ -7876,7 +7876,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // Rebind using either for ... or let!.... let rebind = if maintainsVarSpaceUsingBind then - SynExpr.LetOrUseBang(NoSequencePointAtLetBinding, false, false, intoPat, dataCompAfterOp, contExpr, intoPat.Range) + SynExpr.LetOrUseAndBang(NoSequencePointAtLetBinding, false, false, intoPat, dataCompAfterOp, intoPat.Range, [], contExpr) else SynExpr.ForEach (NoSequencePointAtForLoop, SeqExprOnly false, false, intoPat, dataCompAfterOp, contExpr, intoPat.Range) @@ -7898,7 +7898,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // Rebind using either for ... or let!.... let rebind = if lastUsesBind then - SynExpr.LetOrUseBang(NoSequencePointAtLetBinding, false, false, varSpacePat, dataCompPrior, compClausesExpr, compClausesExpr.Range) + SynExpr.LetOrUseAndBang(NoSequencePointAtLetBinding, false, false, varSpacePat, dataCompPrior, compClausesExpr.Range, [], compClausesExpr) else SynExpr.ForEach (NoSequencePointAtForLoop, SeqExprOnly false, false, varSpacePat, dataCompPrior, compClausesExpr, compClausesExpr.Range) @@ -7947,7 +7947,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | SuppressSequencePointOnStmtOfSequential -> SequencePointAtBinding m | SuppressSequencePointOnExprOfSequential -> NoSequencePointAtDoBinding | SequencePointsAtSeq -> SequencePointAtBinding m - Some(trans true q varSpace (SynExpr.LetOrUseBang(sp, false, true, SynPat.Const(SynConst.Unit, rhsExpr.Range), rhsExpr, innerComp2, m)) translatedCtxt) + Some(trans true q varSpace (SynExpr.LetOrUseAndBang(sp, false, true, SynPat.Const(SynConst.Unit, rhsExpr.Range), rhsExpr, m, [], innerComp2)) translatedCtxt) // "expr; cexpr" is treated as sequential execution | _ -> Some (trans true q varSpace innerComp2 (fun holeFill -> translatedCtxt (SynExpr.Sequential(sp, true, innerComp1, holeFill, m)))) @@ -8004,7 +8004,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some (translatedCtxt (mkSynCall "Using" bindRange [rhsExpr; consumeExpr ])) // 'let! pat = expr in expr' --> build.Bind(e1, (function _argN -> match _argN with pat -> expr)) - | SynExpr.LetOrUseBang(spBind, false, isFromSource, pat, rhsExpr, innerComp, _) -> + | SynExpr.LetOrUseAndBang(spBind, false, isFromSource, pat, rhsExpr, _, [], innerComp) -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) @@ -8024,8 +8024,8 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv translatedCtxt (mkSynCall "Bind" bindRange [rhsExpr; consumeExpr]))) // 'use! pat = e1 in e2' --> build.Bind(e1, (function _argN -> match _argN with pat -> build.Using(x, (fun _argN -> match _argN with pat -> e2)))) - | SynExpr.LetOrUseBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, innerComp, _) - | SynExpr.LetOrUseBang(spBind, true, isFromSource, (SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) as pat), rhsExpr, innerComp, _) -> + | SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, [], innerComp) + | SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) as pat), rhsExpr, _, [], innerComp) -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) @@ -8038,9 +8038,28 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some(translatedCtxt (mkSynCall "Bind" bindRange [rhsExpr; consumeExpr])) // 'use! pat = e1 in e2' where 'pat' is not a simple name --> error - | SynExpr.LetOrUseBang(_spBind, true, _isFromSource, pat, _rhsExpr, _innerComp, _) -> + | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) + // 'let! pat = expr and! pat = expr in expr' --> TODO + //| SynExpr.LetOrUseAndBang(spBind, false, isFromSource, pat, rhsExpr, _, andBangs, innerComp) -> + | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> + + failwith "TODO" + + // 'use! pat = e1 and! pat = e2 in e3' --> TODO + //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) + //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) as pat), rhsExpr, _, andBangs, innerComp) -> + | SynExpr.LetOrUseAndBang(_, true, _, (SynPat.Named (SynPat.Wild _, _, false, _, _)) , _, _, _, _) + | SynExpr.LetOrUseAndBang(_, true, _, (SynPat.LongIdent (LongIdentWithDots([_], _), _, _, _, _, _)), _, _, _, _) -> + + failwith "TODO" + + // 'use! pat = e1 and(Use)! pat = e2 in e3' where any 'pat' is not a simple name --> error + //| SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, andBangs, _innerComp) -> + | SynExpr.LetOrUseAndBang(_, true, _, _, _, _, _, _) -> + failwith "TODO" + | SynExpr.Match (spMatch, expr, clauses, false, m) -> let mMatch = match spMatch with SequencePointAtBinding mMatch -> mMatch | _ -> m if isQuery then error(Error(FSComp.SR.tcMatchMayNotBeUsedWithQuery(), mMatch)) @@ -8106,7 +8125,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv SynExpr.ImplicitZero m else SynExpr.YieldOrReturn((false, true), SynExpr.Const(SynConst.Unit, m), m) - trans true q varSpace (SynExpr.LetOrUseBang(NoSequencePointAtDoBinding, false, false, SynPat.Const(SynConst.Unit, mUnit), rhsExpr, bodyExpr, m)) translatedCtxt + trans true q varSpace (SynExpr.LetOrUseAndBang(NoSequencePointAtDoBinding, false, false, SynPat.Const(SynConst.Unit, mUnit), rhsExpr, m, [], bodyExpr)) translatedCtxt // "expr;" in final position is treated as { expr; zero } // Suppress the sequence point on the "zero" | _ -> @@ -8285,7 +8304,7 @@ and TcSequenceExpression cenv env tpenv comp overallTy m = //SEQPOINT NEEDED - we must consume spBind on this path Some(mkSeqUsing cenv env wholeExprMark bindPatTy genOuterTy inputExpr consumeExpr, tpenv) - | SynExpr.LetOrUseBang(_, _, _, _, _, _, m) -> + | SynExpr.LetOrUseAndBang(range=m) -> error(Error(FSComp.SR.tcUseForInSequenceExpression(), m)) | SynExpr.Match (spMatch, expr, clauses, false, _) -> @@ -8901,7 +8920,7 @@ and TcItemThen cenv overallTy env tpenv (item, mItem, rest, afterResolution) del | SynExpr.YieldOrReturn _ | SynExpr.YieldOrReturnFrom _ | SynExpr.MatchBang _ - | SynExpr.LetOrUseBang _ + | SynExpr.LetOrUseAndBang _ | SynExpr.DoBang _ | SynExpr.TraitCall _ -> false diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index 432db71419..430b834177 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -697,7 +697,7 @@ and /// F# syntax: let! pat = expr anduse! ... and! ... and! pat = expr in expr /// F# syntax: use! pat = expr and! ... anduse! ... and! pat = expr in expr /// Computation expressions only - | LetOrUseAndBang of bindSeqPoint:SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range:range * (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * SynExpr + | LetOrUseAndBang of bindSeqPoint:SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range:range * (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * SynExpr // TODO Why is the range not the last arg anymore? Consider swapping that back? /// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN | MatchBang of matchSeqPoint:SequencePointInfoForBinding * expr:SynExpr * clauses:SynMatchClause list * isExnMatch:bool * range:range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 244387a85b..0a457aabe1 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3004,10 +3004,10 @@ recover: /* EXPERIMENT */ binders: - | BINDER headBindingPattern EQUALS typedSeqExprBlock IN morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ + | BINDER headBindingPattern EQUALS typedSeqExprBlock morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) - let m = unionRanges (rhs parseState 1) $8.Range - SynExpr.LetOrUseAndBang(spBind,$6,$8,m) } + let m = unionRanges (rhs parseState 1) $7.Range + SynExpr.LetOrUseAndBang(spBind,$5,$7,m) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error @@ -3024,7 +3024,6 @@ binders: morebinders: - /* TODO Handle verbose and! */ | AND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders %prec expr_let /* TODO Check precedence */ { $5 (if $1 then "anduse!" else "and!") (rhs parseState 1) // report unterminated error From 6da5655e8b10b050d2af52ba52677eb50304760c Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 14:25:34 +0100 Subject: [PATCH 061/255] Make pars.fs build --- src/fsharp/ast.fs | 6 +++--- src/fsharp/pars.fsy | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index 430b834177..dbd2c4564d 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -697,7 +697,7 @@ and /// F# syntax: let! pat = expr anduse! ... and! ... and! pat = expr in expr /// F# syntax: use! pat = expr and! ... anduse! ... and! pat = expr in expr /// Computation expressions only - | LetOrUseAndBang of bindSeqPoint:SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range:range * (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * SynExpr // TODO Why is the range not the last arg anymore? Consider swapping that back? + | LetOrUseAndBang of bindSeqPoint:SequencePointInfoForBinding * bool * bool * SynPat * rhs:SynExpr * range:range * andBangs:(SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * body:SynExpr // TODO Why is the range not the last arg anymore? Consider swapping that back? /// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN | MatchBang of matchSeqPoint:SequencePointInfoForBinding * expr:SynExpr * clauses:SynMatchClause list * isExnMatch:bool * range:range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *) @@ -2429,6 +2429,6 @@ let rec synExprContainsError inpExpr = | SynExpr.MatchBang (_,e,cl,_,_) -> walkExpr e || walkMatchClauses cl - | SynExpr.LetOrUseBang (_,_,_,_,e1,e2,_) -> - walkExpr e1 || walkExpr e2 + | SynExpr.LetOrUseAndBang (rhs=e1;body=e2;andBangs=es) -> + walkExpr e1 || walkExprs [ for (_,_,_,_,e,_) in es do yield e ] || walkExpr e2 walkExpr inpExpr diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 0a457aabe1..96a8af6d9e 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3004,23 +3004,23 @@ recover: /* EXPERIMENT */ binders: - | BINDER headBindingPattern EQUALS typedSeqExprBlock morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ + | BINDER headBindingPattern EQUALS typedSeqExprBlock IN morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) - let m = unionRanges (rhs parseState 1) $7.Range - SynExpr.LetOrUseAndBang(spBind,$5,$7,m) } + let m = unionRanges (rhs parseState 1) $8.Range + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$6,$8) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let m = unionRanges (rhs parseState 1) $8.Range - SynExpr.LetOrUseAndBang(spBind,$6,$8,m) } + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$6,$8) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) let m = $4.Range.EndRange // zero-width range - SynExpr.LetOrUseAndBang(spBind,$6, SynExpr.ImplicitZero m, mAll) } + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } morebinders: @@ -3330,11 +3330,11 @@ declExpr: { SynExpr.YieldOrReturnFrom(($1,not $1), $2, unionRanges (rhs parseState 1) $2.Range) } | binders - { } + { $1 } | DO_BANG typedSeqExpr IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { let spBind = NoSequencePointAtDoBinding - SynExpr.LetOrUseBang(spBind,false,true,SynPat.Const(SynConst.Unit,$2.Range),$2,$5, unionRanges (rhs parseState 1) $5.Range) } + SynExpr.LetOrUseAndBang(spBind,false,true,SynPat.Const(SynConst.Unit,$2.Range),$2, unionRanges (rhs parseState 1) $5.Range, [], $5) } | ODO_BANG typedSeqExprBlock hardwhiteDefnBindingsTerminator %prec expr_let { SynExpr.DoBang($2, unionRanges (rhs parseState 1) $2.Range) } From 7a934dd2a23a835e4b3c4d9b765b0bd8a37d0b01 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 14:39:17 +0100 Subject: [PATCH 062/255] Fix build errors in ServiceLexing and Service ParseTreeWalk --- src/fsharp/service/ServiceLexing.fs | 2 +- src/fsharp/service/ServiceParseTreeWalk.fs | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index 343fc20317..066d244ced 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -263,7 +263,7 @@ module internal TokenClassifications = | DOWNTO | EXCEPTION | FALSE | FOR | FUN | FUNCTION | FINALLY | LAZY | MATCH | MATCH_BANG | MUTABLE | NEW | OF | OPEN | OR | VOID | EXTERN | INTERFACE | REC | TO | TRUE | TRY | TYPE | VAL | INLINE | WHEN | WHILE | WITH - | IF | THEN | ELSE | DO | DONE | LET(_) | IN (*| NAMESPACE*) | CONST + | IF | THEN | ELSE | DO | DONE | LET(_) | AND_BANG(_) | IN (*| NAMESPACE*) | CONST | HIGH_PRECEDENCE_PAREN_APP | FIXED | HIGH_PRECEDENCE_BRACK_APP | TYPE_COMING_SOON | TYPE_IS_HERE | MODULE_COMING_SOON | MODULE_IS_HERE diff --git a/src/fsharp/service/ServiceParseTreeWalk.fs b/src/fsharp/service/ServiceParseTreeWalk.fs index 7f94652a18..b7f8476aa6 100755 --- a/src/fsharp/service/ServiceParseTreeWalk.fs +++ b/src/fsharp/service/ServiceParseTreeWalk.fs @@ -436,10 +436,17 @@ module public AstTraversal = | SynExpr.ImplicitZero(_range) -> None | SynExpr.YieldOrReturn(_, synExpr, _range) -> traverseSynExpr synExpr | SynExpr.YieldOrReturnFrom(_, synExpr, _range) -> traverseSynExpr synExpr - | SynExpr.LetOrUseBang(_sequencePointInfoForBinding, _, _, synPat, synExpr, synExpr2, _range) -> - [dive synPat synPat.Range traversePat - dive synExpr synExpr.Range traverseSynExpr - dive synExpr2 synExpr2.Range traverseSynExpr] + | SynExpr.LetOrUseAndBang(_sequencePointInfoForBinding, _, _, synPat, synExpr, _range, andBangSynExprs, synExpr2) -> + seq { + yield dive synPat synPat.Range traversePat + yield dive synExpr synExpr.Range traverseSynExpr + yield! + [ for (_,_,_,andBangSynPat,andBangSynExpr,_) in andBangSynExprs do + yield (dive andBangSynPat andBangSynPat.Range traversePat) + yield (dive andBangSynExpr andBangSynExpr.Range traverseSynExpr)] + yield dive synExpr2 synExpr2.Range traverseSynExpr + } + |> List.ofSeq |> pick expr | SynExpr.MatchBang(_sequencePointInfoForBinding, synExpr, synMatchClauseList, _, _range) -> [yield dive synExpr synExpr.Range traverseSynExpr From 76a798489827d69640e66fc2881fd8e72026a9bf Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 14:46:07 +0100 Subject: [PATCH 063/255] Fix build errors in ServiceUntypedParse --- src/fsharp/service/ServiceUntypedParse.fs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 18cb8023de..0fcbc78e36 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -312,9 +312,11 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: Ast.ParsedInput op yield! walkExpr false e2 yield! walkExpr false e3 - | SynExpr.LetOrUseBang (spBind,_,_,_,e1,e2,_) -> + | SynExpr.LetOrUseAndBang (spBind,_,_,_,e1,_,es,e2) -> yield! walkBindSeqPt spBind yield! walkExpr true e1 + for (_,_,_,_,eAndBang,_) in es do + yield! walkExpr true eAndBang yield! walkExpr true e2 | SynExpr.MatchBang (spBind,e,cl,_,_) -> @@ -871,7 +873,15 @@ module UntypedParseImpl = | SynExpr.YieldOrReturnFrom(_, e, _) -> walkExprWithKind parentKind e | (SynExpr.Match(_, e, synMatchClauseList, _, _) | SynExpr.MatchBang(_, e, synMatchClauseList, _, _)) -> walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkClause synMatchClauseList) - | SynExpr.LetOrUseBang(_, _, _, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] + | SynExpr.LetOrUseAndBang(_, _, _, _, e1, _, es, e2) -> + seq { + yield e1 + for (_,_,_,_,eAndBang,_) in es do + yield eAndBang + yield e2 + } + |> List.ofSeq + |> List.tryPick (walkExprWithKind parentKind) | SynExpr.DoBang(e, _) -> walkExprWithKind parentKind e | SynExpr.TraitCall (ts, sign, e, _) -> List.tryPick walkTypar ts From ec7a4ba168b0d9cf750b991061f55558c05fb424 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 15:06:37 +0100 Subject: [PATCH 064/255] Fix remaining build errors from updating the AST & simplify seq CE usages to list comprehensions --- src/fsharp/service/ServiceAssemblyContent.fs | 10 ++++++-- .../service/ServiceInterfaceStubGenerator.fs | 10 ++++++-- src/fsharp/service/ServiceParseTreeWalk.fs | 5 ++-- src/fsharp/service/ServiceStructure.fs | 23 ++++++++++++------- src/fsharp/service/ServiceUntypedParse.fs | 5 ++-- 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/fsharp/service/ServiceAssemblyContent.fs b/src/fsharp/service/ServiceAssemblyContent.fs index a61f8f2b2f..08334189a6 100644 --- a/src/fsharp/service/ServiceAssemblyContent.fs +++ b/src/fsharp/service/ServiceAssemblyContent.fs @@ -705,9 +705,15 @@ module ParsedInput = addLongIdentWithDots ident List.iter walkExpr [e1; e2; e3] | SynExpr.JoinIn (e1, _, e2, _) -> List.iter walkExpr [e1; e2] - | SynExpr.LetOrUseBang (_, _, _, pat, e1, e2, _) -> + | SynExpr.LetOrUseAndBang (_, _, _, pat, e1, _, es, e2) -> walkPat pat - List.iter walkExpr [e1; e2] + [ + yield e1 + for (_,_,_,_,eAndBang,_) in es do + yield eAndBang + yield e2 + ] + |> List.iter walkExpr | SynExpr.TraitCall (ts, sign, e, _) -> List.iter walkTypar ts walkMemberSig sign diff --git a/src/fsharp/service/ServiceInterfaceStubGenerator.fs b/src/fsharp/service/ServiceInterfaceStubGenerator.fs index 5d093079f9..c49f7a6e80 100644 --- a/src/fsharp/service/ServiceInterfaceStubGenerator.fs +++ b/src/fsharp/service/ServiceInterfaceStubGenerator.fs @@ -894,8 +894,14 @@ module internal InterfaceStubGenerator = | SynExpr.DoBang(synExpr, _range) -> walkExpr synExpr - | SynExpr.LetOrUseBang(_sequencePointInfoForBinding, _, _, _synPat, synExpr1, synExpr2, _range) -> - List.tryPick walkExpr [synExpr1; synExpr2] + | SynExpr.LetOrUseAndBang(_sequencePointInfoForBinding, _, _, _synPat, synExpr1, _range, synExprAndBangs, synExpr2) -> + [ + yield synExpr1 + for (_,_,_,_,eAndBang,_) in synExprAndBangs do + yield eAndBang + yield synExpr2 + ] + |> List.tryPick walkExpr | SynExpr.LibraryOnlyILAssembly _ | SynExpr.LibraryOnlyStaticOptimization _ diff --git a/src/fsharp/service/ServiceParseTreeWalk.fs b/src/fsharp/service/ServiceParseTreeWalk.fs index b7f8476aa6..190238bf8b 100755 --- a/src/fsharp/service/ServiceParseTreeWalk.fs +++ b/src/fsharp/service/ServiceParseTreeWalk.fs @@ -437,7 +437,7 @@ module public AstTraversal = | SynExpr.YieldOrReturn(_, synExpr, _range) -> traverseSynExpr synExpr | SynExpr.YieldOrReturnFrom(_, synExpr, _range) -> traverseSynExpr synExpr | SynExpr.LetOrUseAndBang(_sequencePointInfoForBinding, _, _, synPat, synExpr, _range, andBangSynExprs, synExpr2) -> - seq { + [ yield dive synPat synPat.Range traversePat yield dive synExpr synExpr.Range traverseSynExpr yield! @@ -445,8 +445,7 @@ module public AstTraversal = yield (dive andBangSynPat andBangSynPat.Range traversePat) yield (dive andBangSynExpr andBangSynExpr.Range traverseSynExpr)] yield dive synExpr2 synExpr2.Range traverseSynExpr - } - |> List.ofSeq + ] |> pick expr | SynExpr.MatchBang(_sequencePointInfoForBinding, synExpr, synMatchClauseList, _, _range) -> [yield dive synExpr synExpr.Range traverseSynExpr diff --git a/src/fsharp/service/ServiceStructure.fs b/src/fsharp/service/ServiceStructure.fs index c86358c8f5..662181a95f 100644 --- a/src/fsharp/service/ServiceStructure.fs +++ b/src/fsharp/service/ServiceStructure.fs @@ -244,14 +244,21 @@ module Structure = | SynExpr.DoBang (e,r) -> rcheck Scope.Do Collapse.Below r <| Range.modStart 3 r parseExpr e - | SynExpr.LetOrUseBang (_,_,_,pat,e1,e2,_) -> - // for `let!` or `use!` the pattern begins at the end of the keyword so that - // this scope can be used without adjustment if there is no `=` on the same line - // if there is an `=` the range will be adjusted during the tooltip creation - let r = Range.endToEnd pat.Range e1.Range - rcheck Scope.LetOrUseBang Collapse.Below r r - parseExpr e1 - parseExpr e2 + | SynExpr.LetOrUseAndBang (_,_,_,pat,eLet,_,es,eBody) -> + [ + yield eLet + yield! [ for (_,_,_,_,eAndBang,_) in es do yield eAndBang ] + ] + |> List.iter (fun e -> + // for `let!`, `use!`, `and!` or `anduse!` the pattern begins at the end of the + // keyword so that this scope can be used without adjustment if there is no `=` + // on the same line if there is an `=` the range will be adjusted during the + // tooltip creation + let r = Range.endToEnd pat.Range e.Range + rcheck Scope.LetOrUseBang Collapse.Below r r + parseExpr e + ) + parseExpr eBody | SynExpr.For (_,_,_,_,_,e,r) | SynExpr.ForEach (_,_,_,_,_,e,r) -> rcheck Scope.For Collapse.Below r r diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 0fcbc78e36..29239e1353 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -874,13 +874,12 @@ module UntypedParseImpl = | (SynExpr.Match(_, e, synMatchClauseList, _, _) | SynExpr.MatchBang(_, e, synMatchClauseList, _, _)) -> walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkClause synMatchClauseList) | SynExpr.LetOrUseAndBang(_, _, _, _, e1, _, es, e2) -> - seq { + [ yield e1 for (_,_,_,_,eAndBang,_) in es do yield eAndBang yield e2 - } - |> List.ofSeq + ] |> List.tryPick (walkExprWithKind parentKind) | SynExpr.DoBang(e, _) -> walkExprWithKind parentKind e | SynExpr.TraitCall (ts, sign, e, _) -> From 296b505d6bee02d670359b6c0ab71b0983488991 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 15:11:32 +0100 Subject: [PATCH 065/255] Add notes on surrounding work to support new feature --- scratch/docs/Log.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index bd3e5589d8..d2e2f271d8 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -374,4 +374,13 @@ Smart CE builder optimisations, such as swapping out simple `Apply` usages for ` * I will use `apply` because I am most familiar with this encoding and I think it desugars very clearly. -* I will avoid `map` altogether - there is a separate suggestion and RFC for that and given @dsyme's points about not wanting more magic in the desugaring (e.g. using `apply` to implement `map` if `map` is not given explicitly, etc), these two changes should be orthogonal (although my work here is related in the sense that the skills needed to do one are very much like the skills needed to do the other). \ No newline at end of file +* I will avoid `map` altogether - there is a separate suggestion and RFC for that and given @dsyme's points about not wanting more magic in the desugaring (e.g. using `apply` to implement `map` if `map` is not given explicitly, etc), these two changes should be orthogonal (although my work here is related in the sense that the skills needed to do one are very much like the skills needed to do the other). + +## 2018-09-11 + +Beyond making `let! ... and! ...` appear to work, I'll need to: +* Come up with a comprehensive set of tests +* Preempt some common errors and make sure the messages are and least somewhat helpful +* Think about how to update the CE docs on MSDN and whatever hover-over tips VS gives, etc +* Explore the debugging, step-through experience +* Explore the "find definition" experience \ No newline at end of file From 28098ece010d2641b69ed8e5e6c9c3de27863fbc Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 15:31:04 +0100 Subject: [PATCH 066/255] Prepare for testing up to and excluding type checking --- scratch/data/SimpleBuilder.fsx | 18 +++++++++--------- scratch/docs/Log.md | 3 ++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 1925b5f6e6..23e34da105 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -9,13 +9,13 @@ type OptionalBuilder = | None -> None // TODO Make this actually get called when `let! ... and! ...` syntax is used (and automagically if RHSs allow it?) - member __.Apply(x : 'a option, f : ('a -> 'b) option) : 'b option = + member __.Apply(f : ('a -> 'b) option, x : 'a option) : 'b option = match f, x with | Some f, Some x -> f x | _ -> None // TODO Not needed, but for maximum efficiency, we want to use this if it is defined - member __.Map(x : 'a option, f : 'a -> 'b) : 'b option = + member __.Map(f : 'a -> 'b, x : 'a option) : 'b option = match x with | Some x -> f x | None -> None @@ -25,15 +25,15 @@ type OptionalBuilder = let opt = OptionalBuilder() -let x = Some 1 -let y = Some "A" -let z : float option = None +let xOpt = Some 1 +let yOpt = Some "A" +let zOpt = None let foo = opt { - let! x' = x - let! y' = y - let! z' = z + let! x = xOpt + and! y = yOpt + and! z = zOpt return sprintf "x = %d, y = %s, z = %f" x y z } @@ -45,4 +45,4 @@ let foo' = and! z' = z return sprintf "x = %d, y = %s, z = %f" x y z } -*) \ No newline at end of file +*) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index d2e2f271d8..2d14068304 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -383,4 +383,5 @@ Beyond making `let! ... and! ...` appear to work, I'll need to: * Preempt some common errors and make sure the messages are and least somewhat helpful * Think about how to update the CE docs on MSDN and whatever hover-over tips VS gives, etc * Explore the debugging, step-through experience -* Explore the "find definition" experience \ No newline at end of file +* Explore the "find definition" experience +* Chase down "The result of this equality expression is discarded..." \ No newline at end of file From d836dba4772c220e474d1540fe0f94ab60a6ce42 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 15:40:15 +0100 Subject: [PATCH 067/255] Fix up SimpleBuilder.fsx --- scratch/data/SimpleBuilder.fsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 23e34da105..3e016e72bf 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -9,15 +9,15 @@ type OptionalBuilder = | None -> None // TODO Make this actually get called when `let! ... and! ...` syntax is used (and automagically if RHSs allow it?) - member __.Apply(f : ('a -> 'b) option, x : 'a option) : 'b option = - match f, x with - | Some f, Some x -> f x + member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = + match fOpt, xOpt with + | Some f, Some x -> Some <| f x | _ -> None // TODO Not needed, but for maximum efficiency, we want to use this if it is defined - member __.Map(f : 'a -> 'b, x : 'a option) : 'b option = - match x with - | Some x -> f x + member __.Map(f : 'a -> 'b, xOpt : 'a option) : 'b option = + match xOpt with + | Some x -> Some <| f x | None -> None member __.Return(x) = From 312244446bea05915e5207e43cab63d3de15260a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 15:40:46 +0100 Subject: [PATCH 068/255] Add syntax error msg for and! keyword --- src/fsharp/CompileOps.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index d54373c554..755bec74e3 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -996,6 +996,7 @@ let OutputPhasedErrorR (os:StringBuilder) (err:PhasedDiagnostic) = | Parser.TOKEN_OLET(_) -> getErrorString("Parser.TOKEN.OLET") | Parser.TOKEN_OBINDER | Parser.TOKEN_BINDER -> getErrorString("Parser.TOKEN.BINDER") + | Parser.TOKEN_AND_BANG -> getErrorString("Parser.TOKEN.AND.BANG") | Parser.TOKEN_ODO -> getErrorString("Parser.TOKEN.ODO") | Parser.TOKEN_OWITH -> getErrorString("Parser.TOKEN.OWITH") | Parser.TOKEN_OFUNCTION -> getErrorString("Parser.TOKEN.OFUNCTION") From 51358d516f09867437d84cac35c19c656c58e839 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 16:03:40 +0100 Subject: [PATCH 069/255] Note that we'll want to check both light and verbose syntax --- scratch/docs/Log.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 2d14068304..a40ed34986 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -384,4 +384,5 @@ Beyond making `let! ... and! ...` appear to work, I'll need to: * Think about how to update the CE docs on MSDN and whatever hover-over tips VS gives, etc * Explore the debugging, step-through experience * Explore the "find definition" experience -* Chase down "The result of this equality expression is discarded..." \ No newline at end of file +* Chase down "The result of this equality expression is discarded..." +* Make sure both light and verbose syntax look sensible \ No newline at end of file From 9e0f93709068b36c2634651816c6894982babdd6 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 16:46:05 +0100 Subject: [PATCH 070/255] Add link to docs on different syntaxes --- scratch/docs/Log.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index a40ed34986..a86bff0364 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -385,4 +385,6 @@ Beyond making `let! ... and! ...` appear to work, I'll need to: * Explore the debugging, step-through experience * Explore the "find definition" experience * Chase down "The result of this equality expression is discarded..." -* Make sure both light and verbose syntax look sensible \ No newline at end of file +* Make sure both light and verbose syntax look sensible + +Still trying to make the syntax parse properly. I suspect this is to do with the different syntaxes: https://fsharpforfunandprofit.com/posts/fsharp-syntax/ From 47ae84ffccd8e6da972f0eb6c0dd1efba03c6b92 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 17:05:52 +0100 Subject: [PATCH 071/255] Add notes on implementing offside rule for and! --- scratch/docs/Log.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index a86bff0364..0a4f9e612e 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -388,3 +388,18 @@ Beyond making `let! ... and! ...` appear to work, I'll need to: * Make sure both light and verbose syntax look sensible Still trying to make the syntax parse properly. I suspect this is to do with the different syntaxes: https://fsharpforfunandprofit.com/posts/fsharp-syntax/ + +From the debugging: `LET: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n` when filtering `BINDER`. How does thhis need to change to accomodate `and!`? Need to explore `CtxtLetDecl` to get started. + +Okay, I think this: +```F# + // let! ... ~~~> CtxtLetDecl + | BINDER b, (ctxt :: _) -> + let blockLet = match ctxt with CtxtSeqBlock _ -> true | _ -> false + if debug then dprintf "LET: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n" blockLet outputPos tokenStartPos + pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos)) + returnToken tokenLexbufState (if blockLet then OBINDER b else token) +``` +is about runs of let bindings preserving the light syntax. `SeqBlock` is the name for a series of let-bindings in a row? + +By the looks of it, the corollery is that I probably do want to support light and verbose syntax, and then I can largely copy the above lex filter stuff for `and!`. \ No newline at end of file From 8014dd4dc7c08228c6d31ce007a7e992b1372aee Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 17:06:34 +0100 Subject: [PATCH 072/255] Add clear error msg when we hit the unimplemented and! SeqBlock logic --- src/fsharp/LexFilter.fs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index a0985a41f1..ae52e5d669 100755 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -1719,6 +1719,9 @@ type LexFilterImpl (lightSyntaxStatus:LightSyntaxStatus, compilingFsLib, lexer, pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos)) returnToken tokenLexbufState (if blockLet then OBINDER b else token) + | AND_BANG isUse, (ctxt :: _) -> + failwithf "TODO - Don't know how to handle 'and!' in lextfilter yet. isUse = %b, ctxt = %A+" isUse ctxt + | (VAL | STATIC | ABSTRACT | MEMBER | OVERRIDE | DEFAULT), ctxtStack when thereIsACtxtMemberBodyOnTheStackAndWeShouldPopStackForUpcomingMember ctxtStack -> if debug then dprintf "STATIC/MEMBER/OVERRIDE/DEFAULT: already inside CtxtMemberBody, popping all that context before starting next member...\n" // save this token, we'll consume it again later... From 40fd6929b65c3884f74495fc552528ff60048fa3 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 17:25:38 +0100 Subject: [PATCH 073/255] Point to the spec regarding offside rule --- scratch/docs/Log.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 0a4f9e612e..2598e13785 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -402,4 +402,6 @@ Okay, I think this: ``` is about runs of let bindings preserving the light syntax. `SeqBlock` is the name for a series of let-bindings in a row? -By the looks of it, the corollery is that I probably do want to support light and verbose syntax, and then I can largely copy the above lex filter stuff for `and!`. \ No newline at end of file +By the looks of it, the corollery is that I probably do want to support light and verbose syntax, and then I can largely copy the above lex filter stuff for `and!`. + +[The spec](https://fsharp.org/specs/language-spec/4.0/FSharpSpec-4.0-latest.pdf) covers offside contexts (section 15.1.6, page 282). \ No newline at end of file From 29e7e78a754d18b7ed6a2a5edf2a69d6f037c13b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 17:54:50 +0100 Subject: [PATCH 074/255] Add beginnings of offside rule for and! --- src/fsharp/CompileOps.fs | 1 + src/fsharp/Fsc/Fsc.fsproj | 24 +++++++----------------- src/fsharp/LexFilter.fs | 6 +++++- src/fsharp/pars.fsy | 17 +++++++++++++++-- src/fsharp/service/ServiceLexing.fs | 2 +- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 755bec74e3..bd6ee97cd7 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -996,6 +996,7 @@ let OutputPhasedErrorR (os:StringBuilder) (err:PhasedDiagnostic) = | Parser.TOKEN_OLET(_) -> getErrorString("Parser.TOKEN.OLET") | Parser.TOKEN_OBINDER | Parser.TOKEN_BINDER -> getErrorString("Parser.TOKEN.BINDER") + | Parser.TOKEN_OAND_BANG | Parser.TOKEN_AND_BANG -> getErrorString("Parser.TOKEN.AND.BANG") | Parser.TOKEN_ODO -> getErrorString("Parser.TOKEN.ODO") | Parser.TOKEN_OWITH -> getErrorString("Parser.TOKEN.OWITH") diff --git a/src/fsharp/Fsc/Fsc.fsproj b/src/fsharp/Fsc/Fsc.fsproj index aa6c8eed8f..5224032505 100644 --- a/src/fsharp/Fsc/Fsc.fsproj +++ b/src/fsharp/Fsc/Fsc.fsproj @@ -1,7 +1,6 @@ - $(MSBuildProjectDirectory)\..\.. FSharp @@ -25,12 +24,12 @@ true $(OtherFlags) --warnon:1182 - + + C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx + + - - - fscmain.fs @@ -39,19 +38,11 @@ default.win32manifest PreserveNewest + - - - - - + - - - - - {2E4D67B4-522D-4CF7-97E4-BA940F0B18F3} FSharp.Compiler.Private @@ -61,5 +52,4 @@ FSharp.Core - - + \ No newline at end of file diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index ae52e5d669..4854b15310 100755 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -1719,8 +1719,12 @@ type LexFilterImpl (lightSyntaxStatus:LightSyntaxStatus, compilingFsLib, lexer, pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos)) returnToken tokenLexbufState (if blockLet then OBINDER b else token) + // and! ... ~~~> CtxtLetDecl | AND_BANG isUse, (ctxt :: _) -> - failwithf "TODO - Don't know how to handle 'and!' in lextfilter yet. isUse = %b, ctxt = %A+" isUse ctxt + let blockLet = match ctxt with CtxtSeqBlock _ -> true | _ -> false + if debug then dprintf "AND!: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n" blockLet outputPos tokenStartPos + pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos)) + returnToken tokenLexbufState (if blockLet then OAND_BANG isUse else token) | (VAL | STATIC | ABSTRACT | MEMBER | OVERRIDE | DEFAULT), ctxtStack when thereIsACtxtMemberBodyOnTheStackAndWeShouldPopStackForUpcomingMember ctxtStack -> if debug then dprintf "STATIC/MEMBER/OVERRIDE/DEFAULT: already inside CtxtMemberBody, popping all that context before starting next member...\n" diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 96a8af6d9e..b780d22ad9 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -215,6 +215,7 @@ let rangeOfLongIdent(lid:LongIdent) = /* for offside rule */ %token OLET /* LexFilter #light converts 'LET' tokens to 'OLET' when starting (CtxtLetDecl(blockLet=true)) */ %token OBINDER /* LexFilter #light converts 'BINDER' tokens to 'OBINDER' when starting (CtxtLetDecl(blockLet=true)) */ +%token OAND_BANG /* LexFilter #light converts 'AND_BANG' tokens to 'OAND_BANG' when starting (CtxtLetDecl(blockLet=true)) */ %token ODO /* LexFilter #light converts 'DO' tokens to 'ODO' */ %token ODO_BANG /* LexFilter #light converts 'DO_BANG' tokens to 'ODO_BANG' */ %token OTHEN /* LexFilter #light converts 'THEN' tokens to 'OTHEN' */ @@ -3025,12 +3026,24 @@ binders: morebinders: - | AND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders %prec expr_let /* TODO Check precedence */ + | AND_BANG headBindingPattern EQUALS typedSeqExprBlock IN morebinders %prec expr_let /* TODO Check precedence */ + { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *) + let m = rhs parseState 1 (* TODO Pretty sure this is wrong *) + (spBind,$1,true,$2,$4,m) :: $6 } + + | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { $5 (if $1 then "anduse!" else "and!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) - let m = rhs parseState 1 + let m = unionRanges (rhs parseState 1) $8.Range (spBind,$1,true,$2,$4,m) :: $6 } + | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP error %prec expr_let + { // error recovery that allows intellisense when writing incomplete computation expressions + let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) + let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) + let m = $4.Range.EndRange // zero-width range + [ spBind,$1,true,$2,$4, mAll ] } /* TODO Does this whole non-terminal make sense? */ + | %prec prec_no_more_attr_bindings /* TODO Make a prec_no_more_andbang_bindings or something */ { [] } diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index 066d244ced..3a2eeb7dcd 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -257,7 +257,7 @@ module internal TokenClassifications = | PUBLIC | PRIVATE | INTERNAL | BASE | GLOBAL | CONSTRAINT | INSTANCE | DELEGATE | INHERIT|CONSTRUCTOR|DEFAULT|OVERRIDE|ABSTRACT|CLASS | MEMBER | STATIC | NAMESPACE - | OASSERT | OLAZY | ODECLEND | OBLOCKSEP | OEND | OBLOCKBEGIN | ORIGHT_BLOCK_END | OBLOCKEND | OBLOCKEND_COMING_SOON | OBLOCKEND_IS_HERE | OTHEN | OELSE | OLET(_) | OBINDER _ | BINDER _ | ODO | OWITH | OFUNCTION | OFUN | ORESET | ODUMMY _ | DO_BANG | ODO_BANG | YIELD _ | YIELD_BANG _ | OINTERFACE_MEMBER + | OASSERT | OLAZY | ODECLEND | OBLOCKSEP | OEND | OBLOCKBEGIN | ORIGHT_BLOCK_END | OBLOCKEND | OBLOCKEND_COMING_SOON | OBLOCKEND_IS_HERE | OTHEN | OELSE | OLET(_) | OBINDER _ | OAND_BANG _ | BINDER _ | ODO | OWITH | OFUNCTION | OFUN | ORESET | ODUMMY _ | DO_BANG | ODO_BANG | YIELD _ | YIELD_BANG _ | OINTERFACE_MEMBER | ELIF | RARROW | LARROW | SIG | STRUCT | UPCAST | DOWNCAST | NULL | RESERVED | MODULE | AND | AS | ASSERT | ASR | DOWNTO | EXCEPTION | FALSE | FOR | FUN | FUNCTION From 426ed0b69dcb0f48da1156c676ba9612a7812999 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 18:09:20 +0100 Subject: [PATCH 075/255] Fix offside and! compile issues in parser --- src/fsharp/pars.fsy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index b780d22ad9..cbccfabd6e 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3031,13 +3031,13 @@ morebinders: let m = rhs parseState 1 (* TODO Pretty sure this is wrong *) (spBind,$1,true,$2,$4,m) :: $6 } - | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let + | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders %prec expr_let { $5 (if $1 then "anduse!" else "and!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) - let m = unionRanges (rhs parseState 1) $8.Range - (spBind,$1,true,$2,$4,m) :: $6 } + let m = rhs parseState 1 (* TODO Pretty sure this is wrong *) + (spBind,$1,true,$2,$4,m) :: $7 } - | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP error %prec expr_let + | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) From 737bac1f89068d5d2ed328529c2ba09cbbe3f270 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 20:31:06 +0100 Subject: [PATCH 076/255] Add notes from debugging parsing --- scratch/docs/Log.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 2598e13785..e5f5b66dd7 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -404,4 +404,13 @@ is about runs of let bindings preserving the light syntax. `SeqBlock` is the nam By the looks of it, the corollery is that I probably do want to support light and verbose syntax, and then I can largely copy the above lex filter stuff for `and!`. -[The spec](https://fsharp.org/specs/language-spec/4.0/FSharpSpec-4.0-latest.pdf) covers offside contexts (section 15.1.6, page 282). \ No newline at end of file +[The spec](https://fsharp.org/specs/language-spec/4.0/FSharpSpec-4.0-latest.pdf) covers offside contexts (section 15.1.6, page 282). + +> The `SeqBlock` context is the primary context of the analysis. It indicates a sequence of items that must be column-aligned. Where necessary for parsing, the compiler automatically inserts a delimiter that replaces the regular `in` and `;` tokens between the syntax elements. + +Does `isLetContinuator` need `AND_BANG` and `OAND_BANG` adding to it? I think no, else we keep the offside position from the first `let!`? + +Editor index lines and columns from 1. Compiler debug output indexs from 0. Consider putting in an issue for this bjorn and chethusk seem to be in favour. + +For posterity, I am currently invoking the compiler as so: +`fsc.exe --parseonly --debug+ --debug-parse SimpleBuilder.fsx` \ No newline at end of file From fb5e729899c4e11760744ebee141a9f635b91950 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 21:08:25 +0100 Subject: [PATCH 077/255] Add braindump when trying to debug mysterious parse fail --- scratch/docs/Log.md | 64 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index e5f5b66dd7..0204ba07b2 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -413,4 +413,66 @@ Does `isLetContinuator` need `AND_BANG` and `OAND_BANG` adding to it? I think no Editor index lines and columns from 1. Compiler debug output indexs from 0. Consider putting in an issue for this bjorn and chethusk seem to be in favour. For posterity, I am currently invoking the compiler as so: -`fsc.exe --parseonly --debug+ --debug-parse SimpleBuilder.fsx` \ No newline at end of file +`fsc.exe --parseonly --debug+ --debug-parse SimpleBuilder.fsx` + +Once theory I have about what I was doing wrong was having `and!` mess with the offisde rule. Maybe it should work just like `and` does? That seems sensible intutively modulo that `and!`s exist "inside" the `let!` that precedes it. EDIT: Either way, my logic fails, so I guess the error is somewhere else in the lexer or the subsequent filtering. Is there a way to just print the tokens post-filtering? + +`--tokenize` seems to be working okay. From what I can see, an `OBLOCKEND` is inserted before the `and!`. Is that correct? + +``` +offside token at column 8 indicates end of CtxtSeqBlock started at (34:12)! +<-- popping Context(seqblock(subsequent,(34:12))), stack = [let(true,(33:8)); seqblock(subsequent,(33:8)); paren; vanilla((32:4)); + seqblock(subsequent,(32:4)); let(true,(31:0)); seqblock(subsequent,(2:0))] +end of CtxtSeqBlock, insert OBLOCKEND +inserting OBLOCKEND +``` + +Looks like we get as far as reading the identifier after the `and!` too: +``` +AND!: entering CtxtLetDecl(blockLet=true), awaiting EQUALS to go to CtxtSeqBlock ((35:8)) +--> pushing, stack = [let(true,(35:8)); seqblock(subsequent,(33:8)); paren; vanilla((32:4)); + seqblock(subsequent,(32:4)); let(true,(31:0)); seqblock(subsequent,(2:0))] +tokenize - got OAND_BANG @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(36,8)-(36,12) +tokenize - getting one token from scratch\data\SimpleBuilder.fsx +popNextTokenTup: no delayed tokens, running lexer... +popNextTokenTup: no delayed tokens, running lexer... +popNextTokenTup: delayed token, tokenStartPos = (35:15) +tokenize - got IDENT @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(36,13)-(36,14) +tokenize - getting one token from scratch\data\SimpleBuilder.fsx +popNextTokenTup: delayed token, tokenStartPos = (35:15) +CtxtLetDecl: EQUALS, pushing CtxtSeqBlock +popNextTokenTup: no delayed tokens, running lexer... +--> insert OBLOCKBEGIN +--> pushing, stack = [seqblock(first,(36:12)); let(true,(35:8)); seqblock(subsequent,(33:8)); paren; + vanilla((32:4)); seqblock(subsequent,(32:4)); let(true,(31:0)); + seqblock(subsequent,(2:0))] + ``` + + Then the rest, which seems to agree with what I see: + ``` + pushing CtxtVanilla at tokenStartPos = (37:8) +tokenize - got YIELD @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,8)-(38,14) +tokenize - getting one token from scratch\data\SimpleBuilder.fsx +popNextTokenTup: no delayed tokens, running lexer... +popNextTokenTup: no delayed tokens, running lexer... +popNextTokenTup: delayed token, tokenStartPos = (37:23) +tokenize - got IDENT @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,15)-(38,22) +tokenize - getting one token from scratch\data\SimpleBuilder.fsx +popNextTokenTup: delayed token, tokenStartPos = (37:23) +tokenize - got STRING @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,23)-(38,39) +tokenize - getting one token from scratch\data\SimpleBuilder.fsx +popNextTokenTup: no delayed tokens, running lexer... +popNextTokenTup: no delayed tokens, running lexer... +popNextTokenTup: delayed token, tokenStartPos = (37:42) +tokenize - got IDENT @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,40)-(38,41) +tokenize - getting one token from scratch\data\SimpleBuilder.fsx +popNextTokenTup: delayed token, tokenStartPos = (37:42) +popNextTokenTup: no delayed tokens, running lexer... +popNextTokenTup: delayed token, tokenStartPos = (38:4) +tokenize - got IDENT @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,42)-(38,43) +tokenize - getting one token from scratch\data\SimpleBuilder.fsx +popNextTokenTup: delayed token, tokenStartPos = (38:4) +IN/ELSE/ELIF/DONE/RPAREN/RBRACE/END at (38:4) terminates context at position (37:8) +``` + +So is the offside stuff adding/missing something weird that I've not noticed, or is the parsing logic not doing what I expect? \ No newline at end of file From 2c56bdb6334fc0b9c9e4de8d35048d1908097259 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 21:46:24 +0100 Subject: [PATCH 078/255] Move parse fail to and! token by replicating logic for handling and keyword after a let --- src/fsharp/LexFilter.fs | 8 +------- src/fsharp/pars.fsy | 14 ++++++++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index 4854b15310..4e0ec40f61 100755 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -308,6 +308,7 @@ let rec isLetContinuator token = // let ... // and ... | AND -> true + | AND_BANG _ | OAND_BANG _ -> true // TODO Does entering an and! count as "closing the construct"? I assume not here, but... | ORIGHT_BLOCK_END | OBLOCKEND | ODECLEND -> true // The following arise during reprocessing of the inserted tokens when we hit a DONE | ODUMMY(token) -> isLetContinuator(token) | _ -> false @@ -1719,13 +1720,6 @@ type LexFilterImpl (lightSyntaxStatus:LightSyntaxStatus, compilingFsLib, lexer, pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos)) returnToken tokenLexbufState (if blockLet then OBINDER b else token) - // and! ... ~~~> CtxtLetDecl - | AND_BANG isUse, (ctxt :: _) -> - let blockLet = match ctxt with CtxtSeqBlock _ -> true | _ -> false - if debug then dprintf "AND!: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n" blockLet outputPos tokenStartPos - pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos)) - returnToken tokenLexbufState (if blockLet then OAND_BANG isUse else token) - | (VAL | STATIC | ABSTRACT | MEMBER | OVERRIDE | DEFAULT), ctxtStack when thereIsACtxtMemberBodyOnTheStackAndWeShouldPopStackForUpcomingMember ctxtStack -> if debug then dprintf "STATIC/MEMBER/OVERRIDE/DEFAULT: already inside CtxtMemberBody, popping all that context before starting next member...\n" // save this token, we'll consume it again later... diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index cbccfabd6e..32e34fac6a 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3007,17 +3007,20 @@ recover: binders: | BINDER headBindingPattern EQUALS typedSeqExprBlock IN morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) + printfn "In BINDER case" // TODO Remove let m = unionRanges (rhs parseState 1) $8.Range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$6,$8) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error + printfn "In OBINDER case" // TODO Remove let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let m = unionRanges (rhs parseState 1) $8.Range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$6,$8) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions + printfn "In OBINDER error case" // TODO Remove let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) let m = $4.Range.EndRange // zero-width range @@ -3028,17 +3031,20 @@ morebinders: | AND_BANG headBindingPattern EQUALS typedSeqExprBlock IN morebinders %prec expr_let /* TODO Check precedence */ { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *) + printfn "In AND_BANG case" // TODO Remove let m = rhs parseState 1 (* TODO Pretty sure this is wrong *) (spBind,$1,true,$2,$4,m) :: $6 } - | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders %prec expr_let - { $5 (if $1 then "anduse!" else "and!") (rhs parseState 1) // report unterminated error + | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock opt_OBLOCKSEP morebinders %prec expr_let + { // $5 (if $1 then "anduse!" else "and!") (rhs parseState 1) // report unterminated error TODO Can this be fixed without hardwhiteDefnBindingsTerminator here? + printfn "In OAND_BANG case" // TODO Remove let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let m = rhs parseState 1 (* TODO Pretty sure this is wrong *) - (spBind,$1,true,$2,$4,m) :: $7 } + (spBind,$1,true,$2,$4,m) :: $6 } - | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let + | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock opt_OBLOCKSEP error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions + printfn "In OAND_BANG error case" // TODO Remove let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) let m = $4.Range.EndRange // zero-width range From 8a5bdca619592db1a2c6cff807133890b046df48 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 21:48:58 +0100 Subject: [PATCH 079/255] Move single defn terminator that follows let! to after all and!s --- src/fsharp/pars.fsy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 32e34fac6a..72d890679e 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3011,14 +3011,14 @@ binders: let m = unionRanges (rhs parseState 1) $8.Range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$6,$8) } - | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let + | OBINDER headBindingPattern EQUALS typedSeqExprBlock morebinders hardwhiteDefnBindingsTerminator opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error printfn "In OBINDER case" // TODO Remove let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let m = unionRanges (rhs parseState 1) $8.Range - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$6,$8) } + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$5,$8) } - | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator morebinders opt_OBLOCKSEP error %prec expr_let + | OBINDER headBindingPattern EQUALS typedSeqExprBlock morebinders hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions printfn "In OBINDER error case" // TODO Remove let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) From 7a3238ec0ce4e22fa308413deecce002900f2d17 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 11 Sep 2018 21:49:41 +0100 Subject: [PATCH 080/255] Fix broken ref to defn terminator --- src/fsharp/pars.fsy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 72d890679e..4a16a91313 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3012,7 +3012,7 @@ binders: SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$6,$8) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock morebinders hardwhiteDefnBindingsTerminator opt_OBLOCKSEP typedSeqExprBlock %prec expr_let - { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error + { $6 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error printfn "In OBINDER case" // TODO Remove let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let m = unionRanges (rhs parseState 1) $8.Range From e2c939042b882fdc58a914731a6be59726718754 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 12 Sep 2018 12:07:36 +0100 Subject: [PATCH 081/255] Dump more brain-state on parser debugging --- scratch/docs/Log.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 0204ba07b2..155617b61e 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -475,4 +475,8 @@ popNextTokenTup: delayed token, tokenStartPos = (38:4) IN/ELSE/ELIF/DONE/RPAREN/RBRACE/END at (38:4) terminates context at position (37:8) ``` -So is the offside stuff adding/missing something weird that I've not noticed, or is the parsing logic not doing what I expect? \ No newline at end of file +So is the offside stuff adding/missing something weird that I've not noticed, or is the parsing logic not doing what I expect? + +One conflict: `isLetContinuator` currently lets the `and!` be "inside" the `let!`, but the parser expects `hardwhiteDefnBindingsTerminator`, i.e. `ODECLEND`! + +How much of a problem is it that `let! ... and! ...` is using right-recursion? \ No newline at end of file From a528ed0ee826047646b813503f0dcf16572067c1 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 12 Sep 2018 15:25:58 +0100 Subject: [PATCH 082/255] Make at least one example of let! ... and! ... syntax be accepted by the parser --- scratch/data/SimpleBuilder.fsx | 3 +-- src/fsharp/LexFilter.fs | 10 ++++++++-- src/fsharp/TypeChecker.fs | 6 +++--- src/fsharp/pars.fsy | 35 +++++++++++----------------------- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 3e016e72bf..3b0c58d5f0 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -33,8 +33,7 @@ let foo = opt { let! x = xOpt and! y = yOpt - and! z = zOpt - return sprintf "x = %d, y = %s, z = %f" x y z + return sprintf "x = %d, y = %s" x y } (* diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index 4e0ec40f61..0801b286c7 100755 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -308,7 +308,6 @@ let rec isLetContinuator token = // let ... // and ... | AND -> true - | AND_BANG _ | OAND_BANG _ -> true // TODO Does entering an and! count as "closing the construct"? I assume not here, but... | ORIGHT_BLOCK_END | OBLOCKEND | ODECLEND -> true // The following arise during reprocessing of the inserted tokens when we hit a DONE | ODUMMY(token) -> isLetContinuator(token) | _ -> false @@ -343,7 +342,7 @@ let rec isSeqBlockElementContinuator token = // ... // ), <------- NOTE RPAREN HERE // Shortcut.CtrlO) - | END | AND | WITH | THEN | RPAREN | RBRACE | RBRACK | BAR_RBRACK | RQUOTE _ -> true + | END | AND | WITH | THEN | RPAREN | RBRACE | RBRACK | BAR_RBRACK | RQUOTE _ -> true // The following arise during reprocessing of the inserted tokens when we hit a DONE | ORIGHT_BLOCK_END | OBLOCKEND | ODECLEND -> true @@ -1720,6 +1719,13 @@ type LexFilterImpl (lightSyntaxStatus:LightSyntaxStatus, compilingFsLib, lexer, pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos)) returnToken tokenLexbufState (if blockLet then OBINDER b else token) + // and! ... ~~~> CtxtLetDecl + | AND_BANG isUse, (ctxt :: _) -> + let blockLet = match ctxt with CtxtSeqBlock _ -> true | _ -> false + if debug then dprintf "AND!: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n" blockLet outputPos tokenStartPos + pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos)) + returnToken tokenLexbufState (if blockLet then OAND_BANG isUse else token) + | (VAL | STATIC | ABSTRACT | MEMBER | OVERRIDE | DEFAULT), ctxtStack when thereIsACtxtMemberBodyOnTheStackAndWeShouldPopStackForUpcomingMember ctxtStack -> if debug then dprintf "STATIC/MEMBER/OVERRIDE/DEFAULT: already inside CtxtMemberBody, popping all that context before starting next member...\n" // save this token, we'll consume it again later... diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 6a359fc0be..a0aa6836b1 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8045,7 +8045,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv //| SynExpr.LetOrUseAndBang(spBind, false, isFromSource, pat, rhsExpr, _, andBangs, innerComp) -> | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> - failwith "TODO" + failwith "TODO - Type check and!" // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) @@ -8053,12 +8053,12 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | SynExpr.LetOrUseAndBang(_, true, _, (SynPat.Named (SynPat.Wild _, _, false, _, _)) , _, _, _, _) | SynExpr.LetOrUseAndBang(_, true, _, (SynPat.LongIdent (LongIdentWithDots([_], _), _, _, _, _, _)), _, _, _, _) -> - failwith "TODO" + failwith "TODO - Type check and!" // 'use! pat = e1 and(Use)! pat = e2 in e3' where any 'pat' is not a simple name --> error //| SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, andBangs, _innerComp) -> | SynExpr.LetOrUseAndBang(_, true, _, _, _, _, _, _) -> - failwith "TODO" + failwith "TODO - Type check and!" | SynExpr.Match (spMatch, expr, clauses, false, m) -> let mMatch = match spMatch with SequencePointAtBinding mMatch -> mMatch | _ -> m diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 4a16a91313..a7298c6afa 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3005,24 +3005,21 @@ recover: /* EXPERIMENT */ binders: - | BINDER headBindingPattern EQUALS typedSeqExprBlock IN morebinders opt_OBLOCKSEP typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ + | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) - printfn "In BINDER case" // TODO Remove let m = unionRanges (rhs parseState 1) $8.Range - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$6,$8) } + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } - | OBINDER headBindingPattern EQUALS typedSeqExprBlock morebinders hardwhiteDefnBindingsTerminator opt_OBLOCKSEP typedSeqExprBlock %prec expr_let - { $6 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error - printfn "In OBINDER case" // TODO Remove + | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let + { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let m = unionRanges (rhs parseState 1) $8.Range - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$5,$8) } + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } - | OBINDER headBindingPattern EQUALS typedSeqExprBlock morebinders hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let + | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions - printfn "In OBINDER error case" // TODO Remove let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) - let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) + let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) // TODO Check this range let m = $4.Range.EndRange // zero-width range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } @@ -3031,24 +3028,14 @@ morebinders: | AND_BANG headBindingPattern EQUALS typedSeqExprBlock IN morebinders %prec expr_let /* TODO Check precedence */ { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *) - printfn "In AND_BANG case" // TODO Remove let m = rhs parseState 1 (* TODO Pretty sure this is wrong *) (spBind,$1,true,$2,$4,m) :: $6 } - | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock opt_OBLOCKSEP morebinders %prec expr_let - { // $5 (if $1 then "anduse!" else "and!") (rhs parseState 1) // report unterminated error TODO Can this be fixed without hardwhiteDefnBindingsTerminator here? - printfn "In OAND_BANG case" // TODO Remove - let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) + | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders %prec expr_let + { $5 (if $1 then "anduse!" else "and!") (rhs parseState 1) // report unterminated error + let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *) let m = rhs parseState 1 (* TODO Pretty sure this is wrong *) - (spBind,$1,true,$2,$4,m) :: $6 } - - | OAND_BANG headBindingPattern EQUALS typedSeqExprBlock opt_OBLOCKSEP error %prec expr_let - { // error recovery that allows intellisense when writing incomplete computation expressions - printfn "In OAND_BANG error case" // TODO Remove - let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) - let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) - let m = $4.Range.EndRange // zero-width range - [ spBind,$1,true,$2,$4, mAll ] } /* TODO Does this whole non-terminal make sense? */ + (spBind,$1,true,$2,$4,m) :: $7 } | %prec prec_no_more_attr_bindings /* TODO Make a prec_no_more_andbang_bindings or something */ { [] } From 4e61ee64b3e5d8f90e8b6935930b164c0ec04b17 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 12 Sep 2018 15:36:50 +0100 Subject: [PATCH 083/255] Comment and begin sketching first TypeCheck case for and! --- src/fsharp/TypeChecker.fs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index a0aa6836b1..53aa5b21bf 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8041,11 +8041,30 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) - // 'let! pat = expr and! pat = expr in expr' --> TODO - //| SynExpr.LetOrUseAndBang(spBind, false, isFromSource, pat, rhsExpr, _, andBangs, innerComp) -> - | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> + // 'let! pat1 = expr1 and! pat2 = expr2 in exprBody' --> + // build.Apply((function _arg -> match _arg with pat2 -> + // build.Apply((function _arg -> match _arg with pat1 -> + // build.Return(exprBody) + // ), expr1) + // ), expr2) + | SynExpr.LetOrUseAndBang(spBind, false, isFromSource, pat, rhsExpr, _, andBangs, innerComp) -> - failwith "TODO - Type check and!" + let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range + if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) + let innerRange = innerComp.Range + if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Bind" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Bind"), bindRange)) + + // Add the variables to the query variable space, on demand + let varSpace = + addVarsToVarSpace varSpace (fun _mCustomOp env -> + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink + let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) + vspecs, envinner) + + let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr + Some (trans true q varSpace innerComp (fun holeFill -> + let consumeExpr = SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, holeFill, innerRange, SequencePointAtTarget)], spBind, innerRange) + translatedCtxt (mkSynCall "Bind" bindRange [rhsExpr; consumeExpr]))) // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) From 027aae0202c0bdc38b0e0cf13abaf0b9ec57b14b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 12 Sep 2018 16:28:59 +0100 Subject: [PATCH 084/255] Add first cut of desugaring a particular instance of let! ... and! ... --- src/fsharp/TypeChecker.fs | 54 +++++++++++++++++++++++++++------------ src/fsharp/pars.fsy | 1 + 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 53aa5b21bf..af8ec31f07 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8044,27 +8044,47 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // 'let! pat1 = expr1 and! pat2 = expr2 in exprBody' --> // build.Apply((function _arg -> match _arg with pat2 -> // build.Apply((function _arg -> match _arg with pat1 -> - // build.Return(exprBody) + // exprBody // ), expr1) // ), expr2) - | SynExpr.LetOrUseAndBang(spBind, false, isFromSource, pat, rhsExpr, _, andBangs, innerComp) -> + | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, innerComp) -> // TODO Handle use! / anduse! - let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range - if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) - let innerRange = innerComp.Range - if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Bind" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Bind"), bindRange)) - - // Add the variables to the query variable space, on demand - let varSpace = - addVarsToVarSpace varSpace (fun _mCustomOp env -> - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) - vspecs, envinner) + printfn "type checking a let! ... and! ..." // TODO Remove - let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr - Some (trans true q varSpace innerComp (fun holeFill -> - let consumeExpr = SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, holeFill, innerRange, SequencePointAtTarget)], spBind, innerRange) - translatedCtxt (mkSynCall "Bind" bindRange [rhsExpr; consumeExpr]))) + // TODO Lift out to top level? + let rec constructApplies (accComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = + match bindings with + | [] -> accComp + | (spBind, _, isFromSource, pat, rhs, _) :: outerBindings -> + + let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhs.Range + if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) + let innerRange = accComp.Range + if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) + then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) + + // Add the variables to the query variable space, on demand // TODO Think hard about how this should work - shouldn't this happen only once inside all and!s? + let varSpace = + addVarsToVarSpace varSpace (fun _mCustomOp env -> + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink + let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) + vspecs, envinner) + + let rhsExpr = if isFromSource then mkSourceExpr rhs else rhs + + let newAccComp = + trans true q varSpace accComp (fun holeFill -> + let consumeExpr = SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, holeFill, innerRange, SequencePointAtTarget)], spBind, innerRange) + translatedCtxt (mkSynCall "Apply" bindRange [consumeExpr; rhsExpr])) // TODO The lambda (i.e. "consumeExpr") comes first here, but it comes second in Bind - is that okay or needless break with the precent? + + constructApplies newAccComp outerBindings + + let allBindings = + let letBinding = + (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) + List.rev (letBinding :: andBangBindings) + + Some (constructApplies innerComp allBindings) // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index a7298c6afa..0143cde5eb 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -451,6 +451,7 @@ let rangeOfLongIdent(lid:LongIdent) = %nonassoc paren_pat_attribs %left OR BAR_BAR JOIN_IN %left AND /* check */ +%right AND_BANG /* check */ %left AMP AMP_AMP %nonassoc pat_conj %nonassoc expr_not From af6d91e017513ee19a278a7811515230df929628 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 12 Sep 2018 16:47:46 +0100 Subject: [PATCH 085/255] Add second and! to example script so I can check ordering of and! bindings is correct --- scratch/data/SimpleBuilder.fsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 3b0c58d5f0..1df40f776e 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -27,13 +27,14 @@ let opt = OptionalBuilder() let xOpt = Some 1 let yOpt = Some "A" -let zOpt = None +let zOpt = Some 3.0 let foo = opt { let! x = xOpt and! y = yOpt - return sprintf "x = %d, y = %s" x y + and! z = zOpt + return sprintf "x = %d, y = %s, z = %f" x y z } (* From 50ec655c5d12847bb45a3e28c33dd692f33c8860 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 12 Sep 2018 16:49:19 +0100 Subject: [PATCH 086/255] Try to handle var space and add comments for fixing remaining known desugaring issues --- src/fsharp/TypeChecker.fs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index af8ec31f07..b3ad61ec33 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8042,17 +8042,21 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // 'let! pat1 = expr1 and! pat2 = expr2 in exprBody' --> - // build.Apply((function _arg -> match _arg with pat2 -> - // build.Apply((function _arg -> match _arg with pat1 -> - // exprBody + // build.Apply( + // build.Apply( + // (function _arg1 -> match _arg1 with pat1 -> + // (function _arg2 -> match _arg2 with pat2 -> + // exprBody // ), expr1) // ), expr2) | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, innerComp) -> // TODO Handle use! / anduse! printfn "type checking a let! ... and! ..." // TODO Remove + // TODO Add all lambdas then add apply calls (in reverse order), as per comments on this case above + // TODO Lift out to top level? - let rec constructApplies (accComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = + let rec constructApplies (accComp : SynExpr) (varSpace) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = match bindings with | [] -> accComp | (spBind, _, isFromSource, pat, rhs, _) :: outerBindings -> @@ -8062,29 +8066,31 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let innerRange = accComp.Range if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) + + // TODO Assert no RHS refers to and outer LHS, else insert a helpful and!-related error for that - // Add the variables to the query variable space, on demand // TODO Think hard about how this should work - shouldn't this happen only once inside all and!s? - let varSpace = + // Add the variables to the query variable space, on demand + let varSpace = addVarsToVarSpace varSpace (fun _mCustomOp env -> - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) - vspecs, envinner) + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink + let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) + vspecs, envinner) let rhsExpr = if isFromSource then mkSourceExpr rhs else rhs let newAccComp = trans true q varSpace accComp (fun holeFill -> let consumeExpr = SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, holeFill, innerRange, SequencePointAtTarget)], spBind, innerRange) - translatedCtxt (mkSynCall "Apply" bindRange [consumeExpr; rhsExpr])) // TODO The lambda (i.e. "consumeExpr") comes first here, but it comes second in Bind - is that okay or needless break with the precent? + translatedCtxt (mkSynCall "Apply" bindRange [consumeExpr; rhsExpr])) // TODO The lambda (i.e. "consumeExpr") comes first here, but it comes second in Bind - is that okay or needless break with the precent? - constructApplies newAccComp outerBindings + constructApplies newAccComp varSpace outerBindings let allBindings = let letBinding = (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) List.rev (letBinding :: andBangBindings) - Some (constructApplies innerComp allBindings) + Some (constructApplies innerComp varSpace allBindings) // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) From 13e81866294ebf42325e6711a203233c2bfd15cb Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 12 Sep 2018 20:54:40 +0100 Subject: [PATCH 087/255] Refine desugaring of current let! ... and! ... case - a long way to go yet! --- src/fsharp/TypeChecker.fs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index b3ad61ec33..2cab78e4ad 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8056,10 +8056,16 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // TODO Add all lambdas then add apply calls (in reverse order), as per comments on this case above // TODO Lift out to top level? - let rec constructApplies (accComp : SynExpr) (varSpace) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = - match bindings with - | [] -> accComp - | (spBind, _, isFromSource, pat, rhs, _) :: outerBindings -> + // TODO How do we manage the varSpace such that the orthogonality of the and!s is preserved? + // Reads in bindings [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] + // creating a lambda for each one, and stacking up the calls to apply to hook up once + // all of the lambdas have been created. + // Note how we work from the inner expression outward, meaning be create the lambda for the last + // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to + // 'Apply' correspond to their lambda. + let rec constructApplies (accComp : SynExpr) (varSpace) (pendingApplyCalls : (SynExpr -> SynExpr) list) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = + match bindings, pendingApplyCalls with // TODO Nest pattern matches to avoid allocation here? + | (spBind, _, isFromSource, pat, rhs, _) :: remainingBindings, _ -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhs.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) @@ -8067,8 +8073,9 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) - // TODO Assert no RHS refers to and outer LHS, else insert a helpful and!-related error for that + // TODO Assert no RHS refers to and outer LHS, else insert a helpful and!-related error for that instead of just "not defined" or using a value the user might have expected to have been shadowed + // TODO: Intentionally only include the current binding - the whole point of applicatives is to avoid bringing the LHS of an earlier binding in the RHS of another? // Add the variables to the query variable space, on demand let varSpace = addVarsToVarSpace varSpace (fun _mCustomOp env -> @@ -8078,19 +8085,24 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let rhsExpr = if isFromSource then mkSourceExpr rhs else rhs - let newAccComp = + let newAccComp = // TODO NEXT: Split lambda and apply, stacking up applies for later trans true q varSpace accComp (fun holeFill -> let consumeExpr = SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, holeFill, innerRange, SequencePointAtTarget)], spBind, innerRange) translatedCtxt (mkSynCall "Apply" bindRange [consumeExpr; rhsExpr])) // TODO The lambda (i.e. "consumeExpr") comes first here, but it comes second in Bind - is that okay or needless break with the precent? - constructApplies newAccComp varSpace outerBindings + constructApplies newAccComp varSpace [] remainingBindings + + | [], apply :: remainingApplies -> + failwithf "Need to hook up Applies: apply = %+A, remaining = %+A" apply remainingApplies + + | [], [] -> accComp - let allBindings = + let bindingsBottomToTop = let letBinding = (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) - List.rev (letBinding :: andBangBindings) + List.rev (letBinding :: andBangBindings) // [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] - Some (constructApplies innerComp varSpace allBindings) + Some (constructApplies innerComp varSpace [] bindingsBottomToTop) // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) From 0bef48e12ba745bc13ba1a6ed90e339429c677ae Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 12 Sep 2018 21:09:56 +0100 Subject: [PATCH 088/255] Add EoD notes to self --- src/fsharp/TypeChecker.fs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 2cab78e4ad..b78f4c8e7d 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8063,9 +8063,9 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // Note how we work from the inner expression outward, meaning be create the lambda for the last // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to // 'Apply' correspond to their lambda. - let rec constructApplies (accComp : SynExpr) (varSpace) (pendingApplyCalls : (SynExpr -> SynExpr) list) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = - match bindings, pendingApplyCalls with // TODO Nest pattern matches to avoid allocation here? - | (spBind, _, isFromSource, pat, rhs, _) :: remainingBindings, _ -> + let rec constructApplies (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = + match bindings with // TODO Nest pattern matches to avoid allocation here? + | (spBind, _, isFromSource, pat, rhs, _) :: remainingBindings -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhs.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) @@ -8077,7 +8077,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // TODO: Intentionally only include the current binding - the whole point of applicatives is to avoid bringing the LHS of an earlier binding in the RHS of another? // Add the variables to the query variable space, on demand - let varSpace = + let newVarSpace = addVarsToVarSpace varSpace (fun _mCustomOp env -> use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) @@ -8085,24 +8085,28 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let rhsExpr = if isFromSource then mkSourceExpr rhs else rhs - let newAccComp = // TODO NEXT: Split lambda and apply, stacking up applies for later + + // TODO NEXT: Rewrite to use stack frame - more terse and readable solution? + // Go down bindings, preparing applies and stacking up lambdas and varspace, then empty stack of lambdas too + + let newAccComp = + trans true q newVarSpace accComp (fun holeFill -> + translatedCtxt (SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, holeFill, innerRange, SequencePointAtTarget)], spBind, innerRange))) + + let newPendingApplyCall = trans true q varSpace accComp (fun holeFill -> - let consumeExpr = SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, holeFill, innerRange, SequencePointAtTarget)], spBind, innerRange) - translatedCtxt (mkSynCall "Apply" bindRange [consumeExpr; rhsExpr])) // TODO The lambda (i.e. "consumeExpr") comes first here, but it comes second in Bind - is that okay or needless break with the precent? + translatedCtxt (mkSynCall "Apply" bindRange [holeFill; rhsExpr])) - constructApplies newAccComp varSpace [] remainingBindings + constructApplies newAccComp varSpace (newPendingApplyCall :: pendingApplyCalls) remainingBindings - | [], apply :: remainingApplies -> - failwithf "Need to hook up Applies: apply = %+A, remaining = %+A" apply remainingApplies - - | [], [] -> accComp + | [] -> innerComp let bindingsBottomToTop = let letBinding = (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) List.rev (letBinding :: andBangBindings) // [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] - Some (constructApplies innerComp varSpace [] bindingsBottomToTop) + Some (constructApplies bindingsBottomToTop) // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) From b93258f9c8c90f214a2da9884c8ebe4946076637 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 13 Sep 2018 15:21:26 +0100 Subject: [PATCH 089/255] Update current project state --- scratch/docs/Log.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 155617b61e..0e1ae81974 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -479,4 +479,14 @@ So is the offside stuff adding/missing something weird that I've not noticed, or One conflict: `isLetContinuator` currently lets the `and!` be "inside" the `let!`, but the parser expects `hardwhiteDefnBindingsTerminator`, i.e. `ODECLEND`! -How much of a problem is it that `let! ... and! ...` is using right-recursion? \ No newline at end of file +How much of a problem is it that `let! ... and! ...` is using right-recursion? (We seem to use it for `and`...) + +Okay, I think it is time to take a step back. The easiest thing to make `and!` is presumably to emulate an existing construct. Up to and excluding parsing, `and!` works like `and` (after that there's a difference because, semantically, the `and!` is very much a part of the larger `let!` construct). + +NFO `OAND_BANG` - there's no `OAND`! + +One day lost to `build.cmd` not actually building everything. From what I can tell, you *must* use VS or msbuild to rebuild some artefacts. + +## 2018-09-13 + +Parsing seem to at least accept my examples. Working on type checking now. One of the core issue right now if when to call `trans` and `translatedCtxt` on desugared CEs. \ No newline at end of file From f65173fc8688a4f2596c295dd6e7e9463e09dfd4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 13 Sep 2018 15:36:39 +0100 Subject: [PATCH 090/255] Clean up construction of Apply clause in proof-of-concept case --- src/fsharp/TypeChecker.fs | 48 +++++++++++++++++++-------------------- src/fsharp/pars.fsy | 1 + 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index b78f4c8e7d..7a0a6258d1 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8049,64 +8049,62 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // exprBody // ), expr1) // ), expr2) - | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, innerComp) -> // TODO Handle use! / anduse! + | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, bodyComp) -> // TODO Handle use! / anduse! printfn "type checking a let! ... and! ..." // TODO Remove - // TODO Add all lambdas then add apply calls (in reverse order), as per comments on this case above - // TODO Lift out to top level? // TODO How do we manage the varSpace such that the orthogonality of the and!s is preserved? + // TODO Add all lambdas then add apply calls (in reverse order), as per comments on this case above + // TODO Assert no RHS refers to and outer LHS, else insert a helpful and!-related error for that instead of just "not defined" or using a value the user might have expected to have been shadowed + // Reads in bindings [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] // creating a lambda for each one, and stacking up the calls to apply to hook up once // all of the lambdas have been created. // Note how we work from the inner expression outward, meaning be create the lambda for the last // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to // 'Apply' correspond to their lambda. - let rec constructApplies (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = - match bindings with // TODO Nest pattern matches to avoid allocation here? - | (spBind, _, isFromSource, pat, rhs, _) :: remainingBindings -> + let rec constructApplies (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) (pendingApplies : (SynExpr -> SynExpr) list) = - let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhs.Range + match bindings with + | (spBind, _, isFromSource, pat, rhsExpr, _) :: remainingBindings -> + let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) - let innerRange = accComp.Range + let innerRange = innerComp.Range if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) - - // TODO Assert no RHS refers to and outer LHS, else insert a helpful and!-related error for that instead of just "not defined" or using a value the user might have expected to have been shadowed - // TODO: Intentionally only include the current binding - the whole point of applicatives is to avoid bringing the LHS of an earlier binding in the RHS of another? // Add the variables to the query variable space, on demand - let newVarSpace = + let andBangVarSpace = addVarsToVarSpace varSpace (fun _mCustomOp env -> use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) vspecs, envinner) - let rhsExpr = if isFromSource then mkSourceExpr rhs else rhs - + let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr - // TODO NEXT: Rewrite to use stack frame - more terse and readable solution? - // Go down bindings, preparing applies and stacking up lambdas and varspace, then empty stack of lambdas too + let newPendingApplies = + (fun consumeExpr -> mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) :: pendingApplies - let newAccComp = - trans true q newVarSpace accComp (fun holeFill -> + let newInnerComp = + trans true q andBangVarSpace innerComp (fun holeFill -> translatedCtxt (SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, holeFill, innerRange, SequencePointAtTarget)], spBind, innerRange))) - let newPendingApplyCall = - trans true q varSpace accComp (fun holeFill -> - translatedCtxt (mkSynCall "Apply" bindRange [holeFill; rhsExpr])) - - constructApplies newAccComp varSpace (newPendingApplyCall :: pendingApplyCalls) remainingBindings + constructApplies newInnerComp remainingBindings newPendingApplies - | [] -> innerComp + | [] -> + match pendingApplies with + | ap :: remainingApplies -> + constructApplies (ap innerComp) [] remainingApplies + | [] -> + innerComp let bindingsBottomToTop = let letBinding = (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) List.rev (letBinding :: andBangBindings) // [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] - Some (constructApplies bindingsBottomToTop) + Some (constructApplies bodyComp bindingsBottomToTop []) // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 0143cde5eb..0c514de640 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3022,6 +3022,7 @@ binders: let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) // TODO Check this range let m = $4.Range.EndRange // zero-width range + failwith "Inserting implicit zero from OBINDER!" // TODO Remove SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } From f2c9c71bb2283d44838abe71d82da2fc37a8ae20 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 13 Sep 2018 16:07:24 +0100 Subject: [PATCH 091/255] Shanpshot status before pulling updates of varSpace outside construction of desugared expression --- src/fsharp/TypeChecker.fs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 7a0a6258d1..0425884c9a 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8053,10 +8053,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv printfn "type checking a let! ... and! ..." // TODO Remove + // TODO Compute varSpace first and check for RHS references to LHS names, upfront? // TODO Lift out to top level? - // TODO How do we manage the varSpace such that the orthogonality of the and!s is preserved? - // TODO Add all lambdas then add apply calls (in reverse order), as per comments on this case above // TODO Assert no RHS refers to and outer LHS, else insert a helpful and!-related error for that instead of just "not defined" or using a value the user might have expected to have been shadowed + // TODO Assert bindings in an and! chain don't clash - they're logically orthogonal, so shadowing doesn't really make sense // Reads in bindings [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] // creating a lambda for each one, and stacking up the calls to apply to hook up once @@ -8064,7 +8064,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // Note how we work from the inner expression outward, meaning be create the lambda for the last // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to // 'Apply' correspond to their lambda. - let rec constructApplies (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) (pendingApplies : (SynExpr -> SynExpr) list) = + let rec constructApplies (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) varSpace (pendingApplies : (SynExpr -> SynExpr) list) = match bindings with | (spBind, _, isFromSource, pat, rhsExpr, _) :: remainingBindings -> @@ -8075,7 +8075,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) // Add the variables to the query variable space, on demand - let andBangVarSpace = + let varSpace = addVarsToVarSpace varSpace (fun _mCustomOp env -> use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) @@ -8087,15 +8087,14 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv (fun consumeExpr -> mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) :: pendingApplies let newInnerComp = - trans true q andBangVarSpace innerComp (fun holeFill -> - translatedCtxt (SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, holeFill, innerRange, SequencePointAtTarget)], spBind, innerRange))) + SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, innerComp, innerRange, SequencePointAtTarget)], spBind, innerRange) - constructApplies newInnerComp remainingBindings newPendingApplies + constructApplies newInnerComp remainingBindings varSpace newPendingApplies | [] -> match pendingApplies with | ap :: remainingApplies -> - constructApplies (ap innerComp) [] remainingApplies + constructApplies (ap innerComp) [] varSpace remainingApplies | [] -> innerComp @@ -8104,7 +8103,8 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) List.rev (letBinding :: andBangBindings) // [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] - Some (constructApplies bodyComp bindingsBottomToTop []) + // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? + Some (trans true q varSpace bodyComp (fun holeFill -> translatedCtxt (constructApplies holeFill bindingsBottomToTop varSpace []))) // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) From a0d472ff82b547405b471443fb28db81bcfeb64e Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 13 Sep 2018 16:18:46 +0100 Subject: [PATCH 092/255] Move varSpace collation up-front --- src/fsharp/TypeChecker.fs | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 0425884c9a..33210e670a 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8053,18 +8053,13 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv printfn "type checking a let! ... and! ..." // TODO Remove - // TODO Compute varSpace first and check for RHS references to LHS names, upfront? - // TODO Lift out to top level? - // TODO Assert no RHS refers to and outer LHS, else insert a helpful and!-related error for that instead of just "not defined" or using a value the user might have expected to have been shadowed - // TODO Assert bindings in an and! chain don't clash - they're logically orthogonal, so shadowing doesn't really make sense - // Reads in bindings [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] // creating a lambda for each one, and stacking up the calls to apply to hook up once // all of the lambdas have been created. // Note how we work from the inner expression outward, meaning be create the lambda for the last // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to // 'Apply' correspond to their lambda. - let rec constructApplies (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) varSpace (pendingApplies : (SynExpr -> SynExpr) list) = + let rec constructApplies (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) (pendingApplies : (SynExpr -> SynExpr) list) = match bindings with | (spBind, _, isFromSource, pat, rhsExpr, _) :: remainingBindings -> @@ -8074,13 +8069,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) - // Add the variables to the query variable space, on demand - let varSpace = - addVarsToVarSpace varSpace (fun _mCustomOp env -> - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) - vspecs, envinner) - let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr let newPendingApplies = @@ -8089,12 +8077,12 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let newInnerComp = SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, innerComp, innerRange, SequencePointAtTarget)], spBind, innerRange) - constructApplies newInnerComp remainingBindings varSpace newPendingApplies + constructApplies newInnerComp remainingBindings newPendingApplies | [] -> match pendingApplies with | ap :: remainingApplies -> - constructApplies (ap innerComp) [] varSpace remainingApplies + constructApplies (ap innerComp) [] remainingApplies | [] -> innerComp @@ -8103,8 +8091,19 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) List.rev (letBinding :: andBangBindings) // [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] + // Add the variables to the query variable space, on demand + let varSpace = + // TODO Make sure no LHS names clash, and no RHS mentions a name from another LHS + bindingsBottomToTop + |> List.fold (fun acc (_,_,_,pat,_,_) -> + addVarsToVarSpace acc (fun _mCustomOp env -> + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink + let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) + vspecs, envinner) + ) varSpace + // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - Some (trans true q varSpace bodyComp (fun holeFill -> translatedCtxt (constructApplies holeFill bindingsBottomToTop varSpace []))) + Some (trans true q varSpace bodyComp (fun holeFill -> translatedCtxt (constructApplies holeFill bindingsBottomToTop []))) // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) From f1c6847d5742d92df5fe1d470fbbad172d75565f Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 13 Sep 2018 17:27:14 +0100 Subject: [PATCH 093/255] Add details of recet debugging --- scratch/docs/Log.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 0e1ae81974..a36d9cbd70 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -489,4 +489,10 @@ One day lost to `build.cmd` not actually building everything. From what I can te ## 2018-09-13 -Parsing seem to at least accept my examples. Working on type checking now. One of the core issue right now if when to call `trans` and `translatedCtxt` on desugared CEs. \ No newline at end of file +Parsing seem to at least accept my examples. Working on type checking now. One of the core issue right now if when to call `trans` and `translatedCtxt` on desugared CEs. + +...which may, in turn, be due to me not updating `varSpace` correctly. + +16.3.10 of the spec seems to correspond to this, so time for some bookwork! + +Looks like I forgot to insert a call to `Return` between creating the lambdas that introduce the newly-bound names and calling `Apply`. \ No newline at end of file From 8819168adbd3dc996f826349e072f1ac9e4828df Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 13 Sep 2018 17:32:04 +0100 Subject: [PATCH 094/255] Make new syntax work under certain conditions (and erroneously doubly-wrap the result in the functor --- scratch/data/SimpleBuilder.fsx | 5 +++++ src/fsharp/TypeChecker.fs | 30 +++++++++++++++++++++--------- src/fsharp/pars.fsy | 2 +- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 1df40f776e..d5c6efc2cc 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -22,6 +22,9 @@ type OptionalBuilder = member __.Return(x) = Some x + + //member __.Zero () = // TODO: Remove - just here to explore current compiler limitations + // None let opt = OptionalBuilder() @@ -37,6 +40,8 @@ let foo = return sprintf "x = %d, y = %s, z = %f" x y z } +printfn "foo = %+A" foo + (* let foo' = opt { diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 33210e670a..018e987de9 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8044,9 +8044,12 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // 'let! pat1 = expr1 and! pat2 = expr2 in exprBody' --> // build.Apply( // build.Apply( - // (function _arg1 -> match _arg1 with pat1 -> - // (function _arg2 -> match _arg2 with pat2 -> - // exprBody + // build.Return( + // (function _arg1 -> match _arg1 with pat1 -> + // (function _arg2 -> match _arg2 with pat2 -> + // exprBody + // ) + // ) // ), expr1) // ), expr2) | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, bodyComp) -> // TODO Handle use! / anduse! @@ -8059,7 +8062,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // Note how we work from the inner expression outward, meaning be create the lambda for the last // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to // 'Apply' correspond to their lambda. - let rec constructApplies (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) (pendingApplies : (SynExpr -> SynExpr) list) = + let rec desugar (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) (pendingApplies : (SynExpr -> SynExpr) list) = match bindings with | (spBind, _, isFromSource, pat, rhsExpr, _) :: remainingBindings -> @@ -8075,14 +8078,23 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv (fun consumeExpr -> mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) :: pendingApplies let newInnerComp = - SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, innerComp, innerRange, SequencePointAtTarget)], spBind, innerRange) + let matchLambda = + SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, innerComp, innerRange, SequencePointAtTarget)], spBind, innerRange) + + // If this is the last matchLambda, we need to insert a return so that Apply takes an + // F<'a -> 'b> as opposed to just an 'a -> 'b + match remainingBindings with + | _::_ -> + matchLambda + | [] -> + mkSynCall "Return" bindRange [matchLambda] // TODO Fallback to 'Yield' if 'Return' isn't defined? + + desugar newInnerComp remainingBindings newPendingApplies - constructApplies newInnerComp remainingBindings newPendingApplies - | [] -> match pendingApplies with | ap :: remainingApplies -> - constructApplies (ap innerComp) [] remainingApplies + desugar (ap innerComp) [] remainingApplies | [] -> innerComp @@ -8103,7 +8115,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv ) varSpace // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - Some (trans true q varSpace bodyComp (fun holeFill -> translatedCtxt (constructApplies holeFill bindingsBottomToTop []))) + Some (trans true q varSpace bodyComp (fun holeFill -> translatedCtxt (desugar holeFill bindingsBottomToTop []))) // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 0c514de640..45ba619234 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -451,7 +451,7 @@ let rangeOfLongIdent(lid:LongIdent) = %nonassoc paren_pat_attribs %left OR BAR_BAR JOIN_IN %left AND /* check */ -%right AND_BANG /* check */ +%left AND_BANG /* TODO check */ %left AMP AMP_AMP %nonassoc pat_conj %nonassoc expr_not From 13bf70dd7d0d83540ca1739e465c3652684ba05b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 13 Sep 2018 17:52:31 +0100 Subject: [PATCH 095/255] Add note on how to fix "double wrapping" of functor --- scratch/docs/Log.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index a36d9cbd70..b925307c64 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -495,4 +495,6 @@ Parsing seem to at least accept my examples. Working on type checking now. One o 16.3.10 of the spec seems to correspond to this, so time for some bookwork! -Looks like I forgot to insert a call to `Return` between creating the lambdas that introduce the newly-bound names and calling `Apply`. \ No newline at end of file +Looks like I forgot to insert a call to `Return` between creating the lambdas that introduce the newly-bound names and calling `Apply`. + +My example almost works now, in that it builds and runs, but I accidentally doubly wrap the result of the builder in the functor. I think the explicit `Return` the user gives needs to be "moved" to outside the lambdas, as opposed to kept and a "new" `Return` added between them and the calls to `Apply`. The solution is to think about precisely how the desugaring should work a bit more. \ No newline at end of file From d7e5ca7c029e86816761c0d5ae1e883f7c6c0f22 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 11:14:59 +0100 Subject: [PATCH 096/255] List some more interesting desugaring cases --- scratch/docs/Log.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index b925307c64..d9b94027d3 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -267,7 +267,7 @@ This also means we can add optimisations to reduce current desugarings which use ## 2018-09-10 -I don't think we need `in` between `let!`s and `and!`s in the same chain, because any later `and!` in unambiguously part of the chain (a new chain would need to be started with a fresh `let!`). +I don't think we need `in` between `let!`s and `and!`s in the same chain, because any later `and!` in unambiguously part of the chain (a new chain would need to be started with a fresh `let!`). EDIT: That was clearly rubbish, one example of that being wrong is a `let!` nested in another `let!`. Which does the following `and!` belong to without the offside rule or `in`? Seems ambiguous to me! ### Should we desugar to `Bind`, `Apply` or `Map`? @@ -311,6 +311,9 @@ val liftA2 : ('a -> 'b -> 'c) -> f 'a -> f 'b -> f 'c ```F# val apply : f ('a -> 'b) -> f 'a -> f 'b ``` +```F# +val merge : f 'a -> f 'b -> f('a * 'b) +``` Imagine a simple but presumably represenative example CE: ```F# @@ -497,4 +500,16 @@ Parsing seem to at least accept my examples. Working on type checking now. One o Looks like I forgot to insert a call to `Return` between creating the lambdas that introduce the newly-bound names and calling `Apply`. -My example almost works now, in that it builds and runs, but I accidentally doubly wrap the result of the builder in the functor. I think the explicit `Return` the user gives needs to be "moved" to outside the lambdas, as opposed to kept and a "new" `Return` added between them and the calls to `Apply`. The solution is to think about precisely how the desugaring should work a bit more. \ No newline at end of file +My example almost works now, in that it builds and runs, but I accidentally doubly wrap the result of the builder in the functor. I think the explicit `Return` the user gives needs to be "moved" to outside the lambdas, as opposed to kept and a "new" `Return` added between them and the calls to `Apply`. The solution is to think about precisely how the desugaring should work a bit more. + +## 2018-09-17 + +I've been thinking about the slight annoyance of the desugaring that happens in `let! ... and! ...`, specifically, how the `Return` gets "lifted" to cover the whole block below the last `and!`, including whatever expression is handed to `Return`. The tricky examples I can think of right now are: + +* Just moving `Return` to cover the lambda that represents everything after the last `and!` is hard enough! + +* What if the user calls `Yield` more than once? Do we disallow it? If we did allow it, what would the semantics/desugaring be? + +* `let!` when `Apply` and `Return` are defined, but not `Bind` (this is highly related to the `Map` RFC for `let! ... return ...`). + +* `use! ... anduse! ...` \ No newline at end of file From fb2386b9a820cb9b2fb33a24603b12c502ce24e7 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 11:34:53 +0100 Subject: [PATCH 097/255] Add alternative applicative desugaring --- scratch/docs/Log.md | 109 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 16 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index d9b94027d3..a34d0c2d9e 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -80,7 +80,7 @@ Interesting existing annoyance, no idea if it's easily fixable: I tried to use a Managed to mangle the new `match!` syntax to use `letmatch!` on a branch. -```F# +```fsharp let x = Some 1 let y = opt { return "A" } let z = Some 3.5 @@ -107,7 +107,7 @@ Arbitrary list of things to do / remember: * Should there be an `andUse!` or something? What usecases could there be? What would that look like? What I've done so far makes `and!` only valid within a `let!`: -```F# +```fsharp [] SynAndBangExpr = /// AndBang(isUse, bindings, body, wholeRange) @@ -208,7 +208,7 @@ When writing a computation expression builder, is the choice in which of `Yield` @tomd I think one reason was that `let! a = x and b = y` maps pretty directly to `Merge`, so the translation is simpler and it also works nicely if you have something that is also a monad, because then you can translate this: -```F# +```fsharp m { let! a = x and b = y @@ -218,14 +218,14 @@ m { > into this: -```F# +```fsharp m.Bind(m.Merge(x, y), fun (a, b) -> m.Bind(z, fun c -> m.Return(a + b + c) ) ) ``` > For reference, if you do not have `Bind` and have just an applicative, you will not be able to do the second `let! c = z`. You can only write, say: -```F# +```fsharp m { let! a = x and b = y @@ -234,7 +234,7 @@ m { > which can be translated using just `Map`: -```F# +```fsharp m.Map(m.Merge(x, y), fun (a, b) -> a + b) ``` @@ -305,18 +305,18 @@ There's already been plenty of chat about the `apply` vs. `merge` way of doing t Comparing `liftA2` and `apply` because `merge` introduces a tuple, which I find arbitrary and inelegant. -```F# +```fsharp val liftA2 : ('a -> 'b -> 'c) -> f 'a -> f 'b -> f 'c ``` -```F# +```fsharp val apply : f ('a -> 'b) -> f 'a -> f 'b ``` -```F# +```fsharp val merge : f 'a -> f 'b -> f('a * 'b) ``` Imagine a simple but presumably represenative example CE: -```F# +```fsharp option { let! a = aExpr and! b = bExpr @@ -325,7 +325,7 @@ option { } ``` -```F# +```fsharp builder.Apply( builder.Apply( builder.Apply( @@ -336,7 +336,7 @@ builder.Apply( ``` And with some extra body in the middle: -```F# +```fsharp option { let! a = aExpr and! b = bExpr @@ -346,7 +346,7 @@ option { } ``` I presume it would desugar (have yet to confirm this is how `let`s work yet) as so: -```F# +```fsharp builder.Apply( builder.Apply( builder.Apply( @@ -395,7 +395,7 @@ Still trying to make the syntax parse properly. I suspect this is to do with the From the debugging: `LET: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n` when filtering `BINDER`. How does thhis need to change to accomodate `and!`? Need to explore `CtxtLetDecl` to get started. Okay, I think this: -```F# +```fsharp // let! ... ~~~> CtxtLetDecl | BINDER b, (ctxt :: _) -> let blockLet = match ctxt with CtxtSeqBlock _ -> true | _ -> false @@ -508,8 +508,85 @@ I've been thinking about the slight annoyance of the desugaring that happens in * Just moving `Return` to cover the lambda that represents everything after the last `and!` is hard enough! -* What if the user calls `Yield` more than once? Do we disallow it? If we did allow it, what would the semantics/desugaring be? +```fsharp +option { + let! (a,_) = aExpr + and! (_,b) = bExpr + and! (SingleCaseDu c) = cExpr + let d = 3 + 4 + return (a + b + c) * d +} + +// desugars to: + +builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + let d = 3 + 4 + (a + b + c) * d)))), + aExpr), + bExpr), + cExpr) +``` + +* What if the user calls `Yield` more than once? Do we disallow it? If we did allow it, what would the semantics/desugaring be? (This corresponds to "alternative applicatives", I think) + +```fsharp +option { + let! (a,_) = aExpr + and! (_,b) = bExpr + and! (SingleCaseDu c) = cExpr + yield (a + b + c) // Combine cases with alternation function (i.e. `Combine`), earlier yields trump later ones + yield (b + 1) + yield (a + c) +} + +// desugars to: +builder.Combine( + builder.Combine( + builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (a + b + c))))), + aExpr), + bExpr), + cExpr), + builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (b + 1))))), + aExpr), + bExpr), + cExpr) + ), + builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (a + c))))), + aExpr), + bExpr), + cExpr) +) +``` * `let!` when `Apply` and `Return` are defined, but not `Bind` (this is highly related to the `Map` RFC for `let! ... return ...`). -* `use! ... anduse! ...` \ No newline at end of file +* `use! ... anduse! ...` + +* `yield!` and `return!` \ No newline at end of file From 3d79a28014d9552fe3ccbb3271ce0cccd17b3ee2 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 11:40:55 +0100 Subject: [PATCH 098/255] Add extra notes on semi-groups and active patterns on the LHS --- scratch/docs/Log.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index a34d0c2d9e..5e3c80260a 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -533,14 +533,16 @@ builder.Apply( cExpr) ``` -* What if the user calls `Yield` more than once? Do we disallow it? If we did allow it, what would the semantics/desugaring be? (This corresponds to "alternative applicatives", I think) +* What if the user calls `Yield` more than once? Do we disallow it? If we did allow it, what would the semantics/desugaring be? (This corresponds to "alternative applicatives", I think). Follow ups: + * What if an active pattern appears on the LHS - do we call it up to (number of `yield` occurances) times? + * What if `Return` isn't defined? Should we support semi-groups as well as monoids? ```fsharp option { let! (a,_) = aExpr and! (_,b) = bExpr - and! (SingleCaseDu c) = cExpr - yield (a + b + c) // Combine cases with alternation function (i.e. `Combine`), earlier yields trump later ones + and! (SingleCaseDu c) = cExpr // Can the pattern be an active pattern? Does that mean it's potentially called once per occurance of `yield` below? + yield (a + b + c) // `yield` implies alternation, i.e. a monoid (maybe we should support semi-groups too?) yield (b + 1) yield (a + c) } From 27035b93c528a236c5e77bef1b1e2298bff6b151 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 11:47:59 +0100 Subject: [PATCH 099/255] Add desugaring example with normal `let`s interspersed with `yield`s --- scratch/docs/Log.md | 60 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 5e3c80260a..ba5be28f51 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -519,6 +519,7 @@ option { // desugars to: +let d = 3 + 4 // Code outside the `Return` does not have names bound via `let! ... and! ...` in scope builder.Apply( builder.Apply( builder.Apply( @@ -526,7 +527,6 @@ builder.Apply( (fun (a,_) -> (fun (_,b) -> (fun (SingleCaseDu c) -> - let d = 3 + 4 (a + b + c) * d)))), aExpr), bExpr), @@ -587,6 +587,64 @@ builder.Combine( ) ``` +Then thinking about more code outside of `yield` and `return` everythere it could conceivably be valid: + +```fsharp +option { + let! (a,_) = aExpr + and! (_,b) = bExpr + and! (SingleCaseDu c) = cExpr + let n = 7 + yield (a + b + c + n) + let m = 100 + yield (b + m) + let o = -1 + yield (a + c + n + m + o) +} + +// desugars to: +let n = 7 // Any effects here will happen before any of the `yield` lambdas - is that acceptable? Could be cause for confusion since order is different to that implied by the ordering of the lines the code appears on. Binding need to be first so that they are in scope of all of the alternatives below. +let m = 100 +let o = -1 +builder.Combine( + builder.Combine( + builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (a + b + c + n))))), + aExpr), + bExpr), + cExpr), + builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (b + m))))), + aExpr), + bExpr), + cExpr) + ), + builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (a + c + n + m + o))))), + aExpr), + bExpr), + cExpr) +) +``` + * `let!` when `Apply` and `Return` are defined, but not `Bind` (this is highly related to the `Map` RFC for `let! ... return ...`). * `use! ... anduse! ...` From 4e94f1ef10f53f76b33a2a68f482a6bff9093d5d Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 11:59:22 +0100 Subject: [PATCH 100/255] Fix ordering of `let` bindings in applicative CE desugarings --- scratch/docs/Log.md | 112 +++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index ba5be28f51..179b3ad558 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -548,31 +548,34 @@ option { } // desugars to: -builder.Combine( - builder.Combine( + +let alt1 = + builder.Apply( builder.Apply( builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (_,b) -> - (fun (SingleCaseDu c) -> - (a + b + c))))), - aExpr), - bExpr), - cExpr), + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (a + b + c))))), + aExpr), + bExpr), + cExpr) + +let alt2 = + builder.Apply( builder.Apply( builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (_,b) -> - (fun (SingleCaseDu c) -> - (b + 1))))), - aExpr), - bExpr), - cExpr) - ), + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (b + 1))))), + aExpr), + bExpr), + cExpr) + +let alt3 = builder.Apply( builder.Apply( builder.Apply( @@ -584,7 +587,10 @@ builder.Combine( aExpr), bExpr), cExpr) -) + +builder.Combine( + builder.Combine(alt1,alt2), + alt3) ``` Then thinking about more code outside of `yield` and `return` everythere it could conceivably be valid: @@ -594,6 +600,7 @@ option { let! (a,_) = aExpr and! (_,b) = bExpr and! (SingleCaseDu c) = cExpr + let n = 7 yield (a + b + c + n) let m = 100 @@ -603,34 +610,40 @@ option { } // desugars to: -let n = 7 // Any effects here will happen before any of the `yield` lambdas - is that acceptable? Could be cause for confusion since order is different to that implied by the ordering of the lines the code appears on. Binding need to be first so that they are in scope of all of the alternatives below. -let m = 100 -let o = -1 -builder.Combine( - builder.Combine( + +let n = 7 + +let alt1 = + builder.Apply( builder.Apply( builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (_,b) -> - (fun (SingleCaseDu c) -> - (a + b + c + n))))), - aExpr), - bExpr), - cExpr), + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (a + b + c + n))))), + aExpr), + bExpr), + cExpr) + +let m = 100 // By `let` binding here, we keep the expecting scoping and ordering of side-effects + +let alt2 = + builder.Apply( builder.Apply( builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (_,b) -> - (fun (SingleCaseDu c) -> - (b + m))))), - aExpr), - bExpr), - cExpr) - ), + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (b + m))))), + aExpr), + bExpr), + cExpr) + +let o = -1 + +let alt3 = builder.Apply( builder.Apply( builder.Apply( @@ -642,7 +655,12 @@ builder.Combine( aExpr), bExpr), cExpr) -) + +builder.Combine( + builder.Combine(alt1,alt2), + alt3) + +/// Where `alt1`, `alt2`, `alt3` are just fresh variables ``` * `let!` when `Apply` and `Return` are defined, but not `Bind` (this is highly related to the `Map` RFC for `let! ... return ...`). From 2b2f75d013e53a8598e95eab1e0b957c7376d2fb Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 12:19:41 +0100 Subject: [PATCH 101/255] Add nested applicative CE & `yield!` example desugarings --- scratch/docs/Log.md | 63 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 179b3ad558..52918ddbc4 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -598,7 +598,7 @@ Then thinking about more code outside of `yield` and `return` everythere it coul ```fsharp option { let! (a,_) = aExpr - and! (_,b) = bExpr + and! (b,b2) = bExpr and! (SingleCaseDu c) = cExpr let n = 7 @@ -606,7 +606,7 @@ option { let m = 100 yield (b + m) let o = -1 - yield (a + c + n + m + o) + yield (a + b2 + c + n + m + o) } // desugars to: @@ -619,7 +619,7 @@ let alt1 = builder.Apply( builder.Return( (fun (a,_) -> - (fun (_,b) -> + (fun (b,b2) -> (fun (SingleCaseDu c) -> (a + b + c + n))))), aExpr), @@ -634,7 +634,7 @@ let alt2 = builder.Apply( builder.Return( (fun (a,_) -> - (fun (_,b) -> + (fun (b,b2) -> (fun (SingleCaseDu c) -> (b + m))))), aExpr), @@ -649,9 +649,9 @@ let alt3 = builder.Apply( builder.Return( (fun (a,_) -> - (fun (_,b) -> + (fun (b,b2) -> (fun (SingleCaseDu c) -> - (a + c + n + m + o))))), + (a + b2 + c + n + m + o))))), aExpr), bExpr), cExpr) @@ -663,8 +663,53 @@ builder.Combine( /// Where `alt1`, `alt2`, `alt3` are just fresh variables ``` -* `let!` when `Apply` and `Return` are defined, but not `Bind` (this is highly related to the `Map` RFC for `let! ... return ...`). +* Nesting of applicative CEs & `yield!` / `return!` + +```fsharp +option { + let! (a,_) = aExpr + and! (b,_) = bExpr + + yield a + + let n = 7 + + yield! ( + option { + let! c = cExpr + and! d = dExpr + return (b + c + d + n) + }) +} -* `use! ... anduse! ...` +// desugars to: + +let alt1 = + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (b,_) -> + a))), + aExpr), + bExpr) + +let n = 7 + +let alt2 = + builder.YieldFrom( // What would a sensible implementation be except `id`? + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (b,_) -> + a))), + aExpr), + bExpr)) + +builder.Combine(alt1, alt2) +``` + +* `let!` when `Apply` and `Return` are defined, but not `Bind` (this is highly related to the `Map` RFC for `let! ... return ...`). -* `yield!` and `return!` \ No newline at end of file +* `use! ... anduse! ...` \ No newline at end of file From 5a988192c434be1d7ae85e54ef5183f961c9e9d9 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 12:24:45 +0100 Subject: [PATCH 102/255] Change formatting of today's notes to be clearer (bullets -> headers) --- scratch/docs/Log.md | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 52918ddbc4..f754290044 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -506,7 +506,7 @@ My example almost works now, in that it builds and runs, but I accidentally doub I've been thinking about the slight annoyance of the desugaring that happens in `let! ... and! ...`, specifically, how the `Return` gets "lifted" to cover the whole block below the last `and!`, including whatever expression is handed to `Return`. The tricky examples I can think of right now are: -* Just moving `Return` to cover the lambda that represents everything after the last `and!` is hard enough! +### Just moving `Return` to cover the lambda that represents everything after the last `and!` is hard enough! ```fsharp option { @@ -533,7 +533,7 @@ builder.Apply( cExpr) ``` -* What if the user calls `Yield` more than once? Do we disallow it? If we did allow it, what would the semantics/desugaring be? (This corresponds to "alternative applicatives", I think). Follow ups: +### What if the user calls `Yield` more than once? Do we disallow it? If we did allow it, what would the semantics/desugaring be? (This corresponds to "alternative applicatives", I think). Follow ups: * What if an active pattern appears on the LHS - do we call it up to (number of `yield` occurances) times? * What if `Return` isn't defined? Should we support semi-groups as well as monoids? @@ -663,7 +663,7 @@ builder.Combine( /// Where `alt1`, `alt2`, `alt3` are just fresh variables ``` -* Nesting of applicative CEs & `yield!` / `return!` +### Nesting of applicative CEs & `yield!` / `return!` ```fsharp option { @@ -710,6 +710,37 @@ let alt2 = builder.Combine(alt1, alt2) ``` -* `let!` when `Apply` and `Return` are defined, but not `Bind` (this is highly related to the `Map` RFC for `let! ... return ...`). +### `let!` when `Apply` and `Return` are defined, but not `Bind` -* `use! ... anduse! ...` \ No newline at end of file +***N.B. This is highly related to the `Map` RFC for `let! ... return ...`*** + +_If_ we chose to implement `Map` from `Apply` (in order to make a single `let!` for when `Apply` and `Return` are defined but not `Bind`), it could look like this: + +```fsharp +option { + let! (a,_) = aExpr + and! (_,b) = bExpr + and! (SingleCaseDu c) = cExpr + let d = 3 + 4 + return (a + b + c) * d +} + +// desugars to: + +let d = 3 + 4 // Code outside the `Return` does not have names bound via `let! ... and! ...` in scope +builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (_,b) -> + (fun (SingleCaseDu c) -> + (a + b + c) * d)))), + aExpr), + bExpr), + cExpr) +``` + +### `use! ... anduse! ...` + +Not sure how normal `use!` is handled yet, but hopefully it is largely orthogonal in its implementation from the rest of the desugaring. \ No newline at end of file From 37ccaabde63f08c2cfbc0148b963e6a8cecd1d34 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 12:27:05 +0100 Subject: [PATCH 103/255] Show example of `Apply` being used to implement `Map` --- scratch/docs/Log.md | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index f754290044..f4d8222657 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -719,26 +719,18 @@ _If_ we chose to implement `Map` from `Apply` (in order to make a single `let!` ```fsharp option { let! (a,_) = aExpr - and! (_,b) = bExpr - and! (SingleCaseDu c) = cExpr - let d = 3 + 4 - return (a + b + c) * d + let d = 3 + return (a * d) } // desugars to: -let d = 3 + 4 // Code outside the `Return` does not have names bound via `let! ... and! ...` in scope +let d = 3 builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (_,b) -> - (fun (SingleCaseDu c) -> - (a + b + c) * d)))), - aExpr), - bExpr), - cExpr) + builder.Return( + (fun (a,_) -> + (a * d))), + aExpr) ``` ### `use! ... anduse! ...` From 51310350d916b1f0c75a987fc43f97f6a1de503a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 12:28:29 +0100 Subject: [PATCH 104/255] Touch up whitespace and comments --- scratch/docs/Log.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index f4d8222657..d13b5cafb2 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -598,7 +598,7 @@ Then thinking about more code outside of `yield` and `return` everythere it coul ```fsharp option { let! (a,_) = aExpr - and! (b,b2) = bExpr + and! (b,b2) = bExpr and! (SingleCaseDu c) = cExpr let n = 7 @@ -667,8 +667,8 @@ builder.Combine( ```fsharp option { - let! (a,_) = aExpr - and! (b,_) = bExpr + let! (a,_) = aExpr + and! (b,_) = bExpr yield a @@ -718,7 +718,7 @@ _If_ we chose to implement `Map` from `Apply` (in order to make a single `let!` ```fsharp option { - let! (a,_) = aExpr + let! (a,_) = aExpr let d = 3 return (a * d) } @@ -731,6 +731,8 @@ builder.Apply( (fun (a,_) -> (a * d))), aExpr) + +// This desugaring is just a corollary of the earlier, more complex desugarings ``` ### `use! ... anduse! ...` From 5ae38bcd635ecc2e35bb7d501e2112ad3b104b42 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 12:29:03 +0100 Subject: [PATCH 105/255] Fix incorrect MD for very strong emphasis --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index d13b5cafb2..8a965fa0a8 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -712,7 +712,7 @@ builder.Combine(alt1, alt2) ### `let!` when `Apply` and `Return` are defined, but not `Bind` -***N.B. This is highly related to the `Map` RFC for `let! ... return ...`*** +**N.B. This is highly related to the `Map` RFC for `let! ... return ...`** _If_ we chose to implement `Map` from `Apply` (in order to make a single `let!` for when `Apply` and `Return` are defined but not `Bind`), it could look like this: From 8755f50449cc27b998cb8da25a996a9e6399994b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 12:31:05 +0100 Subject: [PATCH 106/255] Record my opinion of picking Map vs. Apply vs.Bind in simple CEs --- scratch/docs/Log.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 8a965fa0a8..86193a08bb 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -735,6 +735,8 @@ builder.Apply( // This desugaring is just a corollary of the earlier, more complex desugarings ``` +My gut says we should use `Map` if it is available, else `Apply`, else `Bind`. + ### `use! ... anduse! ...` Not sure how normal `use!` is handled yet, but hopefully it is largely orthogonal in its implementation from the rest of the desugaring. \ No newline at end of file From a40b4e873fcb4fe9b5715af5f68929474be794c9 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 12:45:49 +0100 Subject: [PATCH 107/255] Record some key points for when rewriting the desugaring logic --- scratch/docs/Log.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 86193a08bb..e8c8c3b318 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -739,4 +739,10 @@ My gut says we should use `Map` if it is available, else `Apply`, else `Bind`. ### `use! ... anduse! ...` -Not sure how normal `use!` is handled yet, but hopefully it is largely orthogonal in its implementation from the rest of the desugaring. \ No newline at end of file +Not sure how normal `use!` is handled yet, but hopefully it is largely orthogonal in its implementation from the rest of the desugaring. + +### Overall concepts for the full desugaring + +1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) +1. A `Map` implementation just falls out of the `Apply` desugaring we have, so I think we should keep that, but allow it to be overriden by an explicit `Map` definition that has been appropriately annotated (similarly, if there is an existing `Bind`, `Apply` needs an annotation to trump `Bind` in the case where both effectively implement `Map`). +1. I won't bother implementing support for semi-groups (i.e. when there are >1 usages of `yield` but no `Zero` defined), although I haven't actually checked what happens in that case right now... \ No newline at end of file From 395336fb0b9a614a60603f52e5c9eff4e3193607 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 12:58:57 +0100 Subject: [PATCH 108/255] Clean up my current shapshot of questions and answers --- scratch/docs/Log.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index e8c8c3b318..98f446e0d5 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -737,12 +737,14 @@ builder.Apply( My gut says we should use `Map` if it is available, else `Apply`, else `Bind`. -### `use! ... anduse! ...` - -Not sure how normal `use!` is handled yet, but hopefully it is largely orthogonal in its implementation from the rest of the desugaring. - ### Overall concepts for the full desugaring +#### Rules 1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) 1. A `Map` implementation just falls out of the `Apply` desugaring we have, so I think we should keep that, but allow it to be overriden by an explicit `Map` definition that has been appropriately annotated (similarly, if there is an existing `Bind`, `Apply` needs an annotation to trump `Bind` in the case where both effectively implement `Map`). -1. I won't bother implementing support for semi-groups (i.e. when there are >1 usages of `yield` but no `Zero` defined), although I haven't actually checked what happens in that case right now... \ No newline at end of file + +#### Remaining Questions +* `do!` - how do we handle that with `Apply` and `Return`, etc.? +* I won't bother implementing support for semi-groups (i.e. when there are >1 usages of `yield` but no `Zero` defined), although I haven't actually checked what happens in that case right now... +* Need to check this vs. the existing implementation for `Bind`, but I assume an applicative CE should end in exactly one `return` or `return!`, else contain >= 1 `yield` or `yield!`, and the last line of the expression is a `yield` or `yield!`. Is that explicitly handled currently? +* Not sure how normal `use!` is handled yet, but hopefully it is largely orthogonal in its implementation from the rest of the desugaring. Need to work out what will happen there. \ No newline at end of file From 504f00925a7dcbbecfaa363623bce8a1ca2a28e5 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 15:13:28 +0100 Subject: [PATCH 109/255] Note that each RHS of a binding should be evaluated at most once --- scratch/docs/Log.md | 1 + 1 file changed, 1 insertion(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 98f446e0d5..21f56832af 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -742,6 +742,7 @@ My gut says we should use `Map` if it is available, else `Apply`, else `Bind`. #### Rules 1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) 1. A `Map` implementation just falls out of the `Apply` desugaring we have, so I think we should keep that, but allow it to be overriden by an explicit `Map` definition that has been appropriately annotated (similarly, if there is an existing `Bind`, `Apply` needs an annotation to trump `Bind` in the case where both effectively implement `Map`). +1. We should evaluate the RHS of each of the bindings only once - i.e. don't naively stamp out the expression n-times for every alternative implied by a `yield`. #### Remaining Questions * `do!` - how do we handle that with `Apply` and `Return`, etc.? From d349584a950117e7af2741ce1e194512563b896f Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 15:20:34 +0100 Subject: [PATCH 110/255] Fix desugaring repeatedly evaluating the RHS of bindings --- scratch/docs/Log.md | 68 +++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 21f56832af..cdf6502bd7 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -549,6 +549,10 @@ option { // desugars to: +let aExprEvaluated = aExpr +let bExprEvaluated = bExpr +let cExprEvaluated = cExpr + let alt1 = builder.Apply( builder.Apply( @@ -558,9 +562,9 @@ let alt1 = (fun (_,b) -> (fun (SingleCaseDu c) -> (a + b + c))))), - aExpr), - bExpr), - cExpr) + aExprEvaluated), + bExprEvaluated), + cExprEvaluated) let alt2 = builder.Apply( @@ -571,9 +575,9 @@ let alt2 = (fun (_,b) -> (fun (SingleCaseDu c) -> (b + 1))))), - aExpr), - bExpr), - cExpr) + aExprEvaluated), + bExprEvaluated), + cExprEvaluated) let alt3 = builder.Apply( @@ -584,9 +588,9 @@ let alt3 = (fun (_,b) -> (fun (SingleCaseDu c) -> (a + c))))), - aExpr), - bExpr), - cExpr) + aExprEvaluated), + bExprEvaluated), + cExprEvaluated) builder.Combine( builder.Combine(alt1,alt2), @@ -611,6 +615,10 @@ option { // desugars to: +let aExprEvaluated = aExpr +let bExprEvaluated = bExpr +let cExprEvaluated = cExpr + let n = 7 let alt1 = @@ -622,9 +630,9 @@ let alt1 = (fun (b,b2) -> (fun (SingleCaseDu c) -> (a + b + c + n))))), - aExpr), - bExpr), - cExpr) + aExprEvaluated), + bExprEvaluated), + cExprEvaluated) let m = 100 // By `let` binding here, we keep the expecting scoping and ordering of side-effects @@ -637,9 +645,9 @@ let alt2 = (fun (b,b2) -> (fun (SingleCaseDu c) -> (b + m))))), - aExpr), - bExpr), - cExpr) + aExprEvaluated), + bExprEvaluated), + cExprEvaluated) let o = -1 @@ -652,9 +660,9 @@ let alt3 = (fun (b,b2) -> (fun (SingleCaseDu c) -> (a + b2 + c + n + m + o))))), - aExpr), - bExpr), - cExpr) + aExprEvaluated), + bExprEvaluated), + cExprEvaluated) builder.Combine( builder.Combine(alt1,alt2), @@ -684,6 +692,9 @@ option { // desugars to: +let aExprEvaluated = aExpr +let bExprEvaluated = bExpr + let alt1 = builder.Apply( builder.Apply( @@ -691,21 +702,23 @@ let alt1 = (fun (a,_) -> (fun (b,_) -> a))), - aExpr), - bExpr) + aExprEvaluated), + bExprEvaluated) let n = 7 let alt2 = builder.YieldFrom( // What would a sensible implementation be except `id`? + let cExprEvaluated = cExpr + let dExprEvaluated = dExpr builder.Apply( builder.Apply( builder.Return( - (fun (a,_) -> - (fun (b,_) -> - a))), - aExpr), - bExpr)) + (fun c -> + (fun d -> + b + c + d + n))), + cExprEvaluated), + dExprEvaluated)) builder.Combine(alt1, alt2) ``` @@ -725,12 +738,13 @@ option { // desugars to: +let aExprEvaluated = aExpr let d = 3 builder.Apply( builder.Return( (fun (a,_) -> (a * d))), - aExpr) + aExprEvaluated) // This desugaring is just a corollary of the earlier, more complex desugarings ``` @@ -742,7 +756,7 @@ My gut says we should use `Map` if it is available, else `Apply`, else `Bind`. #### Rules 1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) 1. A `Map` implementation just falls out of the `Apply` desugaring we have, so I think we should keep that, but allow it to be overriden by an explicit `Map` definition that has been appropriately annotated (similarly, if there is an existing `Bind`, `Apply` needs an annotation to trump `Bind` in the case where both effectively implement `Map`). -1. We should evaluate the RHS of each of the bindings only once - i.e. don't naively stamp out the expression n-times for every alternative implied by a `yield`. +1. We should evaluate the RHS of each of the bindings only once - i.e. don't naively stamp out the expression n-times for every alternative implied by a `yield`. (This ensures side-effects of active patterns act as is probably exprected - once, and before the body of the CE and in the order they were defined) #### Remaining Questions * `do!` - how do we handle that with `Apply` and `Return`, etc.? From 134575bb734b69ff192dca37be47d3f128e3d097 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 16:33:38 +0100 Subject: [PATCH 111/255] Consider the implications for side-effects for various desugarings --- scratch/docs/Log.md | 63 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index cdf6502bd7..8cc3600646 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -519,6 +519,9 @@ option { // desugars to: +let aExprEvaluated = aExpr +let bExprEvaluated = bExpr +let cExprEvaluated = cExpr let d = 3 + 4 // Code outside the `Return` does not have names bound via `let! ... and! ...` in scope builder.Apply( builder.Apply( @@ -528,9 +531,9 @@ builder.Apply( (fun (_,b) -> (fun (SingleCaseDu c) -> (a + b + c) * d)))), - aExpr), - bExpr), - cExpr) + aExprEvaluated), + bExprEvaluated), + cExprEvaluated) ``` ### What if the user calls `Yield` more than once? Do we disallow it? If we did allow it, what would the semantics/desugaring be? (This corresponds to "alternative applicatives", I think). Follow ups: @@ -751,12 +754,64 @@ builder.Apply( My gut says we should use `Map` if it is available, else `Apply`, else `Bind`. +### Active Patterns + +```fsharp +let mutable spy = 0 + +let (|NumAndDoubleNum|) (x : int) = + spy <- spy + 1 + (x, x + x) + +option { + let! (a,_) = aExpr + and! (NumAndDoubleNum(b, doubleb)) = bExpr + + yield a + doubleb + + let n = 7 + + yield a + b + n +} + +// desugars to: + +let aExprEvaluated = aExpr +let bExprEvaluated = bExpr + +let alt1 = + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (NumAndDoubleNum(b, doubleb)) -> + a + doubleb))), + aExprEvaluated), + bExprEvaluated) + +let n = 7 + +let alt2 = + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (NumAndDoubleNum(b, doubleb)) -> + a + b + n))), + aExprEvaluated), + bExprEvaluated) + +builder.Combine(alt1, alt2) + +// spy = 2, is this unacceptably surprising? +``` + ### Overall concepts for the full desugaring #### Rules 1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) 1. A `Map` implementation just falls out of the `Apply` desugaring we have, so I think we should keep that, but allow it to be overriden by an explicit `Map` definition that has been appropriately annotated (similarly, if there is an existing `Bind`, `Apply` needs an annotation to trump `Bind` in the case where both effectively implement `Map`). -1. We should evaluate the RHS of each of the bindings only once - i.e. don't naively stamp out the expression n-times for every alternative implied by a `yield`. (This ensures side-effects of active patterns act as is probably exprected - once, and before the body of the CE and in the order they were defined) +1. We should evaluate the RHS of each of the bindings only once - i.e. don't naively stamp out the expression n-times for every alternative implied by a `yield`. Although pattern matching has to happen for each `yield`, since until we are inside the `yield`'s lambda, we only have a functor in hand, and can't inspect the contents directly. This is only interesting since: pattern matching implies some runtime cost, and active patterns can have side-effects (arguably, that's enough of an abuse that it doesn't matter too much, but I don't like that it is perhaps surprising given the syntax of the CE). #### Remaining Questions * `do!` - how do we handle that with `Apply` and `Return`, etc.? From 13293a9e8d9ffbd131049c5dc3150adac0d72594 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 16:37:52 +0100 Subject: [PATCH 112/255] Capture argument that facotring out bindings per yield might actually be precisely what is counter intuitive --- scratch/docs/Log.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 8cc3600646..d4c9b8256f 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -808,10 +808,13 @@ builder.Combine(alt1, alt2) ### Overall concepts for the full desugaring +One claim might be: _We should evaluate the RHS of each of the bindings only once_ - i.e. don't naively stamp out the expression n-times for every alternative implied by a `yield`. Although pattern matching has to happen for each `yield`, since until we are inside the `yield`'s lambda, we only have a functor in hand, and can't inspect the contents directly. This is only interesting since: pattern matching implies some runtime cost, and active patterns can have side-effects (arguably, that's enough of an abuse that it doesn't matter too much, but I don't like that it is perhaps surprising given the syntax of the CE). + +Another perspective is that by "factoring out" the bindings, we've actually done away with some of the notion that the application implied by each `return` or `yield` is orthogonal, and it's everything else which is the exception, and active patterns are the ones following the rules and doing what is expected. + #### Rules 1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) 1. A `Map` implementation just falls out of the `Apply` desugaring we have, so I think we should keep that, but allow it to be overriden by an explicit `Map` definition that has been appropriately annotated (similarly, if there is an existing `Bind`, `Apply` needs an annotation to trump `Bind` in the case where both effectively implement `Map`). -1. We should evaluate the RHS of each of the bindings only once - i.e. don't naively stamp out the expression n-times for every alternative implied by a `yield`. Although pattern matching has to happen for each `yield`, since until we are inside the `yield`'s lambda, we only have a functor in hand, and can't inspect the contents directly. This is only interesting since: pattern matching implies some runtime cost, and active patterns can have side-effects (arguably, that's enough of an abuse that it doesn't matter too much, but I don't like that it is perhaps surprising given the syntax of the CE). #### Remaining Questions * `do!` - how do we handle that with `Apply` and `Return`, etc.? From 94c4bce434583c32db6c1da9b30d842ab6712f2f Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 16:40:22 +0100 Subject: [PATCH 113/255] Refine thoughts on what logic should be commonised/duplicated in let! ... and! ... notation --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index d4c9b8256f..44dc26b690 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -810,7 +810,7 @@ builder.Combine(alt1, alt2) One claim might be: _We should evaluate the RHS of each of the bindings only once_ - i.e. don't naively stamp out the expression n-times for every alternative implied by a `yield`. Although pattern matching has to happen for each `yield`, since until we are inside the `yield`'s lambda, we only have a functor in hand, and can't inspect the contents directly. This is only interesting since: pattern matching implies some runtime cost, and active patterns can have side-effects (arguably, that's enough of an abuse that it doesn't matter too much, but I don't like that it is perhaps surprising given the syntax of the CE). -Another perspective is that by "factoring out" the bindings, we've actually done away with some of the notion that the application implied by each `return` or `yield` is orthogonal, and it's everything else which is the exception, and active patterns are the ones following the rules and doing what is expected. +Another perspective is that by "factoring out" the bindings, we've actually done away with some of the notion that the application implied by each `return` or `yield` is orthogonal, and it's everything else which is the exception, and active patterns are the ones following the rules and doing what is expected. From that perspective, `let! ... and! ...` can be simply considered as each `yield` and each `binding` happening separately/in parallel - of course that does make it easy to build something which looks terse but implies a very large computation (not that we can't do that already...). #### Rules 1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) From 67d03aed62db103cefce40d4385d25a23d80d950 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 16:44:55 +0100 Subject: [PATCH 114/255] Add desugaring examples for commonising and duplicating approaches to let! ... and! ... desugaring --- scratch/docs/Log.md | 99 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 44dc26b690..0fcb560d07 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -812,6 +812,105 @@ One claim might be: _We should evaluate the RHS of each of the bindings only onc Another perspective is that by "factoring out" the bindings, we've actually done away with some of the notion that the application implied by each `return` or `yield` is orthogonal, and it's everything else which is the exception, and active patterns are the ones following the rules and doing what is expected. From that perspective, `let! ... and! ...` can be simply considered as each `yield` and each `binding` happening separately/in parallel - of course that does make it easy to build something which looks terse but implies a very large computation (not that we can't do that already...). +Might need to consult the literature on this one, although the introduction of side-effects might be what is taking us off-piste. + +#### "Commonising" approach +```fsharp +let mutable spy = 0 + +let (|NumAndDoubleNum|) (x : int) = + spy <- spy + 1 + (x, x + x) + +option { + let! (a,_) = aExpr + and! (NumAndDoubleNum(b, doubleb)) = bExpr + + yield a + doubleb + + let n = 7 + + yield a + b + n +} + +// desugars to: + +let aExprEvaluated = aExpr +let bExprEvaluated = bExpr + +let alt1 = + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (NumAndDoubleNum(b, doubleb)) -> + a + doubleb))), + aExprEvaluated), + bExprEvaluated) + +let n = 7 + +let alt2 = + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (NumAndDoubleNum(b, doubleb)) -> // N.B. That the RHS of the bindings are each evaluated once, but the pattern match is done once _per yield_ + a + b + n))), + aExprEvaluated), + bExprEvaluated) + +builder.Combine(alt1, alt2) + +// spy = 2, is this unacceptably surprising? +``` + +#### "Duplication" approach +```fsharp +let mutable spy = 0 + +let (|NumAndDoubleNum|) (x : int) = + spy <- spy + 1 + (x, x + x) + +option { + let! (a,_) = aExpr + and! (NumAndDoubleNum(b, doubleb)) = bExpr + + yield a + doubleb + + let n = 7 + + yield a + b + n +} + +// desugars to: + +let alt1 = + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (NumAndDoubleNum(b, doubleb)) -> + a + doubleb))), + aExpr), + bExpr) + +let n = 7 + +let alt2 = + builder.Apply( + builder.Apply( + builder.Return( + (fun (a,_) -> + (fun (NumAndDoubleNum(b, doubleb)) -> + a + b + n))), + aExpr), + bExpr) // N.B. Second evaluation of bExpr here + +builder.Combine(alt1, alt2) +``` + #### Rules 1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) 1. A `Map` implementation just falls out of the `Apply` desugaring we have, so I think we should keep that, but allow it to be overriden by an explicit `Map` definition that has been appropriately annotated (similarly, if there is an existing `Bind`, `Apply` needs an annotation to trump `Bind` in the case where both effectively implement `Map`). From 08c6457b6e0641dbe89986f2b5f1b6bb03e0d436 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 16:49:35 +0100 Subject: [PATCH 115/255] Add another option: ban yield inside a let! ... and! ... --- scratch/docs/Log.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 0fcb560d07..13f85edf26 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -815,6 +815,9 @@ Another perspective is that by "factoring out" the bindings, we've actually done Might need to consult the literature on this one, although the introduction of side-effects might be what is taking us off-piste. #### "Commonising" approach + +Potentially more efficient. Side-effects from active patterns happen N times, where everything else happens once. + ```fsharp let mutable spy = 0 @@ -866,6 +869,9 @@ builder.Combine(alt1, alt2) ``` #### "Duplication" approach + +Evaluation of names bound in the `let! ... and! ...` happens once for each yield, but at least this agrees with the number of times the active pattern is called, and is simpler to implement and reason about. + ```fsharp let mutable spy = 0 @@ -911,6 +917,10 @@ let alt2 = builder.Combine(alt1, alt2) ``` +#### "Just don't support yield" approach + +If we support only `Return` and not `Yield` inside `let! ... and! ...` compuation expressions, then this problem goes away. + #### Rules 1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) 1. A `Map` implementation just falls out of the `Apply` desugaring we have, so I think we should keep that, but allow it to be overriden by an explicit `Map` definition that has been appropriately annotated (similarly, if there is an existing `Bind`, `Apply` needs an annotation to trump `Bind` in the case where both effectively implement `Map`). From 4f7b31116f07b2071454fdf0c0203a1386b95b25 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 16:55:02 +0100 Subject: [PATCH 116/255] Add some more arguments around possible desugarings --- scratch/docs/Log.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 13f85edf26..fae92b00c6 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -816,7 +816,7 @@ Might need to consult the literature on this one, although the introduction of s #### "Commonising" approach -Potentially more efficient. Side-effects from active patterns happen N times, where everything else happens once. +Potentially more efficient. Side-effects from active patterns happen N times, but normal `let`s and evaluation of the RHS of the `let! ... and! ...` happens only once. One could argue that, apart from the side-effect of active patterns, this more closely agrees with the syntax. ```fsharp let mutable spy = 0 @@ -870,7 +870,7 @@ builder.Combine(alt1, alt2) #### "Duplication" approach -Evaluation of names bound in the `let! ... and! ...` happens once for each yield, but at least this agrees with the number of times the active pattern is called, and is simpler to implement and reason about. +Evaluation of names bound in the `let! ... and! ...` happens once for each yield (so potentially a lot of times!), but at least this agrees with the number of times the active pattern is called. One could argue that things happening for each `yield` is surprising given the syntax, but perhaps the fact that `and!` is there is enough to signal that we've shifted to a different "mode". I think this approach is simpler to implement and reason about. ```fsharp let mutable spy = 0 From 9d5b59183485cea2bd4ec5df2dbd8e01d87828c6 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 16:57:07 +0100 Subject: [PATCH 117/255] Make the "duplication" approach more internally consistent --- scratch/docs/Log.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index fae92b00c6..79b2a3845a 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -893,6 +893,7 @@ option { // desugars to: let alt1 = + let n = 7 // N.B. This has been duplicated in each alternative, so everything happens as many times as a `yield` occurs in the CE builder.Apply( builder.Apply( builder.Return( @@ -902,9 +903,9 @@ let alt1 = aExpr), bExpr) -let n = 7 let alt2 = + let n = 7 builder.Apply( builder.Apply( builder.Return( @@ -915,6 +916,8 @@ let alt2 = bExpr) // N.B. Second evaluation of bExpr here builder.Combine(alt1, alt2) + +// spy = 2, but that should make sense if we imagine that `and!` signals, amongst other things, that each yield corresponds to another stamping out ``` #### "Just don't support yield" approach From 17f66f17b41e3dd7631c2834b2d022c5d7f5c32a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 17:01:32 +0100 Subject: [PATCH 118/255] Appeal to example with loops as prior art for the commising vs. duplication conundrum --- scratch/docs/Log.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 79b2a3845a..9d1c238eb8 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -872,6 +872,8 @@ builder.Combine(alt1, alt2) Evaluation of names bound in the `let! ... and! ...` happens once for each yield (so potentially a lot of times!), but at least this agrees with the number of times the active pattern is called. One could argue that things happening for each `yield` is surprising given the syntax, but perhaps the fact that `and!` is there is enough to signal that we've shifted to a different "mode". I think this approach is simpler to implement and reason about. +Prior art in loops: If a function with side-effects exists in a loop, we don't automagically pull it out so it's only called once! Yes, you can shoot your foot off by leaving something nasty in a hot loop, but you can also factor it out and save yourself. At least it's clear what is going to happen. + ```fsharp let mutable spy = 0 From 3c1271dba21b361a9ff76da2569c02a5acfdc745 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 17:03:58 +0100 Subject: [PATCH 119/255] Make the potential side-effects more clear in my examples --- scratch/docs/Log.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 9d1c238eb8..6eb7f24a16 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -821,17 +821,17 @@ Potentially more efficient. Side-effects from active patterns happen N times, bu ```fsharp let mutable spy = 0 -let (|NumAndDoubleNum|) (x : int) = +let (|NastySideEffectfulNumAndDoubleNum|) (x : int) = spy <- spy + 1 (x, x + x) option { let! (a,_) = aExpr - and! (NumAndDoubleNum(b, doubleb)) = bExpr + and! (NastySideEffectfulNumAndDoubleNum(b, doubleb)) = bExpr yield a + doubleb - let n = 7 + let n = nastySideEffectfulComputation () yield a + b + n } @@ -846,19 +846,19 @@ let alt1 = builder.Apply( builder.Return( (fun (a,_) -> - (fun (NumAndDoubleNum(b, doubleb)) -> + (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> a + doubleb))), aExprEvaluated), bExprEvaluated) -let n = 7 +let n = nastySideEffectfulComputation () let alt2 = builder.Apply( builder.Apply( builder.Return( (fun (a,_) -> - (fun (NumAndDoubleNum(b, doubleb)) -> // N.B. That the RHS of the bindings are each evaluated once, but the pattern match is done once _per yield_ + (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> // N.B. That the RHS of the bindings are each evaluated once, but the pattern match is done once _per yield_ a + b + n))), aExprEvaluated), bExprEvaluated) @@ -877,17 +877,17 @@ Prior art in loops: If a function with side-effects exists in a loop, we don't a ```fsharp let mutable spy = 0 -let (|NumAndDoubleNum|) (x : int) = +let (|NastySideEffectfulNumAndDoubleNum|) (x : int) = spy <- spy + 1 (x, x + x) option { let! (a,_) = aExpr - and! (NumAndDoubleNum(b, doubleb)) = bExpr + and! (NastySideEffectfulNumAndDoubleNum(b, doubleb)) = bExpr yield a + doubleb - let n = 7 + let n = nastySideEffectfulComputation () yield a + b + n } @@ -895,24 +895,24 @@ option { // desugars to: let alt1 = - let n = 7 // N.B. This has been duplicated in each alternative, so everything happens as many times as a `yield` occurs in the CE + let n = nastySideEffectfulComputation () // N.B. This has been duplicated in each alternative, so everything happens as many times as a `yield` occurs in the CE builder.Apply( builder.Apply( builder.Return( (fun (a,_) -> - (fun (NumAndDoubleNum(b, doubleb)) -> + (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> a + doubleb))), aExpr), bExpr) let alt2 = - let n = 7 + let n = nastySideEffectfulComputation () builder.Apply( builder.Apply( builder.Return( (fun (a,_) -> - (fun (NumAndDoubleNum(b, doubleb)) -> + (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> a + b + n))), aExpr), bExpr) // N.B. Second evaluation of bExpr here From 3dd69dd6f4423d0a67d39959c6d1a62161f866be Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 17:10:39 +0100 Subject: [PATCH 120/255] Add note on `use!` --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 6eb7f24a16..2b4aed5ac0 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -934,4 +934,4 @@ If we support only `Return` and not `Yield` inside `let! ... and! ...` compuatio * `do!` - how do we handle that with `Apply` and `Return`, etc.? * I won't bother implementing support for semi-groups (i.e. when there are >1 usages of `yield` but no `Zero` defined), although I haven't actually checked what happens in that case right now... * Need to check this vs. the existing implementation for `Bind`, but I assume an applicative CE should end in exactly one `return` or `return!`, else contain >= 1 `yield` or `yield!`, and the last line of the expression is a `yield` or `yield!`. Is that explicitly handled currently? -* Not sure how normal `use!` is handled yet, but hopefully it is largely orthogonal in its implementation from the rest of the desugaring. Need to work out what will happen there. \ No newline at end of file +* `Using : 'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable` i.e. kind of a `pure` and a `map` in one. Valid for use inside an `apply` as far as I can tell at first glance (in `use!` right now, we get a `Bind` with a `Using` nested directly inside it). \ No newline at end of file From 16a82c4b806ea3eb9d0062d0431686a75c8832db Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 17:33:43 +0100 Subject: [PATCH 121/255] Fix let location in desugaring --- scratch/docs/Log.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 2b4aed5ac0..00fce1c517 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -895,24 +895,24 @@ option { // desugars to: let alt1 = - let n = nastySideEffectfulComputation () // N.B. This has been duplicated in each alternative, so everything happens as many times as a `yield` occurs in the CE builder.Apply( builder.Apply( builder.Return( (fun (a,_) -> (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> + let n = nastySideEffectfulComputation () // N.B. This has been duplicated in each alternative, so everything happens as many times as a `yield` occurs in the CE a + doubleb))), aExpr), bExpr) let alt2 = - let n = nastySideEffectfulComputation () builder.Apply( builder.Apply( builder.Return( (fun (a,_) -> (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> + let n = nastySideEffectfulComputation () // N.B. This has been duplicated in each alternative, so everything happens as many times as a `yield` occurs in the CE a + b + n))), aExpr), bExpr) // N.B. Second evaluation of bExpr here From d2bb47c79e4972c0f9d0f616956ee1840009c405 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 18:10:59 +0100 Subject: [PATCH 122/255] Record @Nick's suggestions and contributions after our discussion --- scratch/docs/Log.md | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 00fce1c517..72585b7b70 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -718,7 +718,7 @@ let alt2 = builder.Apply( builder.Return( (fun c -> - (fun d -> + (fun d -> // TODO Where on earth does `b` get applied in?! b + c + d + n))), cExprEvaluated), dExprEvaluated)) @@ -900,7 +900,7 @@ let alt1 = builder.Return( (fun (a,_) -> (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> - let n = nastySideEffectfulComputation () // N.B. This has been duplicated in each alternative, so everything happens as many times as a `yield` occurs in the CE + let n = nastySideEffectfulComputation () // This comes _after_ this yield. Why is it here?! a + doubleb))), aExpr), bExpr) @@ -934,4 +934,35 @@ If we support only `Return` and not `Yield` inside `let! ... and! ...` compuatio * `do!` - how do we handle that with `Apply` and `Return`, etc.? * I won't bother implementing support for semi-groups (i.e. when there are >1 usages of `yield` but no `Zero` defined), although I haven't actually checked what happens in that case right now... * Need to check this vs. the existing implementation for `Bind`, but I assume an applicative CE should end in exactly one `return` or `return!`, else contain >= 1 `yield` or `yield!`, and the last line of the expression is a `yield` or `yield!`. Is that explicitly handled currently? -* `Using : 'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable` i.e. kind of a `pure` and a `map` in one. Valid for use inside an `apply` as far as I can tell at first glance (in `use!` right now, we get a `Bind` with a `Using` nested directly inside it). \ No newline at end of file +* `Using : 'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable` i.e. kind of a `pure` and a `map` in one. Valid for use inside an `apply` as far as I can tell at first glance (in `use!` right now, we get a `Bind` with a `Using` nested directly inside it). + +## 2018-09-17 + +How does `yield` desugar into `Combine` right now? Doesn't that mean walking down the expression an doing some rewriting? Can that help us with the issues from yesterday? + +After a conversation wtih @Nick: Can we do this as our desugaring for `let! ... and! ...` in the face of `yield`: +```fsharp +option { + let! x = Some 10 + and! y = Some 20 + let n = 1 + yield x + 5 + let m = 2 + yield y + 5 + } + + // desugaring to: + +builder.Apply( + builder.Apply( + builder.Return( + (fun x -> // Any pattern matching happens once + (fun y -> + builder.YieldFrom( + let n = 1 in + builder.Combine( + builder.Yield(x + 5), // The slightly strange injection of Combine must surely already have corresponding logic when we're using Bind instead? Can we work from that to build the logic for the desuagring here? + let m = 2 in builder.Yield(y + 5)))))), + aExpr), + bExpr) +``` \ No newline at end of file From 0ddb19fd1dd7b44828ef7c498faf8aa541d66fd2 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 18:12:02 +0100 Subject: [PATCH 123/255] Make `let`s slightly more interesting in @Nick's example --- scratch/docs/Log.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 72585b7b70..56c392e10f 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -946,9 +946,9 @@ option { let! x = Some 10 and! y = Some 20 let n = 1 - yield x + 5 + yield x + n let m = 2 - yield y + 5 + yield y + n + m } // desugaring to: @@ -961,8 +961,8 @@ builder.Apply( builder.YieldFrom( let n = 1 in builder.Combine( - builder.Yield(x + 5), // The slightly strange injection of Combine must surely already have corresponding logic when we're using Bind instead? Can we work from that to build the logic for the desuagring here? - let m = 2 in builder.Yield(y + 5)))))), + builder.Yield(x + n), // The slightly strange injection of Combine must surely already have corresponding logic when we're using Bind instead? Can we work from that to build the logic for the desuagring here? + let m = 2 in builder.Yield(y + m)))))), aExpr), bExpr) ``` \ No newline at end of file From 8c3fb518fcf6546e21f791841af06d8e7df28423 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 17 Sep 2018 18:13:29 +0100 Subject: [PATCH 124/255] Clean up comments on Nick's example --- scratch/docs/Log.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 56c392e10f..cd24f88e0a 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -960,9 +960,10 @@ builder.Apply( (fun y -> builder.YieldFrom( let n = 1 in - builder.Combine( - builder.Yield(x + n), // The slightly strange injection of Combine must surely already have corresponding logic when we're using Bind instead? Can we work from that to build the logic for the desuagring here? - let m = 2 in builder.Yield(y + m)))))), + builder.Combine( // The slightly strange injection of Combine must surely already have corresponding logic when we're using Bind instead? Can we work from that to build the logic for the desuagring here? + builder.Yield(x + n), + let m = 2 in // Names only in scope as CE syntax would imply + builder.Yield(y + m)))))), aExpr), bExpr) ``` \ No newline at end of file From e39ddc5dfe924bf05d395b234feecbe6460e7e93 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 10:42:00 +0100 Subject: [PATCH 125/255] Fix mistakes in `let! ... and! ...` desugaring --- scratch/docs/Log.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index cd24f88e0a..6360243944 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -958,12 +958,11 @@ builder.Apply( builder.Return( (fun x -> // Any pattern matching happens once (fun y -> - builder.YieldFrom( - let n = 1 in - builder.Combine( // The slightly strange injection of Combine must surely already have corresponding logic when we're using Bind instead? Can we work from that to build the logic for the desuagring here? - builder.Yield(x + n), - let m = 2 in // Names only in scope as CE syntax would imply - builder.Yield(y + m)))))), - aExpr), - bExpr) + let n = 1 in + builder.Combine( // The slightly strange injection of Combine must surely already have corresponding logic when we're using Bind instead? Can we work from that to build the logic for the desuagring here? + builder.Yield(x + n), + let m = 2 in // Names only in scope as CE syntax would imply + builder.Yield(y + m))))), + Some 10), + Some 20) ``` \ No newline at end of file From b5c9e05c389d1e35c2f5c60b1dfbdb699a19b0c9 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 11:08:23 +0100 Subject: [PATCH 126/255] Give an example of using `yield` when `Bind` is not implemented --- scratch/docs/Log.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 6360243944..badb8f27c7 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -965,4 +965,44 @@ builder.Apply( builder.Yield(y + m))))), Some 10), Some 20) -``` \ No newline at end of file +``` + +Another way to think about this: +We're shoe-horning in a way to make it look like we have `Bind` (note the relevance of the name here!!!) +When we only have `apply`. Perhaps our syntax should be around application instead? +Which, to me, would imply a single return (which captures the thing being applied into). + +That's not the end of the world, since if, for example, you want to `yield` (i.e. pick the first computation that's in the `Some` case, in this builder example) you could write something like: + +```fsharp +let f x y z = + (((x + y) * z) + 3) % 11 + +let xOpt = Some 10 +let yOpt = Some 20 +let zOpt = Some 30 + +option { + yield + option { + let! x = xOpt + and! y = yOpt + and! z = zOpt + return f x y z + } + + yield + option { + let! x = xOpt // We already checked whether `xOpt` was `None` or not above. Why do we have to do it again? Because we haven't defined `Bind`, which is precisely what we need in order to unpack it once and bind that value to a name! + and! y = yOpt + return x * y + } + + yield + option { + let! y = yOpt + and! z = zOpt + return [ y ; y ; z ] + } +} + ``` \ No newline at end of file From 1666c0a1fd6c624284e0d8ee565ef91cb5aeb2f2 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 11:13:35 +0100 Subject: [PATCH 127/255] Add some more comments around new `Apply` syntax --- scratch/docs/Log.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index badb8f27c7..fa97f6fcde 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -995,14 +995,17 @@ option { option { let! x = xOpt // We already checked whether `xOpt` was `None` or not above. Why do we have to do it again? Because we haven't defined `Bind`, which is precisely what we need in order to unpack it once and bind that value to a name! and! y = yOpt - return x * y + // TODO Should we allow `let foo = bar` here? We could translate it so that it's after the `return`, but that seems a bit magical to me. `Apply` is inherently constrained, so why try to act as if it is not? + return + let a = 4 // Fine to put things inside the function passed to `Apply` + a + (x * y) } yield option { let! y = yOpt and! z = zOpt - return [ y ; y ; z ] + return y + z } } ``` \ No newline at end of file From d40158813cfee4364d06901f30fc0ae5b967c6a4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 11:27:22 +0100 Subject: [PATCH 128/255] Add note on syntax for calling a "wrapped" function --- scratch/docs/Log.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index fa97f6fcde..09ded2eaa8 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -1008,4 +1008,14 @@ option { return y + z } } - ``` \ No newline at end of file + ``` + + Also, do we want a convenient way to call an `F<'a ->'b>` which is nicer than: + ```fsharp +option { + let! f = fOpt // fOpt : ('a -> 'b) option + and! x = xOpt // xOpt : 'a option + return f x +} + ``` + ...probably not? That seems nice enough to me. \ No newline at end of file From cbc0f80f3d26e7c0c67b9ad3fcb68a5aa3af338f Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 12:43:34 +0100 Subject: [PATCH 129/255] Snapshot before separating applies from lambdas --- src/fsharp/TypeChecker.fs | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 018e987de9..cb6757efe4 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8041,28 +8041,28 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) - // 'let! pat1 = expr1 and! pat2 = expr2 in exprBody' --> + // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> // build.Apply( // build.Apply( // build.Return( // (function _arg1 -> match _arg1 with pat1 -> // (function _arg2 -> match _arg2 with pat2 -> - // exprBody + // expr3 // ) // ) // ), expr1) // ), expr2) - | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, bodyComp) -> // TODO Handle use! / anduse! + | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, (SynExpr.YieldOrReturn((isYield, isReturn), returnExpr, returnRange) as bodyComp)) when isYield = false -> // TODO Handle use! / anduse! printfn "type checking a let! ... and! ..." // TODO Remove // Reads in bindings [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] - // creating a lambda for each one, and stacking up the calls to apply to hook up once - // all of the lambdas have been created. + // creating a lambda for each one just inside the call to Return, and stacking up the + // calls to Apply to hook up once all of the lambdas have been created. // Note how we work from the inner expression outward, meaning be create the lambda for the last // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to // 'Apply' correspond to their lambda. - let rec desugar (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) (pendingApplies : (SynExpr -> SynExpr) list) = + let rec desugar (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) (pendingApplies : SynExpr -> SynExpr) : (SynExpr -> SynExpr) * SynExpr = match bindings with | (spBind, _, isFromSource, pat, rhsExpr, _) :: remainingBindings -> @@ -8075,28 +8075,15 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr let newPendingApplies = - (fun consumeExpr -> mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) :: pendingApplies + (fun consumeExpr -> mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) >> pendingApplies let newInnerComp = - let matchLambda = - SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, innerComp, innerRange, SequencePointAtTarget)], spBind, innerRange) - - // If this is the last matchLambda, we need to insert a return so that Apply takes an - // F<'a -> 'b> as opposed to just an 'a -> 'b - match remainingBindings with - | _::_ -> - matchLambda - | [] -> - mkSynCall "Return" bindRange [matchLambda] // TODO Fallback to 'Yield' if 'Return' isn't defined? + SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, innerComp, innerRange, SequencePointAtTarget)], spBind, innerRange) desugar newInnerComp remainingBindings newPendingApplies | [] -> - match pendingApplies with - | ap :: remainingApplies -> - desugar (ap innerComp) [] remainingApplies - | [] -> - innerComp + pendingApplies, SynExpr.YieldOrReturn((isYield,isReturn),innerComp,returnRange) let bindingsBottomToTop = let letBinding = @@ -8115,7 +8102,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv ) varSpace // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - Some (trans true q varSpace bodyComp (fun holeFill -> translatedCtxt (desugar holeFill bindingsBottomToTop []))) + Some (trans true q varSpace returnExpr (fun holeFill -> translatedCtxt (desugar holeFill bindingsBottomToTop id))) + + | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> // TODO Handle use! / anduse! + error(new Exception("let! ... and! ... computation expressions must immediately return a value")) // TODO Make more helpful // 'use! pat = e1 and! pat = e2 in e3' --> TODO //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) From d48cde9a48a989b576dc1dde74ff7507f4a3f3fa Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 13:13:19 +0100 Subject: [PATCH 130/255] Separate applies from lambdas --- src/fsharp/TypeChecker.fs | 51 +++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index cb6757efe4..a22a8c2ca8 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8052,23 +8052,39 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // ) // ), expr1) // ), expr2) - | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, (SynExpr.YieldOrReturn((isYield, isReturn), returnExpr, returnRange) as bodyComp)) when isYield = false -> // TODO Handle use! / anduse! + | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, isReturn), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! printfn "type checking a let! ... and! ..." // TODO Remove - // Reads in bindings [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] - // creating a lambda for each one just inside the call to Return, and stacking up the - // calls to Apply to hook up once all of the lambdas have been created. - // Note how we work from the inner expression outward, meaning be create the lambda for the last - // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to - // 'Apply' correspond to their lambda. - let rec desugar (innerComp : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) (pendingApplies : SynExpr -> SynExpr) : (SynExpr -> SynExpr) * SynExpr = + // Here we wrap the expression inside the return into a nested lambda for each binding + // introduced by let! ... and! ... + // This allows us to do any of the pattern matching that appears on the LHS of a binding, + // and make a function such that Apply can be called + let rec constructPure (wrappedReturnExpr : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = match bindings with - | (spBind, _, isFromSource, pat, rhsExpr, _) :: remainingBindings -> + | (spBind, _, _, pat, rhsExpr, _) :: remainingBindings -> + let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range + if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) + let innerRange = returnExpr.Range + if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) + then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) + + let newInnerComp = + SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, wrappedReturnExpr, innerRange, SequencePointAtTarget)], spBind, innerRange) + + constructPure newInnerComp remainingBindings + + | [] -> + SynExpr.YieldOrReturn((isYield,isReturn),wrappedReturnExpr,returnRange) + + // Here we construct a call to Apply for each binding introduced by the let! ... and! ... syntax + let rec constructApplies (pendingApplies : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = + + match bindings with + | (spBind, _, isFromSource, _, rhsExpr, _) :: remainingBindings -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) - let innerRange = innerComp.Range if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) @@ -8077,13 +8093,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let newPendingApplies = (fun consumeExpr -> mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) >> pendingApplies - let newInnerComp = - SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, innerComp, innerRange, SequencePointAtTarget)], spBind, innerRange) - - desugar newInnerComp remainingBindings newPendingApplies + constructApplies newPendingApplies remainingBindings | [] -> - pendingApplies, SynExpr.YieldOrReturn((isYield,isReturn),innerComp,returnRange) + pendingApplies let bindingsBottomToTop = let letBinding = @@ -8101,8 +8114,14 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv vspecs, envinner) ) varSpace + // Note how we work from the inner expression outward, meaning be create the lambda for the last + // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to + // 'Apply' correspond to their lambda. + // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - Some (trans true q varSpace returnExpr (fun holeFill -> translatedCtxt (desugar holeFill bindingsBottomToTop id))) + Some (trans true q varSpace returnExpr (fun holeFill -> + let wrapInCallToApplyForEachBinding = constructApplies id bindingsBottomToTop + wrapInCallToApplyForEachBinding (translatedCtxt (constructPure holeFill bindingsBottomToTop)))) | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> // TODO Handle use! / anduse! error(new Exception("let! ... and! ... computation expressions must immediately return a value")) // TODO Make more helpful From 244aec23df2b1f0870ce2bf435cfe4c03ae94987 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 13:58:59 +0100 Subject: [PATCH 131/255] Keep Return along side its expr --- src/fsharp/TypeChecker.fs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index a22a8c2ca8..a0ba83985c 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7991,7 +7991,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // error case error(Error(FSComp.SR.tcCustomOperationMayNotBeUsedInConjunctionWithNonSimpleLetBindings(), mQueryOp))) - Some (trans true q varSpace innerComp (fun holeFill -> translatedCtxt (SynExpr.LetOrUse (isRec, false, binds, holeFill, m)))) // 'use x = expr in expr' @@ -8056,11 +8055,9 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv printfn "type checking a let! ... and! ..." // TODO Remove - // Here we wrap the expression inside the return into a nested lambda for each binding - // introduced by let! ... and! ... - // This allows us to do any of the pattern matching that appears on the LHS of a binding, - // and make a function such that Apply can be called - let rec constructPure (wrappedReturnExpr : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = + // Here we construct lambdas to do any of the pattern matching that + // appears on the LHS of a binding + let rec constructPure (pendingLambdas : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = match bindings with | (spBind, _, _, pat, rhsExpr, _) :: remainingBindings -> @@ -8070,13 +8067,13 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) - let newInnerComp = - SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, wrappedReturnExpr, innerRange, SequencePointAtTarget)], spBind, innerRange) + let newPendingLambdas inner = + SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, inner, innerRange, SequencePointAtTarget)], spBind, innerRange) - constructPure newInnerComp remainingBindings + constructPure newPendingLambdas remainingBindings | [] -> - SynExpr.YieldOrReturn((isYield,isReturn),wrappedReturnExpr,returnRange) + pendingLambdas // Here we construct a call to Apply for each binding introduced by the let! ... and! ... syntax let rec constructApplies (pendingApplies : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = @@ -8115,13 +8112,18 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv ) varSpace // Note how we work from the inner expression outward, meaning be create the lambda for the last - // 'and!' _first_, and then create the calls to 'Apply' in the opposite order so that the calls to + // 'and!' _first_, and create the calls to 'Apply' in the opposite order so that the calls to // 'Apply' correspond to their lambda. // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - Some (trans true q varSpace returnExpr (fun holeFill -> - let wrapInCallToApplyForEachBinding = constructApplies id bindingsBottomToTop - wrapInCallToApplyForEachBinding (translatedCtxt (constructPure holeFill bindingsBottomToTop)))) + + let newReturn = + let returnExprWrappedInLambdas = + constructPure id bindingsBottomToTop returnExpr + SynExpr.YieldOrReturn((isYield, isReturn), returnExprWrappedInLambdas, returnRange) + + Some (trans true q varSpace newReturn (fun holeFill -> + constructApplies id bindingsBottomToTop (translatedCtxt holeFill))) | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> // TODO Handle use! / anduse! error(new Exception("let! ... and! ... computation expressions must immediately return a value")) // TODO Make more helpful From 9da6da919e6eebc84df6a2eb31dacbf3576e817d Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 14:11:33 +0100 Subject: [PATCH 132/255] Change creation of match lambdas to happen immediately --- src/fsharp/TypeChecker.fs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index a0ba83985c..1f7ca46cb4 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8055,9 +8055,9 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv printfn "type checking a let! ... and! ..." // TODO Remove - // Here we construct lambdas to do any of the pattern matching that + // Here we construct match lambdas to do any of the pattern matching that // appears on the LHS of a binding - let rec constructPure (pendingLambdas : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = + let rec constructMatchLambdas (acc : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = match bindings with | (spBind, _, _, pat, rhsExpr, _) :: remainingBindings -> @@ -8067,13 +8067,13 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) - let newPendingLambdas inner = - SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, inner, innerRange, SequencePointAtTarget)], spBind, innerRange) + let newAcc = + SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) - constructPure newPendingLambdas remainingBindings + constructPure newAcc remainingBindings | [] -> - pendingLambdas + acc // Here we construct a call to Apply for each binding introduced by the let! ... and! ... syntax let rec constructApplies (pendingApplies : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = @@ -8119,7 +8119,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let newReturn = let returnExprWrappedInLambdas = - constructPure id bindingsBottomToTop returnExpr + constructMatchLambdas returnExpr bindingsBottomToTop SynExpr.YieldOrReturn((isYield, isReturn), returnExprWrappedInLambdas, returnRange) Some (trans true q varSpace newReturn (fun holeFill -> From 7dc29166ddecbd4c93c0612c9e22baedd6013096 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 14:21:56 +0100 Subject: [PATCH 133/255] Clean up creation of match lambdas --- src/fsharp/TypeChecker.fs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 1f7ca46cb4..7a485be2b4 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8060,17 +8060,14 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let rec constructMatchLambdas (acc : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = match bindings with - | (spBind, _, _, pat, rhsExpr, _) :: remainingBindings -> - let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range - if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) + | (spBind, _, _, pat, _, _) :: remainingBindings -> + let innerRange = returnExpr.Range - if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) - then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) let newAcc = SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) - constructPure newAcc remainingBindings + constructMatchLambdas newAcc remainingBindings | [] -> acc From f3af2775e7e93bfba67d3a7302733f62f7555a5a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 14:39:26 +0100 Subject: [PATCH 134/255] Make desugaring work for SimpleBuilder.fsx --- scratch/data/SimpleBuilder.fsx | 8 +++++--- src/fsharp/TypeChecker.fs | 29 ++++++++++------------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index d5c6efc2cc..cc51beebb7 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -28,19 +28,21 @@ type OptionalBuilder = let opt = OptionalBuilder() +let fOpt = Some (sprintf "f (x = %d) (y = %s) (z = %f)") let xOpt = Some 1 let yOpt = Some "A" let zOpt = Some 3.0 let foo = opt { - let! x = xOpt + let! f = fOpt + and! x = xOpt and! y = yOpt and! z = zOpt - return sprintf "x = %d, y = %s, z = %f" x y z + return f x y z } -printfn "foo = %+A" foo +printfn "Result: \"%+A\"" foo (* let foo' = diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 7a485be2b4..69558618a1 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8055,23 +8055,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv printfn "type checking a let! ... and! ..." // TODO Remove - // Here we construct match lambdas to do any of the pattern matching that - // appears on the LHS of a binding - let rec constructMatchLambdas (acc : SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = - - match bindings with - | (spBind, _, _, pat, _, _) :: remainingBindings -> - - let innerRange = returnExpr.Range - - let newAcc = - SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) - - constructMatchLambdas newAcc remainingBindings - - | [] -> - acc - // Here we construct a call to Apply for each binding introduced by the let! ... and! ... syntax let rec constructApplies (pendingApplies : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = @@ -8114,9 +8097,17 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? + + // We construct match lambdas to do any of the pattern matching that + // appears on the LHS of a binding + let returnExprWrappedInLambdas = + bindingsBottomToTop + |> List.fold (fun acc (spBind, _, _, pat, _, _) -> + let innerRange = returnExpr.Range + SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) + ) returnExpr + let newReturn = - let returnExprWrappedInLambdas = - constructMatchLambdas returnExpr bindingsBottomToTop SynExpr.YieldOrReturn((isYield, isReturn), returnExprWrappedInLambdas, returnRange) Some (trans true q varSpace newReturn (fun holeFill -> From 7ebe676c90060c7aa5ec78756708b318ff3a2764 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 14:45:48 +0100 Subject: [PATCH 135/255] Make SimpleBuilder.fsx show off None code path and pattern maching on LHS of bindings --- scratch/data/SimpleBuilder.fsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index cc51beebb7..27d1d83eb4 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -42,7 +42,29 @@ let foo = return f x y z } -printfn "Result: \"%+A\"" foo +printfn "foo: \"%+A\"" foo + +let bar = + opt { + let! x = None + and! y = Some 1 + and! z = Some 2 + return x + y + z + 1 + } + +printfn "bar: %+A" bar + +type 'a SingleCaseDu = SingleCaseDu of 'a + +let baz = + opt { + let! x = Some 4 + and! (SingleCaseDu y) = Some (SingleCaseDu 30) + and! (z,_) = Some (200, "whatever") + return x + y + z + 1000 + } + +printfn "baz: %+A" baz (* let foo' = From 190674c3014832c65054d9e6651cc047468f5e76 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 14:48:03 +0100 Subject: [PATCH 136/255] Remove bind from builder in SimpleBuilder.fsx to prove it is not needed --- scratch/data/SimpleBuilder.fsx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 27d1d83eb4..9767afeb4c 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -1,31 +1,14 @@ -// TODO Work towards defining an option applicative builder here [] type OptionalBuilder = - member __.Bind(opt, f) = - match opt with - | Some x -> f x - | None -> None - - // TODO Make this actually get called when `let! ... and! ...` syntax is used (and automagically if RHSs allow it?) member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = match fOpt, xOpt with | Some f, Some x -> Some <| f x | _ -> None - // TODO Not needed, but for maximum efficiency, we want to use this if it is defined - member __.Map(f : 'a -> 'b, xOpt : 'a option) : 'b option = - match xOpt with - | Some x -> Some <| f x - | None -> None - member __.Return(x) = Some x - - //member __.Zero () = // TODO: Remove - just here to explore current compiler limitations - // None - let opt = OptionalBuilder() let fOpt = Some (sprintf "f (x = %d) (y = %s) (z = %f)") From 490bb933df8b9f4e9380a277b92c4df301de7d57 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 14:54:11 +0100 Subject: [PATCH 137/255] Add example let binding inside a return in SimpleBuilder.fsx --- scratch/data/SimpleBuilder.fsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 9767afeb4c..fa192b742b 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -4,11 +4,12 @@ type OptionalBuilder = member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = match fOpt, xOpt with - | Some f, Some x -> Some <| f x + | Some f, Some x -> Some (f x) | _ -> None member __.Return(x) = Some x + let opt = OptionalBuilder() let fOpt = Some (sprintf "f (x = %d) (y = %s) (z = %f)") @@ -44,7 +45,7 @@ let baz = let! x = Some 4 and! (SingleCaseDu y) = Some (SingleCaseDu 30) and! (z,_) = Some (200, "whatever") - return x + y + z + 1000 + return (let w = 50000 in x + y + z + 1000) } printfn "baz: %+A" baz From 3de3c3dcdd133f7a988464bd1001b9ffc7daa80b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 15:04:20 +0100 Subject: [PATCH 138/255] Make let-bound value in the return actually get used in example CE --- scratch/data/SimpleBuilder.fsx | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index fa192b742b..f28f8060f3 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -42,20 +42,10 @@ type 'a SingleCaseDu = SingleCaseDu of 'a let baz = opt { - let! x = Some 4 - and! (SingleCaseDu y) = Some (SingleCaseDu 30) - and! (z,_) = Some (200, "whatever") - return (let w = 50000 in x + y + z + 1000) + let! x = Some 5 + and! (SingleCaseDu y) = Some (SingleCaseDu 40) + and! (z,_) = Some (300, "whatever") + return (let w = 10000 in w + x + y + z + 2000) } printfn "baz: %+A" baz - -(* -let foo' = - opt { - let! x' = x - and! y' = y - and! z' = z - return sprintf "x = %d, y = %s, z = %f" x y z - } -*) From 9a303d52c8059a04e9565212e81627da4f54e03a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 15:04:33 +0100 Subject: [PATCH 139/255] Remove hacky debug printing --- src/fsharp/TypeChecker.fs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 69558618a1..08c7d83e7b 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8053,8 +8053,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // ), expr2) | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, isReturn), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! - printfn "type checking a let! ... and! ..." // TODO Remove - // Here we construct a call to Apply for each binding introduced by the let! ... and! ... syntax let rec constructApplies (pendingApplies : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = From 54a9c378f03f46e3fa89ab6da1952a9783b5d4ce Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 15:14:11 +0100 Subject: [PATCH 140/255] Update Log.md with progress and next steps --- scratch/docs/Log.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 09ded2eaa8..e9b70d1d6a 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -997,8 +997,7 @@ option { and! y = yOpt // TODO Should we allow `let foo = bar` here? We could translate it so that it's after the `return`, but that seems a bit magical to me. `Apply` is inherently constrained, so why try to act as if it is not? return - let a = 4 // Fine to put things inside the function passed to `Apply` - a + (x * y) + (let a = 4 in a + (x * y)) // Fine to put things inside the function passed to `Apply` } yield @@ -1018,4 +1017,9 @@ option { return f x } ``` - ...probably not? That seems nice enough to me. \ No newline at end of file + ...probably not? That seems nice enough to me. + + Okay, so the above seems to approximately work now. Next steps: + * Look at error messages + * Come up with some more examples of what should / should not work (and encode as tests?) + * Put together an RFC \ No newline at end of file From 12eca83c11c5e9c598e1d1c52d4c5a72aee43ec9 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 15:16:08 +0100 Subject: [PATCH 141/255] Add note about considering moving requirement for `return` after `let! ... and! ...` to the parser --- scratch/docs/Log.md | 1 + 1 file changed, 1 insertion(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index e9b70d1d6a..1ee1c8db05 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -1022,4 +1022,5 @@ option { Okay, so the above seems to approximately work now. Next steps: * Look at error messages * Come up with some more examples of what should / should not work (and encode as tests?) + * Justify requiring the trailing `Return` after a `let! ... and! ...` in the tye checker rather than syntax, or move it to the parser * Put together an RFC \ No newline at end of file From 66f905dc3c43fb23bde3261b4cd0131ba99f7e01 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 15:28:03 +0100 Subject: [PATCH 142/255] Add `use! ... anduse! ...` TODO to list --- scratch/docs/Log.md | 1 + 1 file changed, 1 insertion(+) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 1ee1c8db05..b8c738e697 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -1022,5 +1022,6 @@ option { Okay, so the above seems to approximately work now. Next steps: * Look at error messages * Come up with some more examples of what should / should not work (and encode as tests?) + * Look into supporting `use! ... anduse! ...` * Justify requiring the trailing `Return` after a `let! ... and! ...` in the tye checker rather than syntax, or move it to the parser * Put together an RFC \ No newline at end of file From 84363ce603b948099460954c96fadbd76eb4e312 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 19:08:22 +0100 Subject: [PATCH 143/255] Add first cut for a desugaring of `use! ... anduse! ...` --- scratch/docs/Log.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index b8c738e697..d1f6413262 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -1024,4 +1024,37 @@ option { * Come up with some more examples of what should / should not work (and encode as tests?) * Look into supporting `use! ... anduse! ...` * Justify requiring the trailing `Return` after a `let! ... and! ...` in the tye checker rather than syntax, or move it to the parser - * Put together an RFC \ No newline at end of file + * Put together an RFC + + Considering `Using` (I don't have a usecase for this, but I don't really want to leave this out else the feature seems weirdly incomplete): +```fsharp +disposableFoo { + use! x = xDisFoo + and! y = yDisFoo // N.B. The goal is that any let! or and! binding can we replaced with a use! or anduse! binding + anduse! z = zDisFoo + return x + y + } + + // desugaring to: + +builder.Apply( + builder.Apply( + builder.Apply( + builder.Return( + (fun x -> + builder.Using(x, fun x -> + (fun y -> // This corresponds to the and!, so doesn't generate a call to using + (fun z -> + builder.Using(z, fun z -> + x + y + z + ) + ) + ) + ) + ) + ), + xDisFoo), + yDisFoo), + zDisFoo) +``` +I've put this together by basically mapping what was done for monads onto applicatives. I've not really thought this through in terms of whether it actually solves a problem someone could have. \ No newline at end of file From 29a409dccd2d7e6349ff5f9a9c665b8c6cf84de0 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 18 Sep 2018 19:12:54 +0100 Subject: [PATCH 144/255] Add like to scott w's trace builder example --- scratch/docs/Log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index d1f6413262..6c0749065a 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -1057,4 +1057,4 @@ builder.Apply( yDisFoo), zDisFoo) ``` -I've put this together by basically mapping what was done for monads onto applicatives. I've not really thought this through in terms of whether it actually solves a problem someone could have. \ No newline at end of file +I've put this together by basically mapping what was done for monads onto applicatives. I've not really thought this through in terms of whether it actually solves a problem someone could have. Could just work off ScottW's `trace` example: https://fsharpforfunandprofit.com/posts/computation-expressions-builder-part6/ for now \ No newline at end of file From 3209f9861cf4b187bdcf89a562145faa320483e0 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 10:47:24 +0100 Subject: [PATCH 145/255] Enhance SimpleBuilder.fsx to show type checking bug --- scratch/data/SimpleBuilder.fsx | 84 ++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index f28f8060f3..4e9a44ee48 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -7,8 +7,18 @@ type OptionalBuilder = | Some f, Some x -> Some (f x) | _ -> None - member __.Return(x) = - Some x + member __.Return(x) = Some x + + member __.Yield(x) = Some x + + member __.Delay(f) = f + + member __.Zero() = Some () + + member __.Combine(xOpt, yOpt) = + match xOpt with + | Some _ -> xOpt + | None -> yOpt let opt = OptionalBuilder() @@ -17,7 +27,7 @@ let xOpt = Some 1 let yOpt = Some "A" let zOpt = Some 3.0 -let foo = +let foo : string option = opt { let! f = fOpt and! x = xOpt @@ -28,7 +38,7 @@ let foo = printfn "foo: \"%+A\"" foo -let bar = +let bar : int option = opt { let! x = None and! y = Some 1 @@ -40,7 +50,7 @@ printfn "bar: %+A" bar type 'a SingleCaseDu = SingleCaseDu of 'a -let baz = +let baz : int option = opt { let! x = Some 5 and! (SingleCaseDu y) = Some (SingleCaseDu 40) @@ -49,3 +59,67 @@ let baz = } printfn "baz: %+A" baz + +let quux : int option = + opt { + yield bar + yield baz + } + +printfn "quux: %+A" quux + +(* +type TraceBuilder() = + + member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = + match fOpt, xOpt with + | Some f, Some x -> + printfn "Applying %+A to %+A" f x + Some (f x) + | _ -> + printfn "Applying with None. Exiting." + None + + member __.Return(x) = Some x + + member __.Yield(x) = Some x + + member __.Zero() = + printfn "Zero" + Some () + + member __.Delay(f) = + printfn "Delay" + f + + member __.Combine(xOpt, yOpt) = + let res = + match xOpt with + | Some _ -> + xOpt + | None -> + yOpt + printfn "Combining %+A with %+A to get %+A" xOpt yOpt res + res + + member __.ReturnFrom(x) = x + + member __.TryWith(body, handler) = + try __.ReturnFrom(body()) + with e -> handler e + + member __.TryFinally(body, compensation) = + try __.ReturnFrom(body()) + finally compensation() + + member __.Using(disposable:#System.IDisposable, body) = + let body' = fun () -> body disposable + __.TryFinally(body', fun () -> + match disposable with + | null -> + printfn "Not disposing: Value is null" + () + | disp -> + printfn "Disposing %O" disp + disp.Dispose()) +*) \ No newline at end of file From d2a795a7f1b2ea1ea466f1006c156513186f571f Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 11:07:11 +0100 Subject: [PATCH 146/255] Add more explicit typings --- scratch/data/SimpleBuilder.fsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 4e9a44ee48..b65544cd9b 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -7,15 +7,17 @@ type OptionalBuilder = | Some f, Some x -> Some (f x) | _ -> None - member __.Return(x) = Some x + member __.Return(x : 'a) : 'a option = Some x - member __.Yield(x) = Some x + // Below is only needed to make yield keyword work - member __.Delay(f) = f + member __.Yield(x : 'a) : 'a option = Some x - member __.Zero() = Some () + member __.Delay(f : 'a) : 'a = f - member __.Combine(xOpt, yOpt) = + member __.Zero() : unit option = Some () + + member __.Combine(xOpt : 'a option, yOpt : 'a option) : 'a option = match xOpt with | Some _ -> xOpt | None -> yOpt From a5978e362eb418e0ce7b99e02975463e614b460b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 12:02:11 +0100 Subject: [PATCH 147/255] Move Apply calls inside translation --- src/fsharp/TypeChecker.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 08c7d83e7b..8830e541da 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8109,7 +8109,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv SynExpr.YieldOrReturn((isYield, isReturn), returnExprWrappedInLambdas, returnRange) Some (trans true q varSpace newReturn (fun holeFill -> - constructApplies id bindingsBottomToTop (translatedCtxt holeFill))) + translatedCtxt (constructApplies id bindingsBottomToTop holeFill))) | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> // TODO Handle use! / anduse! error(new Exception("let! ... and! ... computation expressions must immediately return a value")) // TODO Make more helpful From ff113959c7e99e0b5f9c435d95e4bafb44bebf7c Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 12:42:05 +0100 Subject: [PATCH 148/255] Move everything but expr inside return into translation. Broken because the expr isn't a cexpr --- src/fsharp/TypeChecker.fs | 41 ++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 8830e541da..cf5ed3d2c0 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8053,8 +8053,13 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // ), expr2) | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, isReturn), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! + let bindingsBottomToTop = + let letBinding = + (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) + List.rev (letBinding :: andBangBindings) // [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] + // Here we construct a call to Apply for each binding introduced by the let! ... and! ... syntax - let rec constructApplies (pendingApplies : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = + let rec constructAppliesForBindings (pendingApplies : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = match bindings with | (spBind, _, isFromSource, _, rhsExpr, _) :: remainingBindings -> @@ -8068,15 +8073,12 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let newPendingApplies = (fun consumeExpr -> mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) >> pendingApplies - constructApplies newPendingApplies remainingBindings + constructAppliesForBindings newPendingApplies remainingBindings | [] -> pendingApplies - let bindingsBottomToTop = - let letBinding = - (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) - List.rev (letBinding :: andBangBindings) // [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] + let wrapInCallsToApply = constructAppliesForBindings id bindingsBottomToTop // Add the variables to the query variable space, on demand let varSpace = @@ -8096,20 +8098,23 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - // We construct match lambdas to do any of the pattern matching that - // appears on the LHS of a binding - let returnExprWrappedInLambdas = - bindingsBottomToTop - |> List.fold (fun acc (spBind, _, _, pat, _, _) -> + Some (trans true q varSpace returnExpr (fun holeFill -> + + // We construct match lambdas to do any of the pattern matching that + // appears on the LHS of a binding + (holeFill, bindingsBottomToTop) + ||> List.fold (fun acc (spBind, _, _, pat, _, _) -> let innerRange = returnExpr.Range SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) - ) returnExpr - - let newReturn = - SynExpr.YieldOrReturn((isYield, isReturn), returnExprWrappedInLambdas, returnRange) - - Some (trans true q varSpace newReturn (fun holeFill -> - translatedCtxt (constructApplies id bindingsBottomToTop holeFill))) + ) + // We take the nested lambdas and put the return back, _outside_ them + // to give an F<'a -> 'b -> 'c -> ...> as Apply expects + |> (fun f -> SynExpr.YieldOrReturn((isYield, isReturn), f, returnRange)) + // We wrap the return in a call to Apply for each lambda + |> wrapInCallsToApply + // We nest the result inside the parent expression + |> translatedCtxt) + ) | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> // TODO Handle use! / anduse! error(new Exception("let! ... and! ... computation expressions must immediately return a value")) // TODO Make more helpful From 3b4da1db6f674edba8290d1403fdc3a699908bd3 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 13:31:11 +0100 Subject: [PATCH 149/255] Move Return back outside this round of translation - I think everything inluding the returnExpr need to be treated in one --- src/fsharp/TypeChecker.fs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index cf5ed3d2c0..3dcfece0b2 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8097,12 +8097,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - - Some (trans true q varSpace returnExpr (fun holeFill -> - + let newReturn = // We construct match lambdas to do any of the pattern matching that // appears on the LHS of a binding - (holeFill, bindingsBottomToTop) + (returnExpr, bindingsBottomToTop) ||> List.fold (fun acc (spBind, _, _, pat, _, _) -> let innerRange = returnExpr.Range SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) @@ -8110,6 +8108,9 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // We take the nested lambdas and put the return back, _outside_ them // to give an F<'a -> 'b -> 'c -> ...> as Apply expects |> (fun f -> SynExpr.YieldOrReturn((isYield, isReturn), f, returnRange)) + + Some (trans true q varSpace newReturn (fun holeFill -> + holeFill // We wrap the return in a call to Apply for each lambda |> wrapInCallsToApply // We nest the result inside the parent expression From 7dae6d24eeaf6dfe3361cc373204d7dc49a01593 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 13:36:00 +0100 Subject: [PATCH 150/255] Move Return back inside this round of translation, but replace CE keyword with call to builder method --- src/fsharp/TypeChecker.fs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 3dcfece0b2..651ef7d921 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8097,20 +8097,19 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - let newReturn = + + Some (trans true q varSpace returnExpr (fun holeFill -> + // We construct match lambdas to do any of the pattern matching that // appears on the LHS of a binding - (returnExpr, bindingsBottomToTop) + (holeFill, bindingsBottomToTop) ||> List.fold (fun acc (spBind, _, _, pat, _, _) -> let innerRange = returnExpr.Range SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) ) // We take the nested lambdas and put the return back, _outside_ them // to give an F<'a -> 'b -> 'c -> ...> as Apply expects - |> (fun f -> SynExpr.YieldOrReturn((isYield, isReturn), f, returnRange)) - - Some (trans true q varSpace newReturn (fun holeFill -> - holeFill + |> (fun f -> mkSynCall "Return" returnRange [f]) // We wrap the return in a call to Apply for each lambda |> wrapInCallsToApply // We nest the result inside the parent expression From 875b900c5b404780a65b2fb87ae7c76f3e47acb7 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 13:36:41 +0100 Subject: [PATCH 151/255] Fix naming of unused value --- src/fsharp/TypeChecker.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 651ef7d921..468b8ec44d 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8051,7 +8051,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // ) // ), expr1) // ), expr2) - | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, isReturn), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! + | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, _), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! let bindingsBottomToTop = let letBinding = From c06c1d7e82b8ea1ebcbc1591cbd70a7c1e9480d8 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 14:07:51 +0100 Subject: [PATCH 152/255] Add missing check for a Return impl. on the builder --- src/fsharp/TypeChecker.fs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 468b8ec44d..de0a708a5e 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8099,7 +8099,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some (trans true q varSpace returnExpr (fun holeFill -> - // We construct match lambdas to do any of the pattern matching that // appears on the LHS of a binding (holeFill, bindingsBottomToTop) @@ -8109,7 +8108,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv ) // We take the nested lambdas and put the return back, _outside_ them // to give an F<'a -> 'b -> 'c -> ...> as Apply expects - |> (fun f -> mkSynCall "Return" returnRange [f]) + |> (fun f -> + if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env returnRange ad "Return" builderTy) + then error(Error(FSComp.SR.tcRequireBuilderMethod("Return"), returnRange)) + mkSynCall "Return" returnRange [f]) // We wrap the return in a call to Apply for each lambda |> wrapInCallsToApply // We nest the result inside the parent expression From 6a7e80d0a8871a10a1ca260330ec56dc7528aea4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 14:43:22 +0100 Subject: [PATCH 153/255] Add what I think should be the desugaring of the most simple example, so I can compare to the desugaring in the compiler --- scratch/data/SimpleBuilder.fsx | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index b65544cd9b..328610429d 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -29,13 +29,37 @@ let xOpt = Some 1 let yOpt = Some "A" let zOpt = Some 3.0 +let superSimpleExampleDesugared : bool option = + opt.Apply( + opt.Apply( + opt.Return( + (fun x -> + (fun y -> + x || y + ) + ) + ), + Some true), + Some false) + +printfn "Super simple example desugared: \"%+A\"" superSimpleExampleDesugared + +let superSimpleExample : bool option = + opt { + let! x = Some true + and! y = Some false + return x || y + } + +printfn "Super simple example: \"%+A\"" superSimpleExample + let foo : string option = opt { let! f = fOpt and! x = xOpt and! y = yOpt and! z = zOpt - return f x y z + return (f x y z) } printfn "foo: \"%+A\"" foo From 8f2c5dc84862bf46f14e70f152aed982705063b8 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 15:08:57 +0100 Subject: [PATCH 154/255] Set up type checker for more detailed debugging of desugarings --- src/fsharp/TypeChecker.fs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index de0a708a5e..b5cbba1a45 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8062,7 +8062,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let rec constructAppliesForBindings (pendingApplies : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = match bindings with - | (spBind, _, isFromSource, _, rhsExpr, _) :: remainingBindings -> + | (spBind, _, isFromSource, pat, rhsExpr, _) :: remainingBindings -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) @@ -8071,7 +8071,9 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr let newPendingApplies = - (fun consumeExpr -> mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) >> pendingApplies + (fun consumeExpr -> + printfn "Creating synthetic Apply call for pattern %+A" pat // TODO Delete + mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) >> pendingApplies constructAppliesForBindings newPendingApplies remainingBindings @@ -8081,7 +8083,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let wrapInCallsToApply = constructAppliesForBindings id bindingsBottomToTop // Add the variables to the query variable space, on demand - let varSpace = + (*let varSpace = // TODO Make sure no LHS names clash, and no RHS mentions a name from another LHS bindingsBottomToTop |> List.fold (fun acc (_,_,_,pat,_,_) -> @@ -8090,6 +8092,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) vspecs, envinner) ) varSpace + *) // Note how we work from the inner expression outward, meaning be create the lambda for the last // 'and!' _first_, and create the calls to 'Apply' in the opposite order so that the calls to @@ -8097,26 +8100,26 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - - Some (trans true q varSpace returnExpr (fun holeFill -> + let desugared = // We construct match lambdas to do any of the pattern matching that // appears on the LHS of a binding - (holeFill, bindingsBottomToTop) - ||> List.fold (fun acc (spBind, _, _, pat, _, _) -> + bindingsBottomToTop + |> List.fold (fun acc (spBind, _, _, pat, _, _) -> let innerRange = returnExpr.Range + printfn "Creating synthetic match lambda for pattern %+A" pat // TODO Delete SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) - ) + ) returnExpr // We take the nested lambdas and put the return back, _outside_ them - // to give an F<'a -> 'b -> 'c -> ...> as Apply expects + // to give an f : F<'a -> 'b -> 'c -> ...> as a series of Apply calls expects |> (fun f -> if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env returnRange ad "Return" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Return"), returnRange)) + printfn "Creating synthetic Return call" // TODO Delete mkSynCall "Return" returnRange [f]) // We wrap the return in a call to Apply for each lambda |> wrapInCallsToApply - // We nest the result inside the parent expression - |> translatedCtxt) - ) + + Some (translatedCtxt desugared) | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> // TODO Handle use! / anduse! error(new Exception("let! ... and! ... computation expressions must immediately return a value")) // TODO Make more helpful @@ -8218,7 +8221,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let delayedExpr = match TryFindIntrinsicOrExtensionMethInfo cenv env mBuilderVal ad "Delay" builderTy with | [] -> basicSynExpr - | _ -> mkSynCall "Delay" mBuilderVal [(mkSynDelay2 basicSynExpr)] + | _ -> mkSynCall "Delay" mBuilderVal [(mkSynDelay2 basicSynExpr)] // TODO If Delay is defined, it is called. Does this work? let quotedSynExpr = @@ -8230,7 +8233,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let runExpr = match TryFindIntrinsicOrExtensionMethInfo cenv env mBuilderVal ad "Run" builderTy with | [] -> quotedSynExpr - | _ -> mkSynCall "Run" mBuilderVal [quotedSynExpr] + | _ -> mkSynCall "Run" mBuilderVal [quotedSynExpr] // TODO If Run is defined, it is called. Does this work?call let lambdaExpr = let mBuilderVal = mBuilderVal.MakeSynthetic() @@ -8238,7 +8241,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let env = match comp with - | SynExpr.YieldOrReturn ((true, _), _, _) -> { env with eContextInfo = ContextInfo.YieldInComputationExpression } + | SynExpr.YieldOrReturn ((true, _), _, _) -> { env with eContextInfo = ContextInfo.YieldInComputationExpression } // TODO Do we need our own new context info? | SynExpr.YieldOrReturn ((_, true), _, _) -> { env with eContextInfo = ContextInfo.ReturnInComputationExpression } | _ -> env From 7e566e02fec76b51b9617f1b23408f39e9fa0b42 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 15:14:38 +0100 Subject: [PATCH 155/255] Sketch alternative applicatives example --- scratch/data/SimpleBuilder.fsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 328610429d..d0cf11ea08 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -13,7 +13,7 @@ type OptionalBuilder = member __.Yield(x : 'a) : 'a option = Some x - member __.Delay(f : 'a) : 'a = f + member __.Delay(f : unit -> 'a) : 'a = f () // If you type `Delay : 'a -> 'a` explicitly, it builds but things get weird. Fast. member __.Zero() : unit option = Some () @@ -46,8 +46,8 @@ printfn "Super simple example desugared: \"%+A\"" superSimpleExampleDesugared let superSimpleExample : bool option = opt { - let! x = Some true - and! y = Some false + let! (x : bool) = Some true + and! (y : bool) = Some false return x || y } @@ -88,8 +88,14 @@ printfn "baz: %+A" baz let quux : int option = opt { - yield bar - yield baz + let! x = None + and! (SingleCaseDu y) = Some (SingleCaseDu 40) + and! (z,_) = Some (300, "whatever") + + // Given the definition of Combine in the builder, I'd expect yield to mean "return this value if it is a Some, else try the next one" + yield x + yield y + yield z } printfn "quux: %+A" quux From f75d04346186ba59b53367f60804a5e9f29518f3 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 17:14:28 +0100 Subject: [PATCH 156/255] Show example of an alternative applicative with no Bind in sight! --- scratch/data/SimpleBuilder.fsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index d0cf11ea08..1e32b54a0e 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -11,7 +11,7 @@ type OptionalBuilder = // Below is only needed to make yield keyword work - member __.Yield(x : 'a) : 'a option = Some x + member __.YieldFrom(x : 'a) : 'a = x member __.Delay(f : unit -> 'a) : 'a = f () // If you type `Delay : 'a -> 'a` explicitly, it builds but things get weird. Fast. @@ -88,14 +88,10 @@ printfn "baz: %+A" baz let quux : int option = opt { - let! x = None - and! (SingleCaseDu y) = Some (SingleCaseDu 40) - and! (z,_) = Some (300, "whatever") - - // Given the definition of Combine in the builder, I'd expect yield to mean "return this value if it is a Some, else try the next one" - yield x - yield y - yield z + yield! None + yield! baz + yield! None + yield! bar } printfn "quux: %+A" quux From 9e695f634f32159f14d8cd4e985be4049c8868c2 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 17:16:51 +0100 Subject: [PATCH 157/255] Give example of mixed inline and bound alternative applicatives --- scratch/data/SimpleBuilder.fsx | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 1e32b54a0e..cd472f133a 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -88,10 +88,29 @@ printfn "baz: %+A" baz let quux : int option = opt { - yield! None - yield! baz - yield! None + yield! + opt { + let! x = Some 11 + and! y = None + and! z = Some 2 + return x + y + z + 1 + } + yield! + opt { + let! x = None + and! y = Some 1 + and! z = None + return x + y + z - 10 + } + yield! + opt { + let! x = Some 14 + and! y = Some 1 + and! z = Some 2 + return x + y + z + 1 + } yield! bar + yield! baz } printfn "quux: %+A" quux From 5f29c4726ef542f2f79f558a72b02b30178c77f1 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 17:21:58 +0100 Subject: [PATCH 158/255] Make the quux an example of loads of nested stuff for an alternative applicative --- scratch/data/SimpleBuilder.fsx | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index cd472f133a..248fff5918 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -88,29 +88,39 @@ printfn "baz: %+A" baz let quux : int option = opt { - yield! + let! a = opt { - let! x = Some 11 - and! y = None - and! z = Some 2 - return x + y + z + 1 + yield! + opt { + let! x = Some 11 + and! y = None + and! z = Some 2 + return x + y + z + 1 + } + yield! + opt { + let! x = Some 11 + and! y = Some 100 + and! z = Some 2 + return x + y + z + 1 + } } - yield! + and! b = opt { let! x = None and! y = Some 1 and! z = None return x + y + z - 10 } - yield! + and! c = opt { let! x = Some 14 and! y = Some 1 and! z = Some 2 return x + y + z + 1 } - yield! bar - yield! baz + and! d = baz + return a * b * c * d } printfn "quux: %+A" quux From 3a2fd42728c69c1343ce28c315d7be8077ffe2bb Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 17:28:19 +0100 Subject: [PATCH 159/255] Wrap quux example in comments --- scratch/data/SimpleBuilder.fsx | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 248fff5918..43a13000d4 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -89,38 +89,40 @@ printfn "baz: %+A" baz let quux : int option = opt { let! a = - opt { + opt { // Takes the first yield!'d value that is not None, i.e. takes the second block in this case + // You caan view yield! (in this builder) to mean a prioritied list of values, where the highest priority Some wins yield! opt { let! x = Some 11 and! y = None and! z = Some 2 - return x + y + z + 1 + return x + y + z + 1 // Bails out because y is None } yield! opt { - let! x = Some 11 - and! y = Some 100 + let! x = Some -3 + and! y = Some 1 and! z = Some 2 - return x + y + z + 1 + return x + y + z + 4 // Computes result because all args are Some } } and! b = opt { - let! x = None + let! x = Some 9 and! y = Some 1 - and! z = None + and! z = Some 4 return x + y + z - 10 } and! c = opt { - let! x = Some 14 + let! x = Some 0 and! y = Some 1 and! z = Some 2 return x + y + z + 1 } - and! d = baz - return a * b * c * d + and! d = baz // Makes use of an optional value from outside the computation expression (bailing out with None if it is None, as with any other value) + return a * b * c * d // Computes this value because all args are some + // Another way to view this is let! ... and! ... is a bit like a conjunction, and yield! ... yield! ... is a bit like a disjunction } printfn "quux: %+A" quux From 8e222ff6eae6a985144937e29e7e790a2438b2ad Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 17:36:52 +0100 Subject: [PATCH 160/255] Add example of `and! _ = ...` working --- scratch/data/SimpleBuilder.fsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 43a13000d4..b9dd2364bf 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -109,9 +109,9 @@ let quux : int option = and! b = opt { let! x = Some 9 - and! y = Some 1 + and! _ = Some "IGNORED" // Inner value goes unused, but we'd still bail out if this value were None and! z = Some 4 - return x + y + z - 10 + return x + z - 10 } and! c = opt { From 35779a817f69024124016e5baff9d8b324515997 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 19 Sep 2018 17:38:11 +0100 Subject: [PATCH 161/255] Add notes about use! and yield! --- scratch/docs/Log.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md index 6c0749065a..17247b3e9b 100644 --- a/scratch/docs/Log.md +++ b/scratch/docs/Log.md @@ -1019,6 +1019,8 @@ option { ``` ...probably not? That seems nice enough to me. +## 2018-09-18 + Okay, so the above seems to approximately work now. Next steps: * Look at error messages * Come up with some more examples of what should / should not work (and encode as tests?) @@ -1057,4 +1059,10 @@ builder.Apply( yDisFoo), zDisFoo) ``` -I've put this together by basically mapping what was done for monads onto applicatives. I've not really thought this through in terms of whether it actually solves a problem someone could have. Could just work off ScottW's `trace` example: https://fsharpforfunandprofit.com/posts/computation-expressions-builder-part6/ for now \ No newline at end of file +I've put this together by basically mapping what was done for monads onto applicatives. I've not really thought this through in terms of whether it actually solves a problem someone could have. Could just work off [ScottW's `trace` example](https://fsharpforfunandprofit.com/posts/computation-expressions-builder-part6/) for now. + +## 2018-09-19 + +One other thing I haven't considered: What happens for `and!` / `anduse!` when the "name" is `_`? + +Alternative applicatives work now. `yield!` does the alternation because of the previously discussed issues with `yield` (forces loads of inputs to be re-evaluated and makes the desugaring unclear, whereas `yield!` enables you to say precisely what to evaluate, since its exactly what you put on the RHS), the cost being mainly that you may need to open a new `opt { }` \ No newline at end of file From 9d2a2d37e5c4e6b71b36d0ab2be3c9f6d1f11297 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 24 Sep 2018 13:15:14 +0100 Subject: [PATCH 162/255] Refine in light of feedback --- ...etbang-andbang-for-applicative-functors.md | 439 ++++++++++++++++++ 1 file changed, 439 insertions(+) create mode 100644 scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md diff --git a/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md b/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md new file mode 100644 index 0000000000..53abd213dc --- /dev/null +++ b/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md @@ -0,0 +1,439 @@ +# F# RFC FS-1062 - Support let! .. and... for applicative functors + +The design suggestion [Support let! .. and... for applicative functors](https://github.com/fsharp/fslang-suggestions/issues/579) has been marked "approved in principle". +This RFC covers the detailed proposal for this suggestion. + +* [x] [Approved in principle](https://github.com/fsharp/fslang-suggestions/issues/579#event-1345361104) & [prioritised](https://github.com/fsharp/fslang-suggestions/issues/579#event-1501977428) +* [x] [Suggestion](https://github.com/fsharp/fslang-suggestions/issues/579) +* [ ] Implementation: [In progress](https://github.com/Microsoft/visualfsharp/pull/FILL-ME-IN) + + +# Summary +[summary]: #summary + +Extend computation expressions to support applicative functors via a new `let! ... and! ... return ...` syntax. + +# Motivation +[motivation]: #motivation + +Applicatives functors (or just "applicatives", for short) have been growing in popularity as a way to build applications and model certain domains over the last decade or so, since McBride and Paterson published [Applicative Programming with Effects](http://www.staff.city.ac.uk/~ross/papers/Applicative.html). Applicatives are now reaching a level of popularity within the community that supporting them with a convenient and readable syntax, as we do for monads, is beginning to make sense. + +## Why Applicatives? + +_Applicative functors_ sit, in terms of power, somewhere between _functors_ (i.e. types which support `Map`), and _monads_ (i.e. types which support `Bind`). + +If we consider `Bind : M<'T> * ('T -> M<'U>) -> M<'U>`, we can see that the second element of the input is a function that requires a value to create the resulting "wrapped value". + +In contrast, `Apply : M<'T -> 'U> * M<'T> -> M<'U>` only needs a wrapped function, which is something we have whilst building our computation. + +So, importantly, applicatives allow us the power to use functions which are "wrapped up" inside a functor, but [preserve our ability to analyse the structure of the computation](https://paolocapriotti.com/assets/applicative.pdf) - and hence introduce optimisations or alternative interpretations - without any values present. This is a critical distinction which can have a huge impact on performance, and indeed on what is possible to construct at all, so has very tangible implications. + +## Examples of Useful Applicatives + +The examples below all make use of types which are applicatives, but explicitly _not_ monads, to allow a powerful model for building a particular kind of computation, whilst preserving enough constraints to offer useful guarantees. + +[Tomas Petricek's formlets blog post](http://tomasp.net/blog/formlets-in-linq.aspx/) introduces the idea that we can use applicatives to build web forms. The guarantee of a static structure of the formlet applicative is used to render forms, but its powerful behaviours still allow useful processing of requests. + +[Pauan's comment about Observables](https://github.com/fsharp/fslang-suggestions/issues/579#issuecomment-310799948) points out that applicatives allow us to avoid frequent resubscriptions to `Observable` values because we know precisely how they'll be hooked up ahead of time, and that it won't change within the lifetime of the applicative. + +[McBride & Paterson's paper](http://www.staff.city.ac.uk/~ross/papers/Applicative.html) introduces a type very much like F#'s `Result<'T,'TError>` which can be used to stitch together functions and values which might fail, but conveniently accumulating up all of the errors which can then be helpfully presented at once, as opposed to immediately presenting the first error. This allows you to take [Scott Wlaschin's Railway Oriented Programming](https://fsharpforfunandprofit.com/rop/) to the next level! + +[Capriotti & Kaposi's paper](https://paolocapriotti.com/assets/applicative.pdf) introduce an example of creating an command line argument parser, where a single applicative can both statically generate help text for the parser, and dynamically parse options given to an application. + +More advanced usages include self-adjusting compuations, such as [Jane Street's Incremental](https://blog.janestreet.com/introducing-incremental/), but with even greater opportunity for efficiencies via the careful balance of power and guarantees that applicatives give (such as the ability to optimise the computation, and avoid recreating the rest of the computation everytime a `Bind` is reached). + +# Detailed design +[design]: #detailed-design + +This RFC introduces a desugaring for applicative computation expressions, much like that which exists for monads and related constructs. + +Example desugaring: + +```fsharp +ce { + let! x = foo + and! y = bar + and! z = baz + return x + y + z + } +``` + +⇒ + +``` +ce.Apply( + ce.Apply( + ce.Apply( + ce.Return( + (fun x -> + (fun y -> + (fun z -> + x + y + z + ) + ) + )), + foo), + bar), + baz) +``` + +To be accepted as an applicative computation expression (CE), the CE must be of the form `let! ... and! ... return ...` (with one or more `and!`s). This means, for example, no `let!`s after an `and!` in the same CE, no normal `let`s inbetween the `let!` and `and!`s, and no usages of `yield` in place of `return`. + +This may sound very constrained, but it is for good reason. The structure imposed by this rule forces the CE to be in a canonical form ([McBride & Paterson](http://www.staff.city.ac.uk/~ross/papers/Applicative.html)): + +> Any expression built from the Applicative combinators can be transformed +to a canonical form in which a single pure function is ‘applied’ to the effectful parts +in depth-first order: +`pure f <*> arg1 <*> ... <*> argN` +This canonical form captures the essence of Applicative programming: computations +have a fixed structure, given by the pure function, and a sequence of subcomputations, +given by the effectful arguments. + +In our case, the expression to the right of `return` (i.e. `pure`) becomes the body of a lambda, whose parameters are introduced by the `let! ... and! ...` preceeding it. This leads to a very straightforward desugaring of the syntax, which therefore ensures both using and understanding the feature is only as complex as is inherently required by the abstraction. + +Despite requiring the canonical form, there are still many ways to build more complex and useful expressions from this syntax. The rest of this section aims to give a tour around these various features. + +## Pattern Matching + +```fsharp +let (|Quad|) (i : int) = + i * i * i * i + +type SingleCaseDu<'a> = SingleCaseDu of 'a + +ce { + let! Quad(x) = foo + and! (y,_) = bar + and! (SingleCaseDu z) = baz + return x + y + z + } +``` + +⇒ + +```fsharp +ce.Apply( + ce.Apply( + ce.Apply( + ce.Return( + (fun Quad(x) -> + (fun (y,_) -> + (fun (SingleCaseDu z) -> + x + y + z + ) + ) + )), + foo), + bar), + baz) +``` + +Pattern matching on the left-hand side of of a `let! ... and! ...` binding is valid, and desugars into the same pattern, but now as part of the lambda corresponding to that binding. + +## Using Monadic and Applicative Styles Simultaneously + +Recall that `let! ... and! ... return ...` syntax precludes an additional `let!` anywhere in the CE. In the case where your applicative happens also to be a monad, and you want to leverage the benefits of an applicative in some places (e.g. for performance reasons) but also use a `let!` (e.g. for convenience, or to do something a pure applicative doesn't support), you must do so inside a different CE context, e.g.: + +```fsharp +ce { + let! quux = + ce { + let! x = foo + and! (y,_) = bar + and! (SingleCaseDu z) = baz + return x + y + z + } + if quux > 6 + then + return quux + else + return 5 +} +``` + +⇒ + +```fsharp +ce.Bind( + ce.Apply( + ce.Apply( + ce.Apply( + ce.Return( + (fun x -> + (fun (y,_) -> + (fun (SingleCaseDu z) -> + x + y + z + ) + ) + )), + foo), + bar), + baz), + (fun quux -> + if quux > 6 + then + return quux + else + return 5) +) +``` + +## Monoids + +The existing `let!` CE syntax allows us to use `Combine` (typically in conjunction with `yield`) to define monad plus instances (i.e. also interpret the type as a monoid). [Just as monad plus is to a monad, alternatives are to applicatives](https://en.wikibooks.org/wiki/Haskell/Alternative_and_MonadPlus), so we can do a similar thing for our applicative CE syntax. One motivation for this might be the command line argument example from earlier, where alternatives allow parsing discriminated unions in a way that translates to something akin to "try this case, else try this case, else try this case, ...". + +One might assume that the syntax would be something such as: + +```fsharp +ce { + let! x = foo + and! y = bar + and! z = baz + yield x + y + z + yield x + y + yield y + z + } +``` + +Unfortunately, the naive desugaring of this can make it very easy to build a resulting chain of method calls which unintentionally ends up being very large: + +``` +ce.Combine( + ce.Combine( + ce.Apply( + ce.Apply( + ce.Apply( + ce.Return( + (fun x -> + (fun y -> + (fun z -> + x + y + z + ) + ) + )), + foo), + bar), + baz), + ce.Apply( + ce.Apply( + ce.Apply( + ce.Return( + (fun x -> + (fun y -> + (fun z -> + x + y + ) + ) + )), + foo), + bar), + baz) + ), + ce.Apply( + ce.Apply( + ce.Apply( + ce.Return( + (fun x -> + (fun y -> + (fun z -> + y + z + ) + ) + )), + foo), + bar), + baz) +) +``` + +*N.B.* How the size of the desugared expression grows with the product of the number of bindings introduced by the `let! ... and! ...` syntax and the number calls to `Combine` implied by the alternative cases. + +An attempt at a very smart desugaring which tries to cut down the resulting expression might, on the face of it, seem like a reasonable option, however, beyond the cost of analysing which values which are introduced by `let! ... and! ...` actually go on to be used, we must also consider the right-hand sides of the `let! ... and! ...` bindings and the pattern matching: Do we evaluated these once up front? Or recompute them in each alternative case at the leaf of the tree of calls to `Combine`? What if the expressions on the right-hand sides have side-effects, or the left-hand side utilises active patterns with side-effects? At that point we either make complex, uninutive rules, or force the CE writer to be explicit. Continuing in the spirit of CEs generally being straightforward desugarings, we choose the latter make make the writer clearly state their desire. + +In order to keep things simple, then, we keep the canonical form introduced earlier, which forces precisely one `return` after a `let! ... and! ...`. However, we can still express alternative applicatives when using the `let! ... and! ...` syntax, we just need to use the same trick as with additional `let!`s and leave the scope of the canonical applicative syntax and therefore leave the additional contraints it places upon us: + +```fsharp +ce { + yield + ce { + let! x = foo + and! y = bar + and! z = baz + return x + y + z + } + yield + ce { + let! x = foo + and! y = bar + return x + y + } + yield + ce { + let! x = foo + and! y = bar + return y + z + } +} +``` + +⇒ + +``` +ce.Combine( + ce.Combine( + ce.Apply( + ce.Apply( + ce.Apply( + ce.Return( + (fun x -> + (fun y -> + (fun z -> + x + y + z + ) + ) + )), + foo), + bar), + baz), + ce.Apply( + ce.Apply( + ce.Return( + (fun x -> + (fun y -> + x + y + ) + )), + foo), + bar) + ), + ce.Apply( + ce.Apply( + ce.Return( + (fun y -> + (fun z -> + y + z + ) + )), + bar), + baz) +) +``` + +*N.B.* How this syntax forces the writer to be explicit about how many times `Apply` should be called, and with which arguments, for each call to `Combine`. Notice also how the right-hand sides are still repeated for each alternative case in order to keep the occurance of potential side-effects from evaluating them predictable, and also occur before the pattern matching _each time_ a new alternative case is explored. + +If this syntax feels a bit heavy, remember that the `yield` keyword is not required, and the new syntax can be mixed with other styles (e.g. a custom `<|>` alternation operator) to strike an appropriate balance as each situation requires. + +## Using + +Just as monads support `Using` via `use!`, applicatives supports it via `use! ... anduse! ...`. Each binding can be either `and!` or `anduse!` (unless it is the first, in which case it must be either `let!` or `use!`), i.e. you can mix-and-match to describe which bindings should be covered by a call to `Using`: + +```fsharp +ce { + use! x = foo // x is wrapped in a call to ce.Using(...) + and! y = bar // y is _not_ wrapped in a call to ce.Using(...) + anduse! z = baz // z is wrapped in a call to ce.Using(...) + return x + y + z + } +``` + +⇒ + +```fsharp +ce.Apply( + ce.Apply( + ce.Apply( + ce.Return( + (fun x -> + ce.Using(x, fun x -> + // N.B. No ce.Using(...) call here because we used `and!` instead of `anduse!` for `y` in the CE. Similarly, we could have chose to use `let!` instead of `use!` for the first binding to avoid a call to Using + (fun y -> + (fun z -> + ce.Using(z, fun z -> + x + y + z + ) + ) + ) + ) + )), + foo), + bar), + baz) +``` + +## Ambiguities surrounding a `let! .. return ...` +[singlelet]: #singlelet + +Some CEs could be validly desugared in multiple ways, depending on which methods are implemented on a builder (assuming the implementations follow the standard laws relating these functions). + +For example: + +```fsharp +ce { + let! x = foo + return x + 1 + } +``` + +Can be desugared via `Bind`: + +```fsharp +ce.Bind( + (fun x -> ce.Return(x + 1)), + foo) +``` + +Or via `Apply`: + +```fsharp +ce.Apply( + ce.Return(fun x -> x + 1), + foo) +``` + +This is because the operation is really equivalent to a `Map`, something which can be implemented in terms of `Return` and either `Bind` or `Apply`. We mentioned that these functions were in some sense more powerful than a plain functor's `Map`, and we are seeing an example of that here. + +In order to avoid breaking backwards compatability, the default resolution is to desugar via `Bind`, _failing if it is not defined on the builder_ (even though, conceptually, it chould be implemented via `Apply`). This is consistent with in previous F# versions. [Later work on supporting `Map`](https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1048-ce-builder-map.md) can then make the choice about how to resolve this in a way which works with that in mind too. + +# Drawbacks +[drawbacks]: #drawbacks + +The new applicative compuation expressions are quite constrainted, and as has been discussed, that is precisely what allows them to be so useful. However, these constraints are potentially somewhat unintuitive to the beginner. Computation expressions already involve one of the steeper learning curves of the F# language features, so the added complexity from this feature needs to be carefully weighed against the potential guarantees, expressiveness and performance gains that they can offer. + +# Alternative Designs +[alternative-designs]: #alternative-designs + +[Tomas Petricek's Joinads](http://tomasp.net/blog/fsharp-variations-joinads.aspx/) offered a superset of the features proposed here, but was [rejected](https://github.com/fsharp/fslang-suggestions/issues/172) due to its complexity. This RFC is of much smaller scope, so should be a much less risky change. + +# Compatibility +[compatibility]: #compatibility + +## Is this a breaking change? + +This change should be backwards compatible. + +* Uses of `let!` without `and!` should still desugar to use `Bind`. See [the discussion of `let! ... return ...`](#singlelet) computation expressions for more details. + +* Existing computation expression builders with an `Apply` method should not change in behaviour, since usages of the builder would still need to add the new `let! ... and! ...` syntax to activate it. + +## What happens when previous versions of the F# compiler encounter this design addition as source code? + +Previous compiler versions reject the new `and!` keyword: + +``` +error FS1141: Identifiers followed by '!' are reserved for future use +``` + +## What happens when previous versions of the F# compiler encounter this design addition in compiled binaries? + +Since the syntax is desugared into a method call on the builder object, after compilation usages of this feature will be usable with previous compiler versions. + +# Unresolved questions +[unresolved]: #unresolved-questions + +Is `anduse!` an acceptable keyword for when `and!` must also imply a call to `Using`? + +There are various ways of desugaring `let! ... return ...` via one of `Map`, `Apply` or `Bind`. [The above](#singlelet) assumes that we are aiming for backward compatability and hence doesn't consider desugaring to `Apply` at all. Is this the best choice? Alternatives include: + +* Using `Apply` in preference to `Bind` for `let! ... return` whenever `Apply` is defined (on the basis that that any reasonable `Apply` implementation will be functionally equivalent, but more efficient than the corresponding `Bind`) +* Subsuming [the RFC for desugaring this to `Map`](https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1048-ce-builder-map.md) and defining a heirarchy between `Map`, `Apply` and `Bind` and some attributes for those methods to allow the creators of builders to opt-in to "optimisations" that pick the least powerful (and hence hopefully most efficient) desugaring. +* Using `Apply` in place of `Bind` only in those instances where `Bind` is not defined (no existing code should break, but this goes somewhat to the contrary of the previous proposal, which we may want to consider in the future) \ No newline at end of file From 9d9921bcb67c541d47611adf930d3b315bb6947a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 24 Sep 2018 13:23:06 +0100 Subject: [PATCH 163/255] Fix spelling mistakes etc --- ...etbang-andbang-for-applicative-functors.md | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md b/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md index 53abd213dc..9ef5854b57 100644 --- a/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md +++ b/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md @@ -1,4 +1,4 @@ -# F# RFC FS-1062 - Support let! .. and... for applicative functors + F# RFC FS-1062 - Support let! .. and... for applicative functors The design suggestion [Support let! .. and... for applicative functors](https://github.com/fsharp/fslang-suggestions/issues/579) has been marked "approved in principle". This RFC covers the detailed proposal for this suggestion. @@ -16,7 +16,7 @@ Extend computation expressions to support applicative functors via a new `let! . # Motivation [motivation]: #motivation -Applicatives functors (or just "applicatives", for short) have been growing in popularity as a way to build applications and model certain domains over the last decade or so, since McBride and Paterson published [Applicative Programming with Effects](http://www.staff.city.ac.uk/~ross/papers/Applicative.html). Applicatives are now reaching a level of popularity within the community that supporting them with a convenient and readable syntax, as we do for monads, is beginning to make sense. +Applicative functors (or just "applicatives", for short) have been growing in popularity as a way to build applications and model certain domains over the last decade or so, since McBride and Paterson published [Applicative Programming with Effects](http://www.staff.city.ac.uk/~ross/papers/Applicative.html). Applicatives are now reaching a level of popularity within the community that supporting them with a convenient and readable syntax, as we do for monads, is beginning to make sense. ## Why Applicatives? @@ -40,7 +40,7 @@ The examples below all make use of types which are applicatives, but explicitly [Capriotti & Kaposi's paper](https://paolocapriotti.com/assets/applicative.pdf) introduce an example of creating an command line argument parser, where a single applicative can both statically generate help text for the parser, and dynamically parse options given to an application. -More advanced usages include self-adjusting compuations, such as [Jane Street's Incremental](https://blog.janestreet.com/introducing-incremental/), but with even greater opportunity for efficiencies via the careful balance of power and guarantees that applicatives give (such as the ability to optimise the computation, and avoid recreating the rest of the computation everytime a `Bind` is reached). +More advanced usages include self-adjusting computations, such as [Jane Street's Incremental](https://blog.janestreet.com/introducing-incremental/), but with even greater opportunity for efficiencies via the careful balance of power and guarantees that applicatives give (such as the ability to optimise the computation, and avoid recreating the rest of the computation every time a `Bind` is reached). # Detailed design [design]: #detailed-design @@ -60,7 +60,7 @@ ce { ⇒ -``` +```fsharp ce.Apply( ce.Apply( ce.Apply( @@ -77,19 +77,15 @@ ce.Apply( baz) ``` -To be accepted as an applicative computation expression (CE), the CE must be of the form `let! ... and! ... return ...` (with one or more `and!`s). This means, for example, no `let!`s after an `and!` in the same CE, no normal `let`s inbetween the `let!` and `and!`s, and no usages of `yield` in place of `return`. +To be accepted as an applicative computation expression (CE), the CE must be of the form `let! ... and! ... return ...` (with one or more `and!`s). This means, for example, no `let!`s after an `and!` in the same CE, no normal `let`s in between the `let!` and `and!`s, and no usages of `yield` in place of `return`. This may sound very constrained, but it is for good reason. The structure imposed by this rule forces the CE to be in a canonical form ([McBride & Paterson](http://www.staff.city.ac.uk/~ross/papers/Applicative.html)): -> Any expression built from the Applicative combinators can be transformed -to a canonical form in which a single pure function is ‘applied’ to the effectful parts -in depth-first order: +> Any expression built from the Applicative combinators can be transformed to a canonical form in which a single pure function is ‘applied’ to the effectful parts in depth-first order: `pure f <*> arg1 <*> ... <*> argN` -This canonical form captures the essence of Applicative programming: computations -have a fixed structure, given by the pure function, and a sequence of subcomputations, -given by the effectful arguments. +This canonical form captures the essence of Applicative programming: computations have a fixed structure, given by the pure function, and a sequence of subcomputations, given by the effectful arguments. -In our case, the expression to the right of `return` (i.e. `pure`) becomes the body of a lambda, whose parameters are introduced by the `let! ... and! ...` preceeding it. This leads to a very straightforward desugaring of the syntax, which therefore ensures both using and understanding the feature is only as complex as is inherently required by the abstraction. +In our case, the expression to the right of `return` (i.e. `pure`) becomes the body of a lambda, whose parameters are introduced by the `let! ... and! ...` preceding it. This leads to a very straightforward desugaring of the syntax, which therefore ensures both using and understanding the feature is only as complex as is inherently required by the abstraction. Despite requiring the canonical form, there are still many ways to build more complex and useful expressions from this syntax. The rest of this section aims to give a tour around these various features. @@ -144,7 +140,7 @@ ce { return x + y + z } if quux > 6 - then + then return quux else return 5 @@ -167,7 +163,7 @@ ce.Bind( ) )), foo), - bar), + bar), baz), (fun quux -> if quux > 6 @@ -197,7 +193,7 @@ ce { Unfortunately, the naive desugaring of this can make it very easy to build a resulting chain of method calls which unintentionally ends up being very large: -``` +```fsharp ce.Combine( ce.Combine( ce.Apply( @@ -226,7 +222,7 @@ ce.Combine( ) )), foo), - bar), + bar), baz) ), ce.Apply( @@ -248,9 +244,9 @@ ce.Combine( *N.B.* How the size of the desugared expression grows with the product of the number of bindings introduced by the `let! ... and! ...` syntax and the number calls to `Combine` implied by the alternative cases. -An attempt at a very smart desugaring which tries to cut down the resulting expression might, on the face of it, seem like a reasonable option, however, beyond the cost of analysing which values which are introduced by `let! ... and! ...` actually go on to be used, we must also consider the right-hand sides of the `let! ... and! ...` bindings and the pattern matching: Do we evaluated these once up front? Or recompute them in each alternative case at the leaf of the tree of calls to `Combine`? What if the expressions on the right-hand sides have side-effects, or the left-hand side utilises active patterns with side-effects? At that point we either make complex, uninutive rules, or force the CE writer to be explicit. Continuing in the spirit of CEs generally being straightforward desugarings, we choose the latter make make the writer clearly state their desire. +An attempt at a very smart desugaring which tries to cut down the resulting expression might, on the face of it, seem like a reasonable option, however, beyond the cost of analysing which values which are introduced by `let! ... and! ...` actually go on to be used, we must also consider the right-hand sides of the `let! ... and! ...` bindings and the pattern matching: Do we evaluated these once up front? Or recompute them in each alternative case at the leaf of the tree of calls to `Combine`? What if the expressions on the right-hand sides have side-effects, or the left-hand side utilises active patterns with side-effects? At that point we either make complex, unintuitive rules, or force the CE writer to be explicit. Continuing in the spirit of CEs generally being straightforward desugarings, we choose the latter make make the writer clearly state their desire. -In order to keep things simple, then, we keep the canonical form introduced earlier, which forces precisely one `return` after a `let! ... and! ...`. However, we can still express alternative applicatives when using the `let! ... and! ...` syntax, we just need to use the same trick as with additional `let!`s and leave the scope of the canonical applicative syntax and therefore leave the additional contraints it places upon us: +In order to keep things simple, then, we keep the canonical form introduced earlier, which forces precisely one `return` after a `let! ... and! ...`. However, we can still express alternative applicatives when using the `let! ... and! ...` syntax, we just need to use the same trick as with additional `let!`s and leave the scope of the canonical applicative syntax and therefore leave the additional constraints it places upon us: ```fsharp ce { @@ -278,7 +274,7 @@ ce { ⇒ -``` +```fsharp ce.Combine( ce.Combine( ce.Apply( @@ -319,7 +315,7 @@ ce.Combine( ) ``` -*N.B.* How this syntax forces the writer to be explicit about how many times `Apply` should be called, and with which arguments, for each call to `Combine`. Notice also how the right-hand sides are still repeated for each alternative case in order to keep the occurance of potential side-effects from evaluating them predictable, and also occur before the pattern matching _each time_ a new alternative case is explored. +*N.B.* How this syntax forces the writer to be explicit about how many times `Apply` should be called, and with which arguments, for each call to `Combine`. Notice also how the right-hand sides are still repeated for each alternative case in order to keep the occurrence of potential side-effects from evaluating them predictable, and also occur before the pattern matching _each time_ a new alternative case is explored. If this syntax feels a bit heavy, remember that the `yield` keyword is not required, and the new syntax can be mixed with other styles (e.g. a custom `<|>` alternation operator) to strike an appropriate balance as each situation requires. @@ -392,12 +388,12 @@ ce.Apply( This is because the operation is really equivalent to a `Map`, something which can be implemented in terms of `Return` and either `Bind` or `Apply`. We mentioned that these functions were in some sense more powerful than a plain functor's `Map`, and we are seeing an example of that here. -In order to avoid breaking backwards compatability, the default resolution is to desugar via `Bind`, _failing if it is not defined on the builder_ (even though, conceptually, it chould be implemented via `Apply`). This is consistent with in previous F# versions. [Later work on supporting `Map`](https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1048-ce-builder-map.md) can then make the choice about how to resolve this in a way which works with that in mind too. +In order to avoid breaking backwards compatibility, the default resolution is to desugar via `Bind`, _failing if it is not defined on the builder_ (even though, conceptually, it should be implemented via `Apply`). This is consistent with in previous F# versions. [Later work on supporting `Map`](https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1048-ce-builder-map.md) can then make the choice about how to resolve this in a way which works with that in mind too. # Drawbacks [drawbacks]: #drawbacks -The new applicative compuation expressions are quite constrainted, and as has been discussed, that is precisely what allows them to be so useful. However, these constraints are potentially somewhat unintuitive to the beginner. Computation expressions already involve one of the steeper learning curves of the F# language features, so the added complexity from this feature needs to be carefully weighed against the potential guarantees, expressiveness and performance gains that they can offer. +The new applicative computation expressions are quite constrained, and as has been discussed, that is precisely what allows them to be so useful. However, these constraints are potentially somewhat unintuitive to the beginner. Computation expressions already involve one of the steeper learning curves of the F# language features, so the added complexity from this feature needs to be carefully weighed against the potential guarantees, expressiveness and performance gains that they can offer. # Alternative Designs [alternative-designs]: #alternative-designs @@ -432,8 +428,8 @@ Since the syntax is desugared into a method call on the builder object, after co Is `anduse!` an acceptable keyword for when `and!` must also imply a call to `Using`? -There are various ways of desugaring `let! ... return ...` via one of `Map`, `Apply` or `Bind`. [The above](#singlelet) assumes that we are aiming for backward compatability and hence doesn't consider desugaring to `Apply` at all. Is this the best choice? Alternatives include: +There are various ways of desugaring `let! ... return ...` via one of `Map`, `Apply` or `Bind`. [The above](#singlelet) assumes that we are aiming for backward compatibility and hence doesn't consider desugaring to `Apply` at all. Is this the best choice? Alternatives include: * Using `Apply` in preference to `Bind` for `let! ... return` whenever `Apply` is defined (on the basis that that any reasonable `Apply` implementation will be functionally equivalent, but more efficient than the corresponding `Bind`) -* Subsuming [the RFC for desugaring this to `Map`](https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1048-ce-builder-map.md) and defining a heirarchy between `Map`, `Apply` and `Bind` and some attributes for those methods to allow the creators of builders to opt-in to "optimisations" that pick the least powerful (and hence hopefully most efficient) desugaring. +* Subsuming [the RFC for desugaring this to `Map`](https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1048-ce-builder-map.md) and defining a hierarchy between `Map`, `Apply` and `Bind` and some attributes for those methods to allow the creators of builders to opt-in to "optimisations" that pick the least powerful (and hence hopefully most efficient) desugaring. * Using `Apply` in place of `Bind` only in those instances where `Bind` is not defined (no existing code should break, but this goes somewhat to the contrary of the previous proposal, which we may want to consider in the future) \ No newline at end of file From 8f2b0e503527a3068a6394ca2cfbc2ab339c0fde Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 24 Sep 2018 13:43:59 +0100 Subject: [PATCH 164/255] Clean up after additional feedback --- ...etbang-andbang-for-applicative-functors.md | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md b/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md index 9ef5854b57..13bcdf9980 100644 --- a/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md +++ b/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md @@ -1,4 +1,4 @@ - F# RFC FS-1062 - Support let! .. and... for applicative functors +# F# RFC FS-1062 - Support let! .. and... for applicative functors The design suggestion [Support let! .. and... for applicative functors](https://github.com/fsharp/fslang-suggestions/issues/579) has been marked "approved in principle". This RFC covers the detailed proposal for this suggestion. @@ -7,7 +7,6 @@ This RFC covers the detailed proposal for this suggestion. * [x] [Suggestion](https://github.com/fsharp/fslang-suggestions/issues/579) * [ ] Implementation: [In progress](https://github.com/Microsoft/visualfsharp/pull/FILL-ME-IN) - # Summary [summary]: #summary @@ -16,17 +15,17 @@ Extend computation expressions to support applicative functors via a new `let! . # Motivation [motivation]: #motivation -Applicative functors (or just "applicatives", for short) have been growing in popularity as a way to build applications and model certain domains over the last decade or so, since McBride and Paterson published [Applicative Programming with Effects](http://www.staff.city.ac.uk/~ross/papers/Applicative.html). Applicatives are now reaching a level of popularity within the community that supporting them with a convenient and readable syntax, as we do for monads, is beginning to make sense. +Applicative functors (or just "applicatives", for short) have been growing in popularity as a way to build applications and model certain domains over the last decade or so, since McBride and Paterson published [Applicative Programming with Effects](http://www.staff.city.ac.uk/~ross/papers/Applicative.html). Applicatives are now reaching a level of popularity within the community that supporting them with a convenient and readable syntax, as we do for monads, makes sense. ## Why Applicatives? -_Applicative functors_ sit, in terms of power, somewhere between _functors_ (i.e. types which support `Map`), and _monads_ (i.e. types which support `Bind`). +[Applicative functors](https://en.wikipedia.org/wiki/Applicative_functor) sit, in terms of power, somewhere between [functors](https://en.wikipedia.org/wiki/Functor#Computer_implementations) (i.e. types which support `Map`), and [monads](https://en.wikipedia.org/wiki/Monad_(functional_programming)) (i.e. types which support `Bind`). If we consider `Bind : M<'T> * ('T -> M<'U>) -> M<'U>`, we can see that the second element of the input is a function that requires a value to create the resulting "wrapped value". In contrast, `Apply : M<'T -> 'U> * M<'T> -> M<'U>` only needs a wrapped function, which is something we have whilst building our computation. -So, importantly, applicatives allow us the power to use functions which are "wrapped up" inside a functor, but [preserve our ability to analyse the structure of the computation](https://paolocapriotti.com/assets/applicative.pdf) - and hence introduce optimisations or alternative interpretations - without any values present. This is a critical distinction which can have a huge impact on performance, and indeed on what is possible to construct at all, so has very tangible implications. +So, importantly, applicatives allow us the power to use functions which are "wrapped up" inside a functor, but [preserve our ability to analyse the structure of the computation](https://paolocapriotti.com/assets/applicative.pdf) - and hence allow for the introduction of optimisations or alternative interpretations - without any values present. This is a critical distinction which can have a huge impact on performance, and indeed on what is possible to construct at all, so has very tangible implications. ## Examples of Useful Applicatives @@ -38,9 +37,9 @@ The examples below all make use of types which are applicatives, but explicitly [McBride & Paterson's paper](http://www.staff.city.ac.uk/~ross/papers/Applicative.html) introduces a type very much like F#'s `Result<'T,'TError>` which can be used to stitch together functions and values which might fail, but conveniently accumulating up all of the errors which can then be helpfully presented at once, as opposed to immediately presenting the first error. This allows you to take [Scott Wlaschin's Railway Oriented Programming](https://fsharpforfunandprofit.com/rop/) to the next level! -[Capriotti & Kaposi's paper](https://paolocapriotti.com/assets/applicative.pdf) introduce an example of creating an command line argument parser, where a single applicative can both statically generate help text for the parser, and dynamically parse options given to an application. +[Capriotti & Kaposi's paper](https://paolocapriotti.com/assets/applicative.pdf) introduces an example of creating an command line argument parser, where a single applicative can both statically generate help text for the parser, and dynamically parse options given to an application. -More advanced usages include self-adjusting computations, such as [Jane Street's Incremental](https://blog.janestreet.com/introducing-incremental/), but with even greater opportunity for efficiencies via the careful balance of power and guarantees that applicatives give (such as the ability to optimise the computation, and avoid recreating the rest of the computation every time a `Bind` is reached). +More advanced usages include self-adjusting computations, such as [Jane Street's Incremental](https://blog.janestreet.com/introducing-incremental/) (written in OCaml), but with even greater opportunity for efficiencies via the careful balance of power and guarantees that applicatives give (such as the ability to optimise the computation, and avoid recreating the rest of the computation every time a `Bind` is reached. # Detailed design [design]: #detailed-design @@ -77,7 +76,7 @@ ce.Apply( baz) ``` -To be accepted as an applicative computation expression (CE), the CE must be of the form `let! ... and! ... return ...` (with one or more `and!`s). This means, for example, no `let!`s after an `and!` in the same CE, no normal `let`s in between the `let!` and `and!`s, and no usages of `yield` in place of `return`. +To be accepted as an applicative computation expression (CE), the CE must be of the form `let! ... and! ... return ...` (with one or more `and!`s). This means, for example, no `let!`s after an `and!` in the same CE, no normal `let`s in between the `let!` and `and!`s, and no usages of `yield` in place of `return`. Similarly, `do!` and `match!` are not valid in an applicative builder, although we allow `use!` and related keywords, which will be covered later. This may sound very constrained, but it is for good reason. The structure imposed by this rule forces the CE to be in a canonical form ([McBride & Paterson](http://www.staff.city.ac.uk/~ross/papers/Applicative.html)): @@ -176,7 +175,7 @@ ce.Bind( ## Monoids -The existing `let!` CE syntax allows us to use `Combine` (typically in conjunction with `yield`) to define monad plus instances (i.e. also interpret the type as a monoid). [Just as monad plus is to a monad, alternatives are to applicatives](https://en.wikibooks.org/wiki/Haskell/Alternative_and_MonadPlus), so we can do a similar thing for our applicative CE syntax. One motivation for this might be the command line argument example from earlier, where alternatives allow parsing discriminated unions in a way that translates to something akin to "try this case, else try this case, else try this case, ...". +The existing `let!` CE syntax allows us to use `Combine` (typically in conjunction with `yield`) to define [monad plus](https://hackage.haskell.org/package/monadplus/docs/Control-Monad-Plus.html) instances (i.e. also interpret the type as a [monoid](https://en.wikipedia.org/wiki/Monoid)). [Just as monad plus is to a monad, alternatives are to applicatives](https://en.wikibooks.org/wiki/Haskell/Alternative_and_MonadPlus), so we can do a similar thing for our applicative CE syntax. One motivation for this might be the command line argument example from earlier, where alternatives allow parsing discriminated unions in a way that translates to something akin to "try this case, else try this case, else try this case, ...". One might assume that the syntax would be something such as: @@ -242,9 +241,9 @@ ce.Combine( ) ``` -*N.B.* How the size of the desugared expression grows with the product of the number of bindings introduced by the `let! ... and! ...` syntax and the number calls to `Combine` implied by the alternative cases. +*N.B.* the size of the desugared expression grows with the product of the number of bindings introduced by the `let! ... and! ...` syntax and the number calls to `Combine` implied by the alternative cases. -An attempt at a very smart desugaring which tries to cut down the resulting expression might, on the face of it, seem like a reasonable option, however, beyond the cost of analysing which values which are introduced by `let! ... and! ...` actually go on to be used, we must also consider the right-hand sides of the `let! ... and! ...` bindings and the pattern matching: Do we evaluated these once up front? Or recompute them in each alternative case at the leaf of the tree of calls to `Combine`? What if the expressions on the right-hand sides have side-effects, or the left-hand side utilises active patterns with side-effects? At that point we either make complex, unintuitive rules, or force the CE writer to be explicit. Continuing in the spirit of CEs generally being straightforward desugarings, we choose the latter make make the writer clearly state their desire. +An attempt at a very smart desugaring which tries to cut down the resulting expression might, on the face of it, seem like a reasonable option. However, beyond the cost of analysing which values which are introduced by `let! ... and! ...` actually go on to be used, we must also consider the right-hand sides of the `let! ... and! ...` bindings and the pattern matching: Do we evaluated these once up front? Or recompute them in each alternative case at the leaf of the tree of calls to `Combine`? What if the expressions on the right-hand sides have side-effects, or the left-hand side utilises active patterns with side-effects? At that point we either make complex, unintuitive rules, or force the CE writer to be explicit. Continuing in the spirit of CEs generally being straightforward desugarings, we choose the latter make make the writer clearly state their desire. In order to keep things simple, then, we keep the canonical form introduced earlier, which forces precisely one `return` after a `let! ... and! ...`. However, we can still express alternative applicatives when using the `let! ... and! ...` syntax, we just need to use the same trick as with additional `let!`s and leave the scope of the canonical applicative syntax and therefore leave the additional constraints it places upon us: @@ -315,7 +314,7 @@ ce.Combine( ) ``` -*N.B.* How this syntax forces the writer to be explicit about how many times `Apply` should be called, and with which arguments, for each call to `Combine`. Notice also how the right-hand sides are still repeated for each alternative case in order to keep the occurrence of potential side-effects from evaluating them predictable, and also occur before the pattern matching _each time_ a new alternative case is explored. +*N.B.* this syntax forces the writer to be explicit about how many times `Apply` should be called, and with which arguments, for each call to `Combine`. Notice also how the right-hand sides are still repeated for each alternative case in order to keep the occurrence of potential side-effects from evaluating them predictable, and also occur before the pattern matching _each time_ a new alternative case is explored. If this syntax feels a bit heavy, remember that the `yield` keyword is not required, and the new syntax can be mixed with other styles (e.g. a custom `<|>` alternation operator) to strike an appropriate balance as each situation requires. @@ -341,11 +340,10 @@ ce.Apply( ce.Return( (fun x -> ce.Using(x, fun x -> - // N.B. No ce.Using(...) call here because we used `and!` instead of `anduse!` for `y` in the CE. Similarly, we could have chose to use `let!` instead of `use!` for the first binding to avoid a call to Using - (fun y -> - (fun z -> - ce.Using(z, fun z -> - x + y + z + (fun y -> // <- N.B. No ce.Using(...) call here because we used `and!` + (fun z -> // instead of `anduse!` for `y` in the CE. Similarly, we + ce.Using(z, fun z -> // could have chose to use `let!` instead of `use!` for the + x + y + z // first binding to avoid a call to Using ) ) ) From 79440988c756af61df66d88f76c58d28554a2280 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 24 Sep 2018 18:01:34 +0100 Subject: [PATCH 165/255] Delete Log.md --- scratch/docs/Log.md | 1068 ------------------------------------------- 1 file changed, 1068 deletions(-) delete mode 100644 scratch/docs/Log.md diff --git a/scratch/docs/Log.md b/scratch/docs/Log.md deleted file mode 100644 index 17247b3e9b..0000000000 --- a/scratch/docs/Log.md +++ /dev/null @@ -1,1068 +0,0 @@ -# F# Contribtions Log - -## 2018-09-03 -Got set up with Toby's help. I put together his key points below. There are a few different getting started guides that I can find, but Toby pointed me to [DEVGUIDE.md](https://github.com/Microsoft/visualfsharp/blob/master/DEVGUIDE.md) which seems to be the most helpful. - -### Notes for Getting Started -1. git clone https://github.com/Microsoft/visualfsharp - There are multiple mirrors, forks, ports, etc, but this is the one you fork and make changes to. There are dev branches, but you probably want to branch from master is this is a new feature. You'll find `DEVGUIDE.md` in the root of that repo. -1. To build, you'll want to run the `build.cmd` script (it sets up some env vars etc, then kicks off msbuild) in the root of the repo, **but** you don't want to run it with `cmd`! You'll want to use the "Developer Command Prompt for VS 2017" and run that as admin. The F# contributors generally seem to refer to this as just "the admin prompt" so don't be confused by that. On my machine, I can find the developer prompt by hitting the Windows key and typing it's name. No doubt you can find it alongside your VS install if Windows search disappoints. -1. Before you can open the F# compiler in Visual Studio, you'll probably to grab a few more SDKs and targeting packs. The F# compiler is multi-target, that is, you can build binaries with the compiler that target a different framework version to that used to build the compiler itself. As such, to build the compiler, you need to pull in a ton of extra framework stuff. It was an additional 3GB or so for me. I used Windows search to find "Visual Studio Installer", hit "Modify" on my VS install, opened the "Individual Components" tab and grabbed _everything_ under the ".NET" heading. -1. Now you can try opening `FSharp.sln`, which you'll find in the root of the repo. You'll also see `VisualFSharp.sln` file in there, but that includes the Visual Studio integrations etc, I think, and is much bigger. You probably won't need it if you're working on core compiler stuff. -1. To test making changes to the compiler and explore what it does, Toby recommended the following: - * Create an example `.fsx` file to build whatever you are insterested in - * Set `Fsc` as the start-up project, with your `.fsx`'s path as its only argument - * Run `Fsc` from inside VS, with will build only the dependencies you require, then will kick off compilation of your script. - * Debug your version of the compiler or whatever to see what's going on (in my case, I have a small computation expression builder which includes various custom operators, etc) - -### Interesting Jumping Off Points -* Docs (in no particular order): - * Toby's thesis - * https://github.com/Microsoft/visualfsharp/blob/master/DEVGUIDE.md - * https://github.com/fsharp/fslang-suggestions/issues/579 - * https://github.com/fsharp/fslang-suggestions/issues/36 - * https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/computation-zoo.pdf - * http://www.staff.city.ac.uk/~ross/papers/Applicative.pdf - * http://delivery.acm.org/10.1145/2980000/2976007/p92-marlow.pdf - * https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions - * https://fsharp.github.io/2015/09/29/fsharp-compiler-guide.html - * https://fsharp.github.io/2016/09/26/fsharp-rfc-process.html -* The `MatchBang` implementation pull request - https://github.com/Microsoft/visualfsharp/pull/4427 -* `TcComputationExpression` in `TypeChecker.fs` -* `LetOrUseBang` usages -* `fsharpqafiles` - how does this all get picked up for running the tests? - -## 2018-09-04 -Explored the compiler, documentation and surrounding literature. - -[Tomas Petricek's "The F# Computation Expression Zoo"](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/computation-zoo.pdf), linked by Don Syme in the `let! ... and! ...` fslang suggestion is very helpful because it defines much of the core of the change very clearly (see part 3, "Semantics of computation expressions", and part 4.4). Interestingly, Petricek seems to prefer the less common "Monoidal" definition of applicatives (see Part 4 of McBride & Patersons's ["Applicative programming with effects"](http://www.staff.city.ac.uk/~ross/papers/Applicative.pdf) and subsequent [explanations by people on the internet](https://argumatronic.com/posts/2017-03-08-applicative-instances.html)) that defines `map` and `merge` rather than `pure` and `apply`. I am starting to wonder now... should we allow `let! ... and! ...` when the user has only defined bind? I suppose it'd allow for future implementations of `pure` & `apply` / `map` & `merge` to make it more efficient (providing the relvant laws relating monads and applicatives hold). - -So now we have prior art for some of both the theory (papers from Petricek and McBride & Patterson on applicatives) and the practice (`match!` pull request, existing `zip` custom operation, Petricek's - rejected! - earlier work on joinads). - -Questions: -* Why does Petricek prefer the monoidal (semigroupal?) definition of applicatives? What different does it make which we pick? - * `merge : f a * f b -> f (a*b)` which is a bit odd because it involves creating a tuple, which doesn't seem very elegant (does it imply allocating a tuple for each application of a value?!). Previously I was wondering about `liftA2 : (a -> b -> c) -> f a -> f b -> f c`, although that wouldn't require `map` since `let map f x = merge (<|) (pure f) x` by that definition, so the `map` would just be optional as an optimisation, just as adding both `merge` and `map` could be an optimisation for when we already have `bind`. I guess we don't have curried functions when defining builders? I'll have to check. -* Why was Petricek's work on joinads rejected by Don Syme? Am I at risk of falling into the same trap? -* Should we allow mixing applicatives and monads, as long as the rules that no RHS of an apply is the LHS of a `let!`? -* What should we do if someone tries to use `let! ... and! ...` if the builder only defines `Bind`? -* Is using `map` and `merge` perferable because we get `map` which gives an optimisation for some usages of existing monadic CEs? -* `map` & `merge` / `apply` vs. `zip`? - -Standard place for tests (see `match!` PR) seems to be: https://github.com/Microsoft/visualfsharp/tree/master/tests/fsharp/core -`match!` commit: https://github.com/Microsoft/visualfsharp/commit/e33831a596bc2858c9311280212b559318272ee4 - -### Next Up -* Answer questions from today (I've already made a post to #langdesign) -* Try to understand the `match!` commit by reading/debugging -* Trying to change the `let!` syntax, e.g. to `foo!` and see it work - -## 2018-09-05 -Still waiting on a response on #langdesign. - -Good news! The [RFC process](https://fsharp.github.io/2016/09/26/fsharp-rfc-process.html) isn't very heavyweight. - -After reading the [Extend ComputationExpression builders with Map](https://github.com/fsharp/fslang-design/issues/258) discussion, I am wondering how `pure` will work. - -Parts of compiler to explore following on from the `match!` PR: -* src/fsharp/LexFilter.fs -* src/fsharp/TypeChecker.fs -* src/fsharp/ast.fs -* src/fsharp/lex.fsl -* src/fsharp/pars.fsy -* src/fsharp/service/ServiceLexing.fs -* src/fsharp/service/ServiceParseTreeWalk.fs -* src/fsharp/service/ServiceUntypedParse.fs - -### Messing with the `match!` syntax - -Interesting existing annoyance, no idea if it's easily fixable: I tried to use a `match!` to do some `printf`-ing, but got the "You haven't defined `Zero`" error. Okay, so I implement that, then I get the same for `Combine`. I'd have liked to have had all of the errors at once to avoid multiple build & run iterations. - -...Now I need a `Delay` too! 😄 - -Managed to mangle the new `match!` syntax to use `letmatch!` on a branch. - -```fsharp -let x = Some 1 -let y = opt { return "A" } -let z = Some 3.5 - -let foo : string option = - opt { - let! x' = x - letmatch! y with - | yValue -> printfn "y was set to %s!" yValue - let! z' = z - return sprintf "x = %d, z = %f" x' z' - } -``` - -The semantics are iff `y` is `None` then bomb out of the CE with `None` else continue down the CE, printing the "y was set..." stuff to `stdout`. - -### Have a go at adding a shonky `let! ... and! ...` - -Arbitrary list of things to do / remember: -* Update AST (not sure how the AST/TAST handle custom operators and built-in CE methods yet) -* Update TAST -* Add `and!` to the frontend -* Add logic for grabbing `.Apply` off the given builder and validating its signature etc (won't both with `.Map` or `.Merge` at this point because that's yet more functions to have to wire up, validate, etc) -* Should there be an `andUse!` or something? What usecases could there be? What would that look like? - -What I've done so far makes `and!` only valid within a `let!`: -```fsharp - [] - SynAndBangExpr = - /// AndBang(isUse, bindings, body, wholeRange) - /// - /// F# syntax: and! pat = expr in expr (Must follow on directly from a let! / and!) - | AndBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range - | NotAndBang of SynExpr -and - [] - SynExpr = - -(* ... *) - - /// SynExpr.LetOrUseBang(spBind, isUse, isFromSource, pat, rhsExpr, bodyExpr, mWholeExpr). - /// - /// F# syntax: let! pat = expr in expr' - /// F# syntax: use! pat = expr in expr' - /// where expr' admits an immediate and! - /// Computation expressions only - | LetOrUseBang of bindSeqPoint:SequencePointInfoForBinding * isUse:bool * isFromSource:bool * SynPat * SynExpr * SynAndBangExpr * range:range -``` - -This is kind of what we want, but its type safety is actually a bit annoying - now `and!`s are a different expression type, and the normal functions, e.g. to test whether something is a control-flow expression, doesn't directly apply to it. I feel like this will need revisiting - will we want a `let!` followed by a list of `and!`s (, i.e. the string "and!" could be like an expression list separator - see `listPatternElements` in `pars.fsy`, started with a "let!" and terminated with a "return". If the list is non-empty => its an applicative expression)? - -## 2018-09-06 - -I am trying to work off `type ... and ...` and `let ... and ...` to work out how to make this fit `let!`. Looks like there some logic in `lexfilter.fs` for mapping from light to verbose syntax that I'll want to understand well. - -### Light And Verbose Syntax - -Why, CE section of the parser, do we have a rule for `BINDER` when `BINDER`s should have been mapped to `OBINDER`s by `lexfilter.fs` - -I should probably get to grips with fslex and fsyacc in a more principled way: -http://courses.softlab.ntua.gr/compilers/2015a/ocamllex-tutorial.pdf -http://courses.softlab.ntua.gr/compilers/2015a/ocamlyacc-tutorial.pdf - -> tomd [2:12 PM] -Question about parsing `let!`: -Is `lexfilter.fs` mean to transform all instaces of `BINDER` to `OBINDER`? I would have naively assumed light syntax works by mapping first to verbose syntax. Yet, `pars.fsy` still seems to have rules for `BINDER` - why is this? - -> dsyme [2:13 PM] -@tomd use of light syntax is optional, so BINDER is used for `#light "off"` - -> tomd [2:15 PM] -Okay, so in `lexfilter.fs` why do we map some `BINDER`s to `OBINDER`s? Is the light syntax the canonical one, in some sense? - -> dsyme [2:16 PM] -Yes, the syntaxes are effectively separate. e.g. we could split pars.fsy into two parsers - one for `#light on` (the default, containing `OBINDER`) and one for #light off (containing `BINDER`) - -### High-Level Questions - -* Should `anduse!` be a thing? If so, what should its final name be? -* How should the ranges work for `let! ... and! ...` groups? For each binding range over the full chain of bindings and the final body? I'd kind of like just the current binding and the body, but ranges have to be continuous - it's baked in pretty deeply. I think I'll just do what let does and accept that it looks like earlier `and!`'s bindings are in scope of later ones. - -### Low-Level Questions - -#### Parsing complexity vs. AST complexity - -The `binders` non-terminal awkwardly contains the final body with a `let!` rather than waiting until after the zero-or-more `and!`s. This makes creating the range more awkward. On the other hand, that keeps the structure of the AST largely as it is, whilst still preventing more `let!`s half way through. I think keeping the AST as it is so we don't get a new `and!` expression type wins here. - -> Toby Shaw [6:57 PM] -I had naively only thought about the other way, but I like your idea more -catches the error in parsing, which is way nicer - -> tomd [6:57 PM] -Yep! - -> Toby Shaw [6:58 PM] -the only problem is that you might find it harder to give a descriptive error message - -> tomd [6:58 PM] -Shoe horning the latter way into parsing is hard too -Yeah, I am no where near thinking about that too hard though -Why do you say that? - -> Toby Shaw [7:04 PM] -the parser has auto-generated error messages, I think -Whereas if you throw the error in type checking, then you can choose the error message - -#### What is allowed to follow a `let! ... and! ...`? - -It's probably worth giving some thought to what should be allowed to come after a `let! ... and! ...` chain. Only allowing `return` might be one simple way to get started? - -## 2018-09-07 - -[Useful description](https://fsprojects.github.io/FSharpPlus/computation-expressions.html) of the various kinds of `monad`, as F# sees things. - -The first thing I want to have work it the `option` applicative, which I think is a strict (no delaying required), monad-plus (plus in this case means take the first `Some` value) CE. - -### Question to #general: - -> tomd [2:43 PM] -When writing a computation expression builder, is the choice in which of `Yield` and `Return` to implement purely down to whether you want the CE to make the `yield` or `return` keyword available? I can't really imagine a world where they're have different implementations (since they'd imply different monad instances, right?) -> -> The context of this question is working out how `pure` would work for `let! ... and! ...` computation expressions (I think `apply` is entirely new, so no prior art to decipher in that case). -> -> tomasp [11:10 PM] -@tomd I think one reason was that `let! a = x and b = y` maps pretty directly to `Merge`, so the translation is simpler -and it also works nicely if you have something that is also a monad, because then you can translate this: - -```fsharp -m { - let! a = x - and b = y - let! c = z - return a + b + c } -``` - -> into this: - -```fsharp -m.Bind(m.Merge(x, y), fun (a, b) -> - m.Bind(z, fun c -> - m.Return(a + b + c) ) ) -``` -> For reference, if you do not have `Bind` and have just an applicative, you will not be able to do the second `let! c = z`. You can only write, say: - -```fsharp -m { - let! a = x - and b = y - return a + b } -``` - -> which can be translated using just `Map`: - -```fsharp -m.Map(m.Merge(x, y), fun (a, b) -> a + b) -``` - -### Choosing `return` vs. `yield` - -Assuming the difference is in which keyword becomes available inside te CE, as person writing a CE, I supppose I'd use `return` for plain old applicatives and `yield` for [alternative applicatives](https://hackage.haskell.org/package/base-4.6.0.1/docs/Control-Applicative.html#t:Alternative), and so on that basis, I think my change should use, say, support `return` initially and `yield` later (when the CE builder writer has defined `Combine`, which is really the alternation operator). -Alternative applicative support via `let! ... and! ...` and `yield` keywords would make writing things with alternatives, e.g. an argument parser, both efficient (no rebuilding the compuation on every application) and convenient (syntax is lightweight and readable). - -### A Note on Alternation -`Combine`, the alternation method, is called when sequencing CE expressions. e.g. for options, we can pick the first in the `Some` case by just listing them inside the CE and providing a left-biased `Combine` method on the builder. - -### Further uses of `Delay` -Note from @Nick: `Delay` is useful even in non-side-effectful CE builders as a way to "hide" evaluating a CE behind a thunk - useful when building the CE itself is expensive. - -### Desugaring - -Proposal for desugaring, a la [the existing CE docs](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions): - -| Method | Typical Signature(s) | Description | -|---------|-----------------------------|-------------| -| `Apply` | `M<'T -> 'U> * M<'T> -> M<'U>` | Called for `let! ... and! ...` and computation expressions. Allows certain operations that do not require the full power of bind to be performed potentially more efficiently, or where they do not even have a sensible bind operation (but to have an apply) to be performed at all. - -| Expression | Translation | And using operators... | -|-------------------------------|-------------|------------------------| -| `let! pattern1 = expr1 and! pattern2 = expr2 and! ... in cexpr` | `builder.apply(builder.apply(builder.apply(fun expr1 expr2 ... -> cexpr, expr1), expr2), ...)` | `cexpr <*> expr1 <*> expr2 <*> ... <*> exprN` - -Given the above, I think it would _not_ require extra work to allow all of the other usual CE keywords to work alongside `let! ... and! ...`, since the desugaring only requires the context of what `and!`s correspond to what `let!`s and otherwise doesn't interact, which (right now) is easily done in the parser/AST. - -This also means we can add optimisations to reduce current desugarings which use `Bind` to use the least powerful that applies of `Map`, `Apply` and `Bind`. Even in a world where these magic optimisations exist (where `let! ... and! ...` syntax wouldn't strictly be necessary for `Apply` to be called, the syntax would be handy because it would be a means for the CE user to assert "I expect this to be possible with only `apply`, I do _not_ want to be using `Bind` here", e.g. because of the runtime cost or because I don't want to care about whether or not `Bind` even exists on this builder). - -## 2018-09-10 - -I don't think we need `in` between `let!`s and `and!`s in the same chain, because any later `and!` in unambiguously part of the chain (a new chain would need to be started with a fresh `let!`). EDIT: That was clearly rubbish, one example of that being wrong is a `let!` nested in another `let!`. Which does the following `and!` belong to without the offside rule or `in`? Seems ambiguous to me! - -### Should we desugar to `Bind`, `Apply` or `Map`? - -Right now, we can only ever desugar `let!` to a `Bind`. This isn't ideal because `Bind` can be expensive and can make statically examining the structure of the full computation impossible (the can't know what will happen "behind a bind" until a value is present for us to use to generate the subsequent computation). - -If `Apply` is available on the builder, we can use it as a (potentially) more efficient alternative for `Bind` where the RHS of a `let!` does not make reference to the LHS of a previous `let!`. In a sense, it allows us to bind multiple names which are not interdependent simultaneously. It also allows us to apply functions on the LHS of a `let!` to values on the LHS of a `let!` without `Bind`. This is useful because implementing `Bind` isn't possible for as many interesting structures as for `Apply`. - -Similarly, if `Map` is available, we can use is as an alternative to `Apply` and `Bind` if we take the value on the LHS of a `let!` and transform it with reference to the LHS of any other `let!` at any point. Again, `Map` is possible to implement for more structures than both `Bind` and `Apply`, and it is potentially more efficient to use too. - -`Bind`, `Apply` and `Map` are related via various laws that govern how they "should" behave - i.e. we expect any "sensible" CE builder writer will produce methods for which these hold. For example, `builder.Bind(fun x -> builder.Return(f x))` and `builder.Apply(builder.Return(f), x)` should both produce the same result as `builder.Map(f, x)`. - -The most minimal change we could make is to just make `let! ... and! ...` desugar to `Apply` as defined earlier. This makes CE builders useful for strutures for which we can't or haven't defined `Bind`. It also allows the more efficient `Apply` to be used even where `Bind` is defined, and potentially allows more static examination of the resulting value. - -Beyond that, we could start more aggressively using the most constrained (and hence generally most efficient) desugaring we can. e.g. even if the CE writer does not explicitly use `let! ... and! ...`, we may notice that no LHS of a `let!` is used on the RHS of another, so we can desugar to `Apply` instead. If the laws hold, this would probably create a more efficient program which behaves identically, however, _it will do something different to the `Bind` desugaring if the laws don't hold_! e.g. If `Apply` calls `System.Environment.Exit()` but `Bind` does not, the program will do something drastically different. As such, the more aggressive, optimising desugarings are potentially dangerous so we probably want to consider these separately to the `let! ... and! ...` proposal. Interestingly, however, if a CE writer was using a builder which defined `Bind` but not `Apply` or `Map`, and later the builder was updated to implement `Apply` and `Map` in a way that held with respect to the laws, then without a change of their source code, they could get some new optimisations after recompiling. - -Even in a world with these auto-magical, optimising desugarings, having the `let! ... and! ...` syntax is still useful, since it offers a way for the CE writer to declare "I expect this should be possible with `Apply` and therefore do not require `Bind`, so please verify that is true and use only `Apply`". This makes it possible to be confident that the desugaring will avoid using `Bind` e.g. because it is expensive. - -Assuming we used the full suite of optimising desugarings (i.e. using `Map` in preference to `Apply`, in preference to `Bind` when possible), we would get this: - -| Expression | Translation | Optimisation | -|-------------------------------|-------------|--------------| -|`let! pat1 = exp1 and! pat2 = exp2 in let! pat3 = exp1 in return pat1 + pat2 + pat3`| `builder.Apply(builder.Apply(builder.Apply(builder.Return(fun pat1 pat2 pat3 -> pat1 + pat2 + pat3), exp1), exp2), exp3)` | Use `Apply` rather than `Bind` despite final binding not being part of preceding `and!` chain. -| `let! x = y in return x + 1` | `builder.Map((fun x -> x + 1), y)` | Use `Map` rather than `Bind` or `Apply` despite `let!` - -### `liftA2` vs. `apply` vs. `merge` - -There's already been plenty of chat about the `apply` vs. `merge` way of doing things, but some people also claim `liftA2` is easier to write (`apply` often prompts the question "why would I end up with a function in my functor" and then people get hung up on that). - -> From Hackage: -> `liftA2 :: (a -> b -> c) -> f a -> f b -> f c` -> -> Lift a binary function to actions. -> -> Some functors support an implementation of `liftA2` that is more efficient than the default one. In particular, if fmap is an expensive operation, it is likely better to use `liftA2` than to fmap over the structure and then use `apply`. We can also look at `liftA2` are a generalised `Merge` that avoids arbitrarily creating a tuple. With `liftA2` we can nicely introduce it along the lines of "it's like `map`, but where we can have 2 parameters". Or maybe that obscures the essence of the contribution an applicative provides over a functor? - -Comparing `liftA2` and `apply` because `merge` introduces a tuple, which I find arbitrary and inelegant. - -```fsharp -val liftA2 : ('a -> 'b -> 'c) -> f 'a -> f 'b -> f 'c -``` -```fsharp -val apply : f ('a -> 'b) -> f 'a -> f 'b -``` -```fsharp -val merge : f 'a -> f 'b -> f('a * 'b) -``` - -Imagine a simple but presumably represenative example CE: -```fsharp -option { - let! a = aExpr - and! b = bExpr - and! c = cExpr - return a + b + c // We're explicitly returning here (i.e. calling `pure`, so this neatly maps into the `apply` desugaring - liftA2 presumes the function is unwrapped, so we'd be passing in (<|) to apply the lambda doing the summation anyway) -} -``` - -```fsharp -builder.Apply( - builder.Apply( - builder.Apply( - builder.Return(fun a b c -> a + b + c), - aExpr), - bExpr), - cExpr) -``` - -And with some extra body in the middle: -```fsharp -option { - let! a = aExpr - and! b = bExpr - and! c = cExpr - let d = 3 + 4 - return (a + b + c) * d -} -``` -I presume it would desugar (have yet to confirm this is how `let`s work yet) as so: -```fsharp -builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - fun a b c -> - let d = 3 + 4 - (a + b + c) * d), - aExpr), - bExpr), - cExpr) -``` - -I assert `Apply` is the best way because the desugaring is most natural. The only unintuitive thing, perhaps, is that the first binding goes into the inner most `Apply` and we work outwards from there, which is the reverse of how you read the top-to-bottom CE. - -### Answers from @dsyme - -Why was joinads rejected? It made various things significantly more complicated, e.g. pattern matching. - -`Map` can be added as an efficient desugaring of `let! x = y in return x + 1`, but `Map` will need an attribute on it to indicate the backwards incompatible optimisation is allowed by this builder. - -Smart CE builder optimisations, such as swapping out simple `Apply` usages for `Map` where possible, etc, won't be accepted because it'll make the "simple" desugaring not so simple. - -`Map` & `Merge` vs. `Pure` & `Apply` vs. `Pure` & `LiftA2`: The choice is largely arbitrary, pick whichever. - -### Decision Record - -* Nothing to worry about from the rejection of the joinads proposal - this change does not introduce the level of complexity that caused the issues in that case. - -* I will use `apply` because I am most familiar with this encoding and I think it desugars very clearly. - -* I will avoid `map` altogether - there is a separate suggestion and RFC for that and given @dsyme's points about not wanting more magic in the desugaring (e.g. using `apply` to implement `map` if `map` is not given explicitly, etc), these two changes should be orthogonal (although my work here is related in the sense that the skills needed to do one are very much like the skills needed to do the other). - -## 2018-09-11 - -Beyond making `let! ... and! ...` appear to work, I'll need to: -* Come up with a comprehensive set of tests -* Preempt some common errors and make sure the messages are and least somewhat helpful -* Think about how to update the CE docs on MSDN and whatever hover-over tips VS gives, etc -* Explore the debugging, step-through experience -* Explore the "find definition" experience -* Chase down "The result of this equality expression is discarded..." -* Make sure both light and verbose syntax look sensible - -Still trying to make the syntax parse properly. I suspect this is to do with the different syntaxes: https://fsharpforfunandprofit.com/posts/fsharp-syntax/ - -From the debugging: `LET: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n` when filtering `BINDER`. How does thhis need to change to accomodate `and!`? Need to explore `CtxtLetDecl` to get started. - -Okay, I think this: -```fsharp - // let! ... ~~~> CtxtLetDecl - | BINDER b, (ctxt :: _) -> - let blockLet = match ctxt with CtxtSeqBlock _ -> true | _ -> false - if debug then dprintf "LET: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n" blockLet outputPos tokenStartPos - pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos)) - returnToken tokenLexbufState (if blockLet then OBINDER b else token) -``` -is about runs of let bindings preserving the light syntax. `SeqBlock` is the name for a series of let-bindings in a row? - -By the looks of it, the corollery is that I probably do want to support light and verbose syntax, and then I can largely copy the above lex filter stuff for `and!`. - -[The spec](https://fsharp.org/specs/language-spec/4.0/FSharpSpec-4.0-latest.pdf) covers offside contexts (section 15.1.6, page 282). - -> The `SeqBlock` context is the primary context of the analysis. It indicates a sequence of items that must be column-aligned. Where necessary for parsing, the compiler automatically inserts a delimiter that replaces the regular `in` and `;` tokens between the syntax elements. - -Does `isLetContinuator` need `AND_BANG` and `OAND_BANG` adding to it? I think no, else we keep the offside position from the first `let!`? - -Editor index lines and columns from 1. Compiler debug output indexs from 0. Consider putting in an issue for this bjorn and chethusk seem to be in favour. - -For posterity, I am currently invoking the compiler as so: -`fsc.exe --parseonly --debug+ --debug-parse SimpleBuilder.fsx` - -Once theory I have about what I was doing wrong was having `and!` mess with the offisde rule. Maybe it should work just like `and` does? That seems sensible intutively modulo that `and!`s exist "inside" the `let!` that precedes it. EDIT: Either way, my logic fails, so I guess the error is somewhere else in the lexer or the subsequent filtering. Is there a way to just print the tokens post-filtering? - -`--tokenize` seems to be working okay. From what I can see, an `OBLOCKEND` is inserted before the `and!`. Is that correct? - -``` -offside token at column 8 indicates end of CtxtSeqBlock started at (34:12)! -<-- popping Context(seqblock(subsequent,(34:12))), stack = [let(true,(33:8)); seqblock(subsequent,(33:8)); paren; vanilla((32:4)); - seqblock(subsequent,(32:4)); let(true,(31:0)); seqblock(subsequent,(2:0))] -end of CtxtSeqBlock, insert OBLOCKEND -inserting OBLOCKEND -``` - -Looks like we get as far as reading the identifier after the `and!` too: -``` -AND!: entering CtxtLetDecl(blockLet=true), awaiting EQUALS to go to CtxtSeqBlock ((35:8)) ---> pushing, stack = [let(true,(35:8)); seqblock(subsequent,(33:8)); paren; vanilla((32:4)); - seqblock(subsequent,(32:4)); let(true,(31:0)); seqblock(subsequent,(2:0))] -tokenize - got OAND_BANG @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(36,8)-(36,12) -tokenize - getting one token from scratch\data\SimpleBuilder.fsx -popNextTokenTup: no delayed tokens, running lexer... -popNextTokenTup: no delayed tokens, running lexer... -popNextTokenTup: delayed token, tokenStartPos = (35:15) -tokenize - got IDENT @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(36,13)-(36,14) -tokenize - getting one token from scratch\data\SimpleBuilder.fsx -popNextTokenTup: delayed token, tokenStartPos = (35:15) -CtxtLetDecl: EQUALS, pushing CtxtSeqBlock -popNextTokenTup: no delayed tokens, running lexer... ---> insert OBLOCKBEGIN ---> pushing, stack = [seqblock(first,(36:12)); let(true,(35:8)); seqblock(subsequent,(33:8)); paren; - vanilla((32:4)); seqblock(subsequent,(32:4)); let(true,(31:0)); - seqblock(subsequent,(2:0))] - ``` - - Then the rest, which seems to agree with what I see: - ``` - pushing CtxtVanilla at tokenStartPos = (37:8) -tokenize - got YIELD @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,8)-(38,14) -tokenize - getting one token from scratch\data\SimpleBuilder.fsx -popNextTokenTup: no delayed tokens, running lexer... -popNextTokenTup: no delayed tokens, running lexer... -popNextTokenTup: delayed token, tokenStartPos = (37:23) -tokenize - got IDENT @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,15)-(38,22) -tokenize - getting one token from scratch\data\SimpleBuilder.fsx -popNextTokenTup: delayed token, tokenStartPos = (37:23) -tokenize - got STRING @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,23)-(38,39) -tokenize - getting one token from scratch\data\SimpleBuilder.fsx -popNextTokenTup: no delayed tokens, running lexer... -popNextTokenTup: no delayed tokens, running lexer... -popNextTokenTup: delayed token, tokenStartPos = (37:42) -tokenize - got IDENT @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,40)-(38,41) -tokenize - getting one token from scratch\data\SimpleBuilder.fsx -popNextTokenTup: delayed token, tokenStartPos = (37:42) -popNextTokenTup: no delayed tokens, running lexer... -popNextTokenTup: delayed token, tokenStartPos = (38:4) -tokenize - got IDENT @ C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx(38,42)-(38,43) -tokenize - getting one token from scratch\data\SimpleBuilder.fsx -popNextTokenTup: delayed token, tokenStartPos = (38:4) -IN/ELSE/ELIF/DONE/RPAREN/RBRACE/END at (38:4) terminates context at position (37:8) -``` - -So is the offside stuff adding/missing something weird that I've not noticed, or is the parsing logic not doing what I expect? - -One conflict: `isLetContinuator` currently lets the `and!` be "inside" the `let!`, but the parser expects `hardwhiteDefnBindingsTerminator`, i.e. `ODECLEND`! - -How much of a problem is it that `let! ... and! ...` is using right-recursion? (We seem to use it for `and`...) - -Okay, I think it is time to take a step back. The easiest thing to make `and!` is presumably to emulate an existing construct. Up to and excluding parsing, `and!` works like `and` (after that there's a difference because, semantically, the `and!` is very much a part of the larger `let!` construct). - -NFO `OAND_BANG` - there's no `OAND`! - -One day lost to `build.cmd` not actually building everything. From what I can tell, you *must* use VS or msbuild to rebuild some artefacts. - -## 2018-09-13 - -Parsing seem to at least accept my examples. Working on type checking now. One of the core issue right now if when to call `trans` and `translatedCtxt` on desugared CEs. - -...which may, in turn, be due to me not updating `varSpace` correctly. - -16.3.10 of the spec seems to correspond to this, so time for some bookwork! - -Looks like I forgot to insert a call to `Return` between creating the lambdas that introduce the newly-bound names and calling `Apply`. - -My example almost works now, in that it builds and runs, but I accidentally doubly wrap the result of the builder in the functor. I think the explicit `Return` the user gives needs to be "moved" to outside the lambdas, as opposed to kept and a "new" `Return` added between them and the calls to `Apply`. The solution is to think about precisely how the desugaring should work a bit more. - -## 2018-09-17 - -I've been thinking about the slight annoyance of the desugaring that happens in `let! ... and! ...`, specifically, how the `Return` gets "lifted" to cover the whole block below the last `and!`, including whatever expression is handed to `Return`. The tricky examples I can think of right now are: - -### Just moving `Return` to cover the lambda that represents everything after the last `and!` is hard enough! - -```fsharp -option { - let! (a,_) = aExpr - and! (_,b) = bExpr - and! (SingleCaseDu c) = cExpr - let d = 3 + 4 - return (a + b + c) * d -} - -// desugars to: - -let aExprEvaluated = aExpr -let bExprEvaluated = bExpr -let cExprEvaluated = cExpr -let d = 3 + 4 // Code outside the `Return` does not have names bound via `let! ... and! ...` in scope -builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (_,b) -> - (fun (SingleCaseDu c) -> - (a + b + c) * d)))), - aExprEvaluated), - bExprEvaluated), - cExprEvaluated) -``` - -### What if the user calls `Yield` more than once? Do we disallow it? If we did allow it, what would the semantics/desugaring be? (This corresponds to "alternative applicatives", I think). Follow ups: - * What if an active pattern appears on the LHS - do we call it up to (number of `yield` occurances) times? - * What if `Return` isn't defined? Should we support semi-groups as well as monoids? - -```fsharp -option { - let! (a,_) = aExpr - and! (_,b) = bExpr - and! (SingleCaseDu c) = cExpr // Can the pattern be an active pattern? Does that mean it's potentially called once per occurance of `yield` below? - yield (a + b + c) // `yield` implies alternation, i.e. a monoid (maybe we should support semi-groups too?) - yield (b + 1) - yield (a + c) -} - -// desugars to: - -let aExprEvaluated = aExpr -let bExprEvaluated = bExpr -let cExprEvaluated = cExpr - -let alt1 = - builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (_,b) -> - (fun (SingleCaseDu c) -> - (a + b + c))))), - aExprEvaluated), - bExprEvaluated), - cExprEvaluated) - -let alt2 = - builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (_,b) -> - (fun (SingleCaseDu c) -> - (b + 1))))), - aExprEvaluated), - bExprEvaluated), - cExprEvaluated) - -let alt3 = - builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (_,b) -> - (fun (SingleCaseDu c) -> - (a + c))))), - aExprEvaluated), - bExprEvaluated), - cExprEvaluated) - -builder.Combine( - builder.Combine(alt1,alt2), - alt3) -``` - -Then thinking about more code outside of `yield` and `return` everythere it could conceivably be valid: - -```fsharp -option { - let! (a,_) = aExpr - and! (b,b2) = bExpr - and! (SingleCaseDu c) = cExpr - - let n = 7 - yield (a + b + c + n) - let m = 100 - yield (b + m) - let o = -1 - yield (a + b2 + c + n + m + o) -} - -// desugars to: - -let aExprEvaluated = aExpr -let bExprEvaluated = bExpr -let cExprEvaluated = cExpr - -let n = 7 - -let alt1 = - builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (b,b2) -> - (fun (SingleCaseDu c) -> - (a + b + c + n))))), - aExprEvaluated), - bExprEvaluated), - cExprEvaluated) - -let m = 100 // By `let` binding here, we keep the expecting scoping and ordering of side-effects - -let alt2 = - builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (b,b2) -> - (fun (SingleCaseDu c) -> - (b + m))))), - aExprEvaluated), - bExprEvaluated), - cExprEvaluated) - -let o = -1 - -let alt3 = - builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (b,b2) -> - (fun (SingleCaseDu c) -> - (a + b2 + c + n + m + o))))), - aExprEvaluated), - bExprEvaluated), - cExprEvaluated) - -builder.Combine( - builder.Combine(alt1,alt2), - alt3) - -/// Where `alt1`, `alt2`, `alt3` are just fresh variables -``` - -### Nesting of applicative CEs & `yield!` / `return!` - -```fsharp -option { - let! (a,_) = aExpr - and! (b,_) = bExpr - - yield a - - let n = 7 - - yield! ( - option { - let! c = cExpr - and! d = dExpr - return (b + c + d + n) - }) -} - -// desugars to: - -let aExprEvaluated = aExpr -let bExprEvaluated = bExpr - -let alt1 = - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (b,_) -> - a))), - aExprEvaluated), - bExprEvaluated) - -let n = 7 - -let alt2 = - builder.YieldFrom( // What would a sensible implementation be except `id`? - let cExprEvaluated = cExpr - let dExprEvaluated = dExpr - builder.Apply( - builder.Apply( - builder.Return( - (fun c -> - (fun d -> // TODO Where on earth does `b` get applied in?! - b + c + d + n))), - cExprEvaluated), - dExprEvaluated)) - -builder.Combine(alt1, alt2) -``` - -### `let!` when `Apply` and `Return` are defined, but not `Bind` - -**N.B. This is highly related to the `Map` RFC for `let! ... return ...`** - -_If_ we chose to implement `Map` from `Apply` (in order to make a single `let!` for when `Apply` and `Return` are defined but not `Bind`), it could look like this: - -```fsharp -option { - let! (a,_) = aExpr - let d = 3 - return (a * d) -} - -// desugars to: - -let aExprEvaluated = aExpr -let d = 3 -builder.Apply( - builder.Return( - (fun (a,_) -> - (a * d))), - aExprEvaluated) - -// This desugaring is just a corollary of the earlier, more complex desugarings -``` - -My gut says we should use `Map` if it is available, else `Apply`, else `Bind`. - -### Active Patterns - -```fsharp -let mutable spy = 0 - -let (|NumAndDoubleNum|) (x : int) = - spy <- spy + 1 - (x, x + x) - -option { - let! (a,_) = aExpr - and! (NumAndDoubleNum(b, doubleb)) = bExpr - - yield a + doubleb - - let n = 7 - - yield a + b + n -} - -// desugars to: - -let aExprEvaluated = aExpr -let bExprEvaluated = bExpr - -let alt1 = - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (NumAndDoubleNum(b, doubleb)) -> - a + doubleb))), - aExprEvaluated), - bExprEvaluated) - -let n = 7 - -let alt2 = - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (NumAndDoubleNum(b, doubleb)) -> - a + b + n))), - aExprEvaluated), - bExprEvaluated) - -builder.Combine(alt1, alt2) - -// spy = 2, is this unacceptably surprising? -``` - -### Overall concepts for the full desugaring - -One claim might be: _We should evaluate the RHS of each of the bindings only once_ - i.e. don't naively stamp out the expression n-times for every alternative implied by a `yield`. Although pattern matching has to happen for each `yield`, since until we are inside the `yield`'s lambda, we only have a functor in hand, and can't inspect the contents directly. This is only interesting since: pattern matching implies some runtime cost, and active patterns can have side-effects (arguably, that's enough of an abuse that it doesn't matter too much, but I don't like that it is perhaps surprising given the syntax of the CE). - -Another perspective is that by "factoring out" the bindings, we've actually done away with some of the notion that the application implied by each `return` or `yield` is orthogonal, and it's everything else which is the exception, and active patterns are the ones following the rules and doing what is expected. From that perspective, `let! ... and! ...` can be simply considered as each `yield` and each `binding` happening separately/in parallel - of course that does make it easy to build something which looks terse but implies a very large computation (not that we can't do that already...). - -Might need to consult the literature on this one, although the introduction of side-effects might be what is taking us off-piste. - -#### "Commonising" approach - -Potentially more efficient. Side-effects from active patterns happen N times, but normal `let`s and evaluation of the RHS of the `let! ... and! ...` happens only once. One could argue that, apart from the side-effect of active patterns, this more closely agrees with the syntax. - -```fsharp -let mutable spy = 0 - -let (|NastySideEffectfulNumAndDoubleNum|) (x : int) = - spy <- spy + 1 - (x, x + x) - -option { - let! (a,_) = aExpr - and! (NastySideEffectfulNumAndDoubleNum(b, doubleb)) = bExpr - - yield a + doubleb - - let n = nastySideEffectfulComputation () - - yield a + b + n -} - -// desugars to: - -let aExprEvaluated = aExpr -let bExprEvaluated = bExpr - -let alt1 = - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> - a + doubleb))), - aExprEvaluated), - bExprEvaluated) - -let n = nastySideEffectfulComputation () - -let alt2 = - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> // N.B. That the RHS of the bindings are each evaluated once, but the pattern match is done once _per yield_ - a + b + n))), - aExprEvaluated), - bExprEvaluated) - -builder.Combine(alt1, alt2) - -// spy = 2, is this unacceptably surprising? -``` - -#### "Duplication" approach - -Evaluation of names bound in the `let! ... and! ...` happens once for each yield (so potentially a lot of times!), but at least this agrees with the number of times the active pattern is called. One could argue that things happening for each `yield` is surprising given the syntax, but perhaps the fact that `and!` is there is enough to signal that we've shifted to a different "mode". I think this approach is simpler to implement and reason about. - -Prior art in loops: If a function with side-effects exists in a loop, we don't automagically pull it out so it's only called once! Yes, you can shoot your foot off by leaving something nasty in a hot loop, but you can also factor it out and save yourself. At least it's clear what is going to happen. - -```fsharp -let mutable spy = 0 - -let (|NastySideEffectfulNumAndDoubleNum|) (x : int) = - spy <- spy + 1 - (x, x + x) - -option { - let! (a,_) = aExpr - and! (NastySideEffectfulNumAndDoubleNum(b, doubleb)) = bExpr - - yield a + doubleb - - let n = nastySideEffectfulComputation () - - yield a + b + n -} - -// desugars to: - -let alt1 = - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> - let n = nastySideEffectfulComputation () // This comes _after_ this yield. Why is it here?! - a + doubleb))), - aExpr), - bExpr) - - -let alt2 = - builder.Apply( - builder.Apply( - builder.Return( - (fun (a,_) -> - (fun (NastySideEffectfulNumAndDoubleNum(b, doubleb)) -> - let n = nastySideEffectfulComputation () // N.B. This has been duplicated in each alternative, so everything happens as many times as a `yield` occurs in the CE - a + b + n))), - aExpr), - bExpr) // N.B. Second evaluation of bExpr here - -builder.Combine(alt1, alt2) - -// spy = 2, but that should make sense if we imagine that `and!` signals, amongst other things, that each yield corresponds to another stamping out -``` - -#### "Just don't support yield" approach - -If we support only `Return` and not `Yield` inside `let! ... and! ...` compuation expressions, then this problem goes away. - -#### Rules -1. We create a structure of nested calls to `Apply` with a `Return` on the inside wrapping a lambda at every `return` or `yield` that appears in the CE. (We can create a function up-front which "wraps" any `Return` in the calls to `Apply` and bind that to a fresh variable for later calls to `Combine` if necessary, so that other scoping rules and orderings are preserved) -1. A `Map` implementation just falls out of the `Apply` desugaring we have, so I think we should keep that, but allow it to be overriden by an explicit `Map` definition that has been appropriately annotated (similarly, if there is an existing `Bind`, `Apply` needs an annotation to trump `Bind` in the case where both effectively implement `Map`). - -#### Remaining Questions -* `do!` - how do we handle that with `Apply` and `Return`, etc.? -* I won't bother implementing support for semi-groups (i.e. when there are >1 usages of `yield` but no `Zero` defined), although I haven't actually checked what happens in that case right now... -* Need to check this vs. the existing implementation for `Bind`, but I assume an applicative CE should end in exactly one `return` or `return!`, else contain >= 1 `yield` or `yield!`, and the last line of the expression is a `yield` or `yield!`. Is that explicitly handled currently? -* `Using : 'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable` i.e. kind of a `pure` and a `map` in one. Valid for use inside an `apply` as far as I can tell at first glance (in `use!` right now, we get a `Bind` with a `Using` nested directly inside it). - -## 2018-09-17 - -How does `yield` desugar into `Combine` right now? Doesn't that mean walking down the expression an doing some rewriting? Can that help us with the issues from yesterday? - -After a conversation wtih @Nick: Can we do this as our desugaring for `let! ... and! ...` in the face of `yield`: -```fsharp -option { - let! x = Some 10 - and! y = Some 20 - let n = 1 - yield x + n - let m = 2 - yield y + n + m - } - - // desugaring to: - -builder.Apply( - builder.Apply( - builder.Return( - (fun x -> // Any pattern matching happens once - (fun y -> - let n = 1 in - builder.Combine( // The slightly strange injection of Combine must surely already have corresponding logic when we're using Bind instead? Can we work from that to build the logic for the desuagring here? - builder.Yield(x + n), - let m = 2 in // Names only in scope as CE syntax would imply - builder.Yield(y + m))))), - Some 10), - Some 20) -``` - -Another way to think about this: -We're shoe-horning in a way to make it look like we have `Bind` (note the relevance of the name here!!!) -When we only have `apply`. Perhaps our syntax should be around application instead? -Which, to me, would imply a single return (which captures the thing being applied into). - -That's not the end of the world, since if, for example, you want to `yield` (i.e. pick the first computation that's in the `Some` case, in this builder example) you could write something like: - -```fsharp -let f x y z = - (((x + y) * z) + 3) % 11 - -let xOpt = Some 10 -let yOpt = Some 20 -let zOpt = Some 30 - -option { - yield - option { - let! x = xOpt - and! y = yOpt - and! z = zOpt - return f x y z - } - - yield - option { - let! x = xOpt // We already checked whether `xOpt` was `None` or not above. Why do we have to do it again? Because we haven't defined `Bind`, which is precisely what we need in order to unpack it once and bind that value to a name! - and! y = yOpt - // TODO Should we allow `let foo = bar` here? We could translate it so that it's after the `return`, but that seems a bit magical to me. `Apply` is inherently constrained, so why try to act as if it is not? - return - (let a = 4 in a + (x * y)) // Fine to put things inside the function passed to `Apply` - } - - yield - option { - let! y = yOpt - and! z = zOpt - return y + z - } -} - ``` - - Also, do we want a convenient way to call an `F<'a ->'b>` which is nicer than: - ```fsharp -option { - let! f = fOpt // fOpt : ('a -> 'b) option - and! x = xOpt // xOpt : 'a option - return f x -} - ``` - ...probably not? That seems nice enough to me. - -## 2018-09-18 - - Okay, so the above seems to approximately work now. Next steps: - * Look at error messages - * Come up with some more examples of what should / should not work (and encode as tests?) - * Look into supporting `use! ... anduse! ...` - * Justify requiring the trailing `Return` after a `let! ... and! ...` in the tye checker rather than syntax, or move it to the parser - * Put together an RFC - - Considering `Using` (I don't have a usecase for this, but I don't really want to leave this out else the feature seems weirdly incomplete): -```fsharp -disposableFoo { - use! x = xDisFoo - and! y = yDisFoo // N.B. The goal is that any let! or and! binding can we replaced with a use! or anduse! binding - anduse! z = zDisFoo - return x + y - } - - // desugaring to: - -builder.Apply( - builder.Apply( - builder.Apply( - builder.Return( - (fun x -> - builder.Using(x, fun x -> - (fun y -> // This corresponds to the and!, so doesn't generate a call to using - (fun z -> - builder.Using(z, fun z -> - x + y + z - ) - ) - ) - ) - ) - ), - xDisFoo), - yDisFoo), - zDisFoo) -``` -I've put this together by basically mapping what was done for monads onto applicatives. I've not really thought this through in terms of whether it actually solves a problem someone could have. Could just work off [ScottW's `trace` example](https://fsharpforfunandprofit.com/posts/computation-expressions-builder-part6/) for now. - -## 2018-09-19 - -One other thing I haven't considered: What happens for `and!` / `anduse!` when the "name" is `_`? - -Alternative applicatives work now. `yield!` does the alternation because of the previously discussed issues with `yield` (forces loads of inputs to be re-evaluated and makes the desugaring unclear, whereas `yield!` enables you to say precisely what to evaluate, since its exactly what you put on the RHS), the cost being mainly that you may need to open a new `opt { }` \ No newline at end of file From 0171c5d3f5515f8a7996eb1f1b3e7ed38d9661d6 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 24 Sep 2018 18:33:20 +0100 Subject: [PATCH 166/255] Add note about order of args to Apply - be consistent with bind, or with function application? --- src/fsharp/TypeChecker.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index b5cbba1a45..f8de4ec4ef 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8073,7 +8073,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let newPendingApplies = (fun consumeExpr -> printfn "Creating synthetic Apply call for pattern %+A" pat // TODO Delete - mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) >> pendingApplies + mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) >> pendingApplies // TODO Swap order of args to Apply? constructAppliesForBindings newPendingApplies remainingBindings From 706db995f08241c4f70d5381de80f129bfe4894e Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 24 Sep 2018 18:44:34 +0100 Subject: [PATCH 167/255] Fix bad merge --- src/fsharp/TypeChecker.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 61097475bd..750799ffbe 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8386,7 +8386,7 @@ and TcSequenceExpression cenv env tpenv comp overallTy m = //SEQPOINT NEEDED - we must consume spBind on this path Some(mkSeqUsing cenv env wholeExprMark bindPatTy genOuterTy inputExpr consumeExpr, tpenv) - | SynExpr.LetOrUseBang(_, _, _, _, _, _, m) -> + | SynExpr.LetOrUseAndBang(_, _, _, _, _, m, _, _) -> error(Error(FSComp.SR.tcUseForInSequenceExpression(), m)) | SynExpr.Match (spMatch, expr, clauses, _) -> From 3bc18fa76439e6a4f934a3e1b2b7f84ee72b5522 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 25 Sep 2018 13:53:17 +0100 Subject: [PATCH 168/255] Update xlf --- ...etbang-andbang-for-applicative-functors.md | 433 ------------------ src/fsharp/xlf/FSStrings.cs.xlf | 5 + src/fsharp/xlf/FSStrings.de.xlf | 5 + src/fsharp/xlf/FSStrings.en.xlf | 5 + src/fsharp/xlf/FSStrings.es.xlf | 5 + src/fsharp/xlf/FSStrings.fr.xlf | 5 + src/fsharp/xlf/FSStrings.it.xlf | 5 + src/fsharp/xlf/FSStrings.ja.xlf | 5 + src/fsharp/xlf/FSStrings.ko.xlf | 5 + src/fsharp/xlf/FSStrings.pl.xlf | 5 + src/fsharp/xlf/FSStrings.pt-BR.xlf | 5 + src/fsharp/xlf/FSStrings.ru.xlf | 5 + src/fsharp/xlf/FSStrings.tr.xlf | 5 + src/fsharp/xlf/FSStrings.zh-Hans.xlf | 5 + src/fsharp/xlf/FSStrings.zh-Hant.xlf | 5 + 15 files changed, 70 insertions(+), 433 deletions(-) delete mode 100644 scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md diff --git a/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md b/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md deleted file mode 100644 index 13bcdf9980..0000000000 --- a/scratch/FS-1062-support-letbang-andbang-for-applicative-functors.md +++ /dev/null @@ -1,433 +0,0 @@ -# F# RFC FS-1062 - Support let! .. and... for applicative functors - -The design suggestion [Support let! .. and... for applicative functors](https://github.com/fsharp/fslang-suggestions/issues/579) has been marked "approved in principle". -This RFC covers the detailed proposal for this suggestion. - -* [x] [Approved in principle](https://github.com/fsharp/fslang-suggestions/issues/579#event-1345361104) & [prioritised](https://github.com/fsharp/fslang-suggestions/issues/579#event-1501977428) -* [x] [Suggestion](https://github.com/fsharp/fslang-suggestions/issues/579) -* [ ] Implementation: [In progress](https://github.com/Microsoft/visualfsharp/pull/FILL-ME-IN) - -# Summary -[summary]: #summary - -Extend computation expressions to support applicative functors via a new `let! ... and! ... return ...` syntax. - -# Motivation -[motivation]: #motivation - -Applicative functors (or just "applicatives", for short) have been growing in popularity as a way to build applications and model certain domains over the last decade or so, since McBride and Paterson published [Applicative Programming with Effects](http://www.staff.city.ac.uk/~ross/papers/Applicative.html). Applicatives are now reaching a level of popularity within the community that supporting them with a convenient and readable syntax, as we do for monads, makes sense. - -## Why Applicatives? - -[Applicative functors](https://en.wikipedia.org/wiki/Applicative_functor) sit, in terms of power, somewhere between [functors](https://en.wikipedia.org/wiki/Functor#Computer_implementations) (i.e. types which support `Map`), and [monads](https://en.wikipedia.org/wiki/Monad_(functional_programming)) (i.e. types which support `Bind`). - -If we consider `Bind : M<'T> * ('T -> M<'U>) -> M<'U>`, we can see that the second element of the input is a function that requires a value to create the resulting "wrapped value". - -In contrast, `Apply : M<'T -> 'U> * M<'T> -> M<'U>` only needs a wrapped function, which is something we have whilst building our computation. - -So, importantly, applicatives allow us the power to use functions which are "wrapped up" inside a functor, but [preserve our ability to analyse the structure of the computation](https://paolocapriotti.com/assets/applicative.pdf) - and hence allow for the introduction of optimisations or alternative interpretations - without any values present. This is a critical distinction which can have a huge impact on performance, and indeed on what is possible to construct at all, so has very tangible implications. - -## Examples of Useful Applicatives - -The examples below all make use of types which are applicatives, but explicitly _not_ monads, to allow a powerful model for building a particular kind of computation, whilst preserving enough constraints to offer useful guarantees. - -[Tomas Petricek's formlets blog post](http://tomasp.net/blog/formlets-in-linq.aspx/) introduces the idea that we can use applicatives to build web forms. The guarantee of a static structure of the formlet applicative is used to render forms, but its powerful behaviours still allow useful processing of requests. - -[Pauan's comment about Observables](https://github.com/fsharp/fslang-suggestions/issues/579#issuecomment-310799948) points out that applicatives allow us to avoid frequent resubscriptions to `Observable` values because we know precisely how they'll be hooked up ahead of time, and that it won't change within the lifetime of the applicative. - -[McBride & Paterson's paper](http://www.staff.city.ac.uk/~ross/papers/Applicative.html) introduces a type very much like F#'s `Result<'T,'TError>` which can be used to stitch together functions and values which might fail, but conveniently accumulating up all of the errors which can then be helpfully presented at once, as opposed to immediately presenting the first error. This allows you to take [Scott Wlaschin's Railway Oriented Programming](https://fsharpforfunandprofit.com/rop/) to the next level! - -[Capriotti & Kaposi's paper](https://paolocapriotti.com/assets/applicative.pdf) introduces an example of creating an command line argument parser, where a single applicative can both statically generate help text for the parser, and dynamically parse options given to an application. - -More advanced usages include self-adjusting computations, such as [Jane Street's Incremental](https://blog.janestreet.com/introducing-incremental/) (written in OCaml), but with even greater opportunity for efficiencies via the careful balance of power and guarantees that applicatives give (such as the ability to optimise the computation, and avoid recreating the rest of the computation every time a `Bind` is reached. - -# Detailed design -[design]: #detailed-design - -This RFC introduces a desugaring for applicative computation expressions, much like that which exists for monads and related constructs. - -Example desugaring: - -```fsharp -ce { - let! x = foo - and! y = bar - and! z = baz - return x + y + z - } -``` - -⇒ - -```fsharp -ce.Apply( - ce.Apply( - ce.Apply( - ce.Return( - (fun x -> - (fun y -> - (fun z -> - x + y + z - ) - ) - )), - foo), - bar), - baz) -``` - -To be accepted as an applicative computation expression (CE), the CE must be of the form `let! ... and! ... return ...` (with one or more `and!`s). This means, for example, no `let!`s after an `and!` in the same CE, no normal `let`s in between the `let!` and `and!`s, and no usages of `yield` in place of `return`. Similarly, `do!` and `match!` are not valid in an applicative builder, although we allow `use!` and related keywords, which will be covered later. - -This may sound very constrained, but it is for good reason. The structure imposed by this rule forces the CE to be in a canonical form ([McBride & Paterson](http://www.staff.city.ac.uk/~ross/papers/Applicative.html)): - -> Any expression built from the Applicative combinators can be transformed to a canonical form in which a single pure function is ‘applied’ to the effectful parts in depth-first order: -`pure f <*> arg1 <*> ... <*> argN` -This canonical form captures the essence of Applicative programming: computations have a fixed structure, given by the pure function, and a sequence of subcomputations, given by the effectful arguments. - -In our case, the expression to the right of `return` (i.e. `pure`) becomes the body of a lambda, whose parameters are introduced by the `let! ... and! ...` preceding it. This leads to a very straightforward desugaring of the syntax, which therefore ensures both using and understanding the feature is only as complex as is inherently required by the abstraction. - -Despite requiring the canonical form, there are still many ways to build more complex and useful expressions from this syntax. The rest of this section aims to give a tour around these various features. - -## Pattern Matching - -```fsharp -let (|Quad|) (i : int) = - i * i * i * i - -type SingleCaseDu<'a> = SingleCaseDu of 'a - -ce { - let! Quad(x) = foo - and! (y,_) = bar - and! (SingleCaseDu z) = baz - return x + y + z - } -``` - -⇒ - -```fsharp -ce.Apply( - ce.Apply( - ce.Apply( - ce.Return( - (fun Quad(x) -> - (fun (y,_) -> - (fun (SingleCaseDu z) -> - x + y + z - ) - ) - )), - foo), - bar), - baz) -``` - -Pattern matching on the left-hand side of of a `let! ... and! ...` binding is valid, and desugars into the same pattern, but now as part of the lambda corresponding to that binding. - -## Using Monadic and Applicative Styles Simultaneously - -Recall that `let! ... and! ... return ...` syntax precludes an additional `let!` anywhere in the CE. In the case where your applicative happens also to be a monad, and you want to leverage the benefits of an applicative in some places (e.g. for performance reasons) but also use a `let!` (e.g. for convenience, or to do something a pure applicative doesn't support), you must do so inside a different CE context, e.g.: - -```fsharp -ce { - let! quux = - ce { - let! x = foo - and! (y,_) = bar - and! (SingleCaseDu z) = baz - return x + y + z - } - if quux > 6 - then - return quux - else - return 5 -} -``` - -⇒ - -```fsharp -ce.Bind( - ce.Apply( - ce.Apply( - ce.Apply( - ce.Return( - (fun x -> - (fun (y,_) -> - (fun (SingleCaseDu z) -> - x + y + z - ) - ) - )), - foo), - bar), - baz), - (fun quux -> - if quux > 6 - then - return quux - else - return 5) -) -``` - -## Monoids - -The existing `let!` CE syntax allows us to use `Combine` (typically in conjunction with `yield`) to define [monad plus](https://hackage.haskell.org/package/monadplus/docs/Control-Monad-Plus.html) instances (i.e. also interpret the type as a [monoid](https://en.wikipedia.org/wiki/Monoid)). [Just as monad plus is to a monad, alternatives are to applicatives](https://en.wikibooks.org/wiki/Haskell/Alternative_and_MonadPlus), so we can do a similar thing for our applicative CE syntax. One motivation for this might be the command line argument example from earlier, where alternatives allow parsing discriminated unions in a way that translates to something akin to "try this case, else try this case, else try this case, ...". - -One might assume that the syntax would be something such as: - -```fsharp -ce { - let! x = foo - and! y = bar - and! z = baz - yield x + y + z - yield x + y - yield y + z - } -``` - -Unfortunately, the naive desugaring of this can make it very easy to build a resulting chain of method calls which unintentionally ends up being very large: - -```fsharp -ce.Combine( - ce.Combine( - ce.Apply( - ce.Apply( - ce.Apply( - ce.Return( - (fun x -> - (fun y -> - (fun z -> - x + y + z - ) - ) - )), - foo), - bar), - baz), - ce.Apply( - ce.Apply( - ce.Apply( - ce.Return( - (fun x -> - (fun y -> - (fun z -> - x + y - ) - ) - )), - foo), - bar), - baz) - ), - ce.Apply( - ce.Apply( - ce.Apply( - ce.Return( - (fun x -> - (fun y -> - (fun z -> - y + z - ) - ) - )), - foo), - bar), - baz) -) -``` - -*N.B.* the size of the desugared expression grows with the product of the number of bindings introduced by the `let! ... and! ...` syntax and the number calls to `Combine` implied by the alternative cases. - -An attempt at a very smart desugaring which tries to cut down the resulting expression might, on the face of it, seem like a reasonable option. However, beyond the cost of analysing which values which are introduced by `let! ... and! ...` actually go on to be used, we must also consider the right-hand sides of the `let! ... and! ...` bindings and the pattern matching: Do we evaluated these once up front? Or recompute them in each alternative case at the leaf of the tree of calls to `Combine`? What if the expressions on the right-hand sides have side-effects, or the left-hand side utilises active patterns with side-effects? At that point we either make complex, unintuitive rules, or force the CE writer to be explicit. Continuing in the spirit of CEs generally being straightforward desugarings, we choose the latter make make the writer clearly state their desire. - -In order to keep things simple, then, we keep the canonical form introduced earlier, which forces precisely one `return` after a `let! ... and! ...`. However, we can still express alternative applicatives when using the `let! ... and! ...` syntax, we just need to use the same trick as with additional `let!`s and leave the scope of the canonical applicative syntax and therefore leave the additional constraints it places upon us: - -```fsharp -ce { - yield - ce { - let! x = foo - and! y = bar - and! z = baz - return x + y + z - } - yield - ce { - let! x = foo - and! y = bar - return x + y - } - yield - ce { - let! x = foo - and! y = bar - return y + z - } -} -``` - -⇒ - -```fsharp -ce.Combine( - ce.Combine( - ce.Apply( - ce.Apply( - ce.Apply( - ce.Return( - (fun x -> - (fun y -> - (fun z -> - x + y + z - ) - ) - )), - foo), - bar), - baz), - ce.Apply( - ce.Apply( - ce.Return( - (fun x -> - (fun y -> - x + y - ) - )), - foo), - bar) - ), - ce.Apply( - ce.Apply( - ce.Return( - (fun y -> - (fun z -> - y + z - ) - )), - bar), - baz) -) -``` - -*N.B.* this syntax forces the writer to be explicit about how many times `Apply` should be called, and with which arguments, for each call to `Combine`. Notice also how the right-hand sides are still repeated for each alternative case in order to keep the occurrence of potential side-effects from evaluating them predictable, and also occur before the pattern matching _each time_ a new alternative case is explored. - -If this syntax feels a bit heavy, remember that the `yield` keyword is not required, and the new syntax can be mixed with other styles (e.g. a custom `<|>` alternation operator) to strike an appropriate balance as each situation requires. - -## Using - -Just as monads support `Using` via `use!`, applicatives supports it via `use! ... anduse! ...`. Each binding can be either `and!` or `anduse!` (unless it is the first, in which case it must be either `let!` or `use!`), i.e. you can mix-and-match to describe which bindings should be covered by a call to `Using`: - -```fsharp -ce { - use! x = foo // x is wrapped in a call to ce.Using(...) - and! y = bar // y is _not_ wrapped in a call to ce.Using(...) - anduse! z = baz // z is wrapped in a call to ce.Using(...) - return x + y + z - } -``` - -⇒ - -```fsharp -ce.Apply( - ce.Apply( - ce.Apply( - ce.Return( - (fun x -> - ce.Using(x, fun x -> - (fun y -> // <- N.B. No ce.Using(...) call here because we used `and!` - (fun z -> // instead of `anduse!` for `y` in the CE. Similarly, we - ce.Using(z, fun z -> // could have chose to use `let!` instead of `use!` for the - x + y + z // first binding to avoid a call to Using - ) - ) - ) - ) - )), - foo), - bar), - baz) -``` - -## Ambiguities surrounding a `let! .. return ...` -[singlelet]: #singlelet - -Some CEs could be validly desugared in multiple ways, depending on which methods are implemented on a builder (assuming the implementations follow the standard laws relating these functions). - -For example: - -```fsharp -ce { - let! x = foo - return x + 1 - } -``` - -Can be desugared via `Bind`: - -```fsharp -ce.Bind( - (fun x -> ce.Return(x + 1)), - foo) -``` - -Or via `Apply`: - -```fsharp -ce.Apply( - ce.Return(fun x -> x + 1), - foo) -``` - -This is because the operation is really equivalent to a `Map`, something which can be implemented in terms of `Return` and either `Bind` or `Apply`. We mentioned that these functions were in some sense more powerful than a plain functor's `Map`, and we are seeing an example of that here. - -In order to avoid breaking backwards compatibility, the default resolution is to desugar via `Bind`, _failing if it is not defined on the builder_ (even though, conceptually, it should be implemented via `Apply`). This is consistent with in previous F# versions. [Later work on supporting `Map`](https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1048-ce-builder-map.md) can then make the choice about how to resolve this in a way which works with that in mind too. - -# Drawbacks -[drawbacks]: #drawbacks - -The new applicative computation expressions are quite constrained, and as has been discussed, that is precisely what allows them to be so useful. However, these constraints are potentially somewhat unintuitive to the beginner. Computation expressions already involve one of the steeper learning curves of the F# language features, so the added complexity from this feature needs to be carefully weighed against the potential guarantees, expressiveness and performance gains that they can offer. - -# Alternative Designs -[alternative-designs]: #alternative-designs - -[Tomas Petricek's Joinads](http://tomasp.net/blog/fsharp-variations-joinads.aspx/) offered a superset of the features proposed here, but was [rejected](https://github.com/fsharp/fslang-suggestions/issues/172) due to its complexity. This RFC is of much smaller scope, so should be a much less risky change. - -# Compatibility -[compatibility]: #compatibility - -## Is this a breaking change? - -This change should be backwards compatible. - -* Uses of `let!` without `and!` should still desugar to use `Bind`. See [the discussion of `let! ... return ...`](#singlelet) computation expressions for more details. - -* Existing computation expression builders with an `Apply` method should not change in behaviour, since usages of the builder would still need to add the new `let! ... and! ...` syntax to activate it. - -## What happens when previous versions of the F# compiler encounter this design addition as source code? - -Previous compiler versions reject the new `and!` keyword: - -``` -error FS1141: Identifiers followed by '!' are reserved for future use -``` - -## What happens when previous versions of the F# compiler encounter this design addition in compiled binaries? - -Since the syntax is desugared into a method call on the builder object, after compilation usages of this feature will be usable with previous compiler versions. - -# Unresolved questions -[unresolved]: #unresolved-questions - -Is `anduse!` an acceptable keyword for when `and!` must also imply a call to `Using`? - -There are various ways of desugaring `let! ... return ...` via one of `Map`, `Apply` or `Bind`. [The above](#singlelet) assumes that we are aiming for backward compatibility and hence doesn't consider desugaring to `Apply` at all. Is this the best choice? Alternatives include: - -* Using `Apply` in preference to `Bind` for `let! ... return` whenever `Apply` is defined (on the basis that that any reasonable `Apply` implementation will be functionally equivalent, but more efficient than the corresponding `Bind`) -* Subsuming [the RFC for desugaring this to `Map`](https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1048-ce-builder-map.md) and defining a hierarchy between `Map`, `Apply` and `Bind` and some attributes for those methods to allow the creators of builders to opt-in to "optimisations" that pick the least powerful (and hence hopefully most efficient) desugaring. -* Using `Apply` in place of `Bind` only in those instances where `Bind` is not defined (no existing code should break, but this goes somewhat to the contrary of the previous proposal, which we may want to consider in the future) \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.cs.xlf b/src/fsharp/xlf/FSStrings.cs.xlf index dd20d2c329..9ea20d8caa 100644 --- a/src/fsharp/xlf/FSStrings.cs.xlf +++ b/src/fsharp/xlf/FSStrings.cs.xlf @@ -1617,6 +1617,11 @@ Vnitřní chyba: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.de.xlf b/src/fsharp/xlf/FSStrings.de.xlf index 11c588c25a..882d7ca350 100644 --- a/src/fsharp/xlf/FSStrings.de.xlf +++ b/src/fsharp/xlf/FSStrings.de.xlf @@ -1617,6 +1617,11 @@ Interner Fehler: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.en.xlf b/src/fsharp/xlf/FSStrings.en.xlf index c7f55180a5..2520b4c96b 100644 --- a/src/fsharp/xlf/FSStrings.en.xlf +++ b/src/fsharp/xlf/FSStrings.en.xlf @@ -1617,6 +1617,11 @@ internal error: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.es.xlf b/src/fsharp/xlf/FSStrings.es.xlf index a64fb62268..162405b9cf 100644 --- a/src/fsharp/xlf/FSStrings.es.xlf +++ b/src/fsharp/xlf/FSStrings.es.xlf @@ -1617,6 +1617,11 @@ error interno: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.fr.xlf b/src/fsharp/xlf/FSStrings.fr.xlf index a56bd5baad..87d3a2909c 100644 --- a/src/fsharp/xlf/FSStrings.fr.xlf +++ b/src/fsharp/xlf/FSStrings.fr.xlf @@ -1617,6 +1617,11 @@ erreur interne : {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.it.xlf b/src/fsharp/xlf/FSStrings.it.xlf index b8a0c2aa38..61dab305da 100644 --- a/src/fsharp/xlf/FSStrings.it.xlf +++ b/src/fsharp/xlf/FSStrings.it.xlf @@ -1617,6 +1617,11 @@ errore interno: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.ja.xlf b/src/fsharp/xlf/FSStrings.ja.xlf index e2bc61160b..829dec26d2 100644 --- a/src/fsharp/xlf/FSStrings.ja.xlf +++ b/src/fsharp/xlf/FSStrings.ja.xlf @@ -1617,6 +1617,11 @@ 内部エラー: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.ko.xlf b/src/fsharp/xlf/FSStrings.ko.xlf index 8f398f713c..b73580dad4 100644 --- a/src/fsharp/xlf/FSStrings.ko.xlf +++ b/src/fsharp/xlf/FSStrings.ko.xlf @@ -1617,6 +1617,11 @@ 내부 오류: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.pl.xlf b/src/fsharp/xlf/FSStrings.pl.xlf index b634fb8ab9..171d2ca74a 100644 --- a/src/fsharp/xlf/FSStrings.pl.xlf +++ b/src/fsharp/xlf/FSStrings.pl.xlf @@ -1617,6 +1617,11 @@ błąd wewnętrzny: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.pt-BR.xlf b/src/fsharp/xlf/FSStrings.pt-BR.xlf index 0d185ea82f..ae643d7579 100644 --- a/src/fsharp/xlf/FSStrings.pt-BR.xlf +++ b/src/fsharp/xlf/FSStrings.pt-BR.xlf @@ -1617,6 +1617,11 @@ erro interno: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.ru.xlf b/src/fsharp/xlf/FSStrings.ru.xlf index ebae0bf0ec..8b1e079f45 100644 --- a/src/fsharp/xlf/FSStrings.ru.xlf +++ b/src/fsharp/xlf/FSStrings.ru.xlf @@ -1617,6 +1617,11 @@ внутренняя ошибка: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.tr.xlf b/src/fsharp/xlf/FSStrings.tr.xlf index bc70825fa2..0303e8b5be 100644 --- a/src/fsharp/xlf/FSStrings.tr.xlf +++ b/src/fsharp/xlf/FSStrings.tr.xlf @@ -1617,6 +1617,11 @@ iç hata: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.zh-Hans.xlf b/src/fsharp/xlf/FSStrings.zh-Hans.xlf index 12be285159..b55cc3a015 100644 --- a/src/fsharp/xlf/FSStrings.zh-Hans.xlf +++ b/src/fsharp/xlf/FSStrings.zh-Hans.xlf @@ -1617,6 +1617,11 @@ 内部错误: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSStrings.zh-Hant.xlf b/src/fsharp/xlf/FSStrings.zh-Hant.xlf index 81cad28dd5..5cd805df06 100644 --- a/src/fsharp/xlf/FSStrings.zh-Hant.xlf +++ b/src/fsharp/xlf/FSStrings.zh-Hant.xlf @@ -1617,6 +1617,11 @@ 內部錯誤: {0} + + keyword 'and!' + keyword 'and!' + + \ No newline at end of file From 890567ebf2e0546a59c5d0284bb69022eacf8862 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 26 Sep 2018 12:30:40 +0100 Subject: [PATCH 169/255] Sketch out desugaring of use! ... anduse! ... to builder.MapUsing(...) --- scratch/data/SimpleBuilder.fsx | 49 ++++++++++++++++++----------- src/fsharp/TypeChecker.fs | 57 ++++++++++++++++++---------------- 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index b9dd2364bf..f2d99947df 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -127,8 +127,7 @@ let quux : int option = printfn "quux: %+A" quux -(* -type TraceBuilder() = +type TraceOptBuilder() = member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = match fOpt, xOpt with @@ -161,24 +160,38 @@ type TraceBuilder() = printfn "Combining %+A with %+A to get %+A" xOpt yOpt res res - member __.ReturnFrom(x) = x - - member __.TryWith(body, handler) = - try __.ReturnFrom(body()) + member __.MapTryWith(body, handler) = + try body() with e -> handler e - member __.TryFinally(body, compensation) = - try __.ReturnFrom(body()) + member __.MapTryFinally(body, compensation) = + try body() finally compensation() - member __.Using(disposable:#System.IDisposable, body) = + member __.MapUsing(disposable:#System.IDisposable, body) = + printfn "Using disposable %O" disposable let body' = fun () -> body disposable - __.TryFinally(body', fun () -> - match disposable with - | null -> - printfn "Not disposing: Value is null" - () - | disp -> - printfn "Disposing %O" disp - disp.Dispose()) -*) \ No newline at end of file + __.MapTryFinally(body', fun () -> + printfn "Disposing %O" disposable + disposable.Dispose()) + +let traceOpt = TraceOptBuilder() + +type 'a FakeDisposable = + FakeDisposable of 'a + with + interface System.IDisposable with + member __.Dispose() = + let (FakeDisposable x) = __ + printf "\"Disposed\" %+A" x + () + +let fooUsing : string option = + traceOpt { + use! x = Some (FakeDisposable 1) + anduse! y = Some (FakeDisposable 2) + anduse! z = Some (FakeDisposable 3) + return sprintf "x = %+A, y = %+A, z = %+A" x y z + } + +printfn "fooUsing: \"%+A\"" fooUsing \ No newline at end of file diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 750799ffbe..b08754d89a 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8041,10 +8041,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr Some(translatedCtxt (mkSynCall "Bind" bindRange [rhsExpr; consumeExpr])) - // 'use! pat = e1 in e2' where 'pat' is not a simple name --> error - | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> - error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) - // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> // build.Apply( // build.Apply( @@ -8056,11 +8052,14 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // ) // ), expr1) // ), expr2) - | SynExpr.LetOrUseAndBang(letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, _), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! + // 'use! pat = e1 and! pat = e2 in e3' --> TODO + // 'let! pat = e1 anduse! pat = e2 in e3' --> TODO + // 'use! pat = e1 anduse! pat = e2 in e3' --> TODO + | SynExpr.LetOrUseAndBang(letSpBind, letIsUse, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, _), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! let bindingsBottomToTop = let letBinding = - (letSpBind, false, letIsFromSource, letPat, letRhsExpr, letm) + (letSpBind, letIsUse, letIsFromSource, letPat, letRhsExpr, letm) List.rev (letBinding :: andBangBindings) // [ andBangExprN ; ... ; andBangExpr2 ; andBangExpr1 ; letExpr ] // Here we construct a call to Apply for each binding introduced by the let! ... and! ... syntax @@ -8069,7 +8068,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv match bindings with | (spBind, _, isFromSource, pat, rhsExpr, _) :: remainingBindings -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range - if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) + if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) // TODO "Apply may not be used in queries" if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) @@ -8107,12 +8106,27 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let desugared = // We construct match lambdas to do any of the pattern matching that - // appears on the LHS of a binding + // appears on the LHS of a binding, adding a call to builder.Using(...) + // if required bindingsBottomToTop - |> List.fold (fun acc (spBind, _, _, pat, _, _) -> - let innerRange = returnExpr.Range - printfn "Creating synthetic match lambda for pattern %+A" pat // TODO Delete - SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) + |> List.fold (fun acc (spBind, isUse, _, pat, rhsExpr, m) -> + match isUse, pat with + | true, SynPat.Named (SynPat.Wild _, id, false, _, _) + | true, SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) -> + let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range + if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "MapUsing" builderTy) + then error(Error(FSComp.SR.tcRequireBuilderMethod("MapUsing"), m)) + printfn "Creating synthetic match lambda and Using call for pattern %+A" pat // TODO Delete + let consumeExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, transNoQueryOps acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) + let consumeExpr = mkSynCall "MapUsing" bindRange [SynExpr.Ident(id); consumeExpr ] + SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, consumeExpr, id.idRange, SequencePointAtTarget)], spBind, bindRange) + | true, _ -> + // TODO Support explicitly typed names on the LHS of a use!/anduse! + error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // TODO Change error to mention `anduse!` + | false, _ -> + let innerRange = returnExpr.Range + printfn "Creating synthetic match lambda for pattern %+A" pat // TODO Delete + SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) ) returnExpr // We take the nested lambdas and put the return back, _outside_ them // to give an f : F<'a -> 'b -> 'c -> ...> as a series of Apply calls expects @@ -8126,21 +8140,12 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some (translatedCtxt desugared) - | SynExpr.LetOrUseAndBang(_, false, _, _, _, _, _, _) -> // TODO Handle use! / anduse! - error(new Exception("let! ... and! ... computation expressions must immediately return a value")) // TODO Make more helpful - - // 'use! pat = e1 and! pat = e2 in e3' --> TODO - //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, andBangs, innerComp) - //| SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) as pat), rhsExpr, _, andBangs, innerComp) -> - | SynExpr.LetOrUseAndBang(_, true, _, (SynPat.Named (SynPat.Wild _, _, false, _, _)) , _, _, _, _) - | SynExpr.LetOrUseAndBang(_, true, _, (SynPat.LongIdent (LongIdentWithDots([_], _), _, _, _, _, _)), _, _, _, _) -> - - failwith "TODO - Type check and!" + // 'use! pat = e1 ... in e2' where 'pat' is not a simple name --> error + | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, _, _innerComp) -> + error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) - // 'use! pat = e1 and(Use)! pat = e2 in e3' where any 'pat' is not a simple name --> error - //| SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, andBangs, _innerComp) -> - | SynExpr.LetOrUseAndBang(_, true, _, _, _, _, _, _) -> - failwith "TODO - Type check and!" + | SynExpr.LetOrUseAndBang _ -> + error(new Exception("let! ... and! ... computation expressions must immediately return a value")) // TODO Make more helpful | SynExpr.Match (spMatch, expr, clauses, m) -> let mMatch = match spMatch with SequencePointAtBinding mMatch -> mMatch | _ -> m From f976ff85c800d2e42657a848073d7737eba1595f Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 26 Sep 2018 16:50:31 +0100 Subject: [PATCH 170/255] Update example to flush out issues in desugaring - probably varSpace --- scratch/data/SimpleBuilder.fsx | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index f2d99947df..02f9e72d15 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -186,12 +186,33 @@ type 'a FakeDisposable = printf "\"Disposed\" %+A" x () +let simpleFooUsing : int option = + traceOpt { + use! xUsing = Some (FakeDisposable 1) + and! y = Some 2 + return (let (FakeDisposable x) = xUsing in x + y) + } + +printfn "simplefooUsing: \"%+A\"" simpleFooUsing + let fooUsing : string option = traceOpt { - use! x = Some (FakeDisposable 1) - anduse! y = Some (FakeDisposable 2) - anduse! z = Some (FakeDisposable 3) - return sprintf "x = %+A, y = %+A, z = %+A" x y z + use! xUsing = Some (FakeDisposable 1) + anduse! yUsing = Some (FakeDisposable 2) + anduse! zUsing = Some (FakeDisposable 3) + return (let (FakeDisposable x) = xUsing in sprintf "Unwrapped x = %d" x) + } + +printfn "fooUsing: \"%+A\"" fooUsing + +(* INVALID: Not a simple name on the LHS +let fooUsing2 : int option = + traceOpt { + use! (FakeDisposable x) = Some (FakeDisposable 1) + anduse! (FakeDisposable y) = Some (FakeDisposable 2) + anduse! (FakeDisposable z) = Some (FakeDisposable 3) + return x * y + z } -printfn "fooUsing: \"%+A\"" fooUsing \ No newline at end of file +printfn "fooUsing2: \"%+A\"" fooUsing +*) \ No newline at end of file From 57ebdfa36a1e3782c13e13312b17161035aea698 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 26 Sep 2018 17:20:57 +0100 Subject: [PATCH 171/255] Remove varSpace and trans from let! ... and! ... / use! ... anduse! ... and related syntax - we don't allow allow anything inside them which needs translating --- src/fsharp/TypeChecker.fs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index b08754d89a..142a38a825 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8086,24 +8086,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let wrapInCallsToApply = constructAppliesForBindings id bindingsBottomToTop - // Add the variables to the query variable space, on demand - (*let varSpace = - // TODO Make sure no LHS names clash, and no RHS mentions a name from another LHS - bindingsBottomToTop - |> List.fold (fun acc (_,_,_,pat,_,_) -> - addVarsToVarSpace acc (fun _mCustomOp env -> - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (pat, None) - vspecs, envinner) - ) varSpace - *) - // Note how we work from the inner expression outward, meaning be create the lambda for the last // 'and!' _first_, and create the calls to 'Apply' in the opposite order so that the calls to // 'Apply' correspond to their lambda. - // TODO Collect up varSpace and use it here? Does this mean and! bindings cannot shadow each other? - let desugared = // We construct match lambdas to do any of the pattern matching that // appears on the LHS of a binding, adding a call to builder.Using(...) @@ -8117,7 +8103,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "MapUsing" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("MapUsing"), m)) printfn "Creating synthetic match lambda and Using call for pattern %+A" pat // TODO Delete - let consumeExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, transNoQueryOps acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) + let consumeExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) let consumeExpr = mkSynCall "MapUsing" bindRange [SynExpr.Ident(id); consumeExpr ] SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, consumeExpr, id.idRange, SequencePointAtTarget)], spBind, bindRange) | true, _ -> From 3ae17d1a85c21af178411060a5f1e0120f899b96 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 26 Sep 2018 18:51:19 +0100 Subject: [PATCH 172/255] Fix up SimpleBuilder to not introduce delay (mainly becaue it has the wrong signature here!) --- scratch/data/SimpleBuilder.fsx | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 02f9e72d15..2d2ead9963 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -118,7 +118,7 @@ let quux : int option = let! x = Some 0 and! y = Some 1 and! z = Some 2 - return x + y + z + 1 + return (x + y + z + 1) } and! d = baz // Makes use of an optional value from outside the computation expression (bailing out with None if it is None, as with any other value) return a * b * c * d // Computes this value because all args are some @@ -146,10 +146,6 @@ type TraceOptBuilder() = printfn "Zero" Some () - member __.Delay(f) = - printfn "Delay" - f - member __.Combine(xOpt, yOpt) = let res = match xOpt with @@ -160,10 +156,6 @@ type TraceOptBuilder() = printfn "Combining %+A with %+A to get %+A" xOpt yOpt res res - member __.MapTryWith(body, handler) = - try body() - with e -> handler e - member __.MapTryFinally(body, compensation) = try body() finally compensation() @@ -177,8 +169,8 @@ type TraceOptBuilder() = let traceOpt = TraceOptBuilder() -type 'a FakeDisposable = - FakeDisposable of 'a +type FakeDisposable = + FakeDisposable of int with interface System.IDisposable with member __.Dispose() = @@ -186,11 +178,11 @@ type 'a FakeDisposable = printf "\"Disposed\" %+A" x () -let simpleFooUsing : int option = +let simpleFooUsing : string option = traceOpt { - use! xUsing = Some (FakeDisposable 1) - and! y = Some 2 - return (let (FakeDisposable x) = xUsing in x + y) + use! xUsing = Some (FakeDisposable 1) + anduse! yUsing = Some (FakeDisposable 2) + return (sprintf "xUsing = %+A, yUsing = %+A" xUsing yUsing) } printfn "simplefooUsing: \"%+A\"" simpleFooUsing From 813300318b962cdb6182161807ff83d8f9f18af7 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 26 Sep 2018 18:51:39 +0100 Subject: [PATCH 173/255] Add note to remove call to mkSourceExpr for applicative CEs --- src/fsharp/TypeChecker.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 142a38a825..0ddcfba4fb 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8072,7 +8072,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Apply"), bindRange)) - let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr + let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr // TODO Can delete? Won't use query.Source? let newPendingApplies = (fun consumeExpr -> @@ -8103,9 +8103,9 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "MapUsing" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("MapUsing"), m)) printfn "Creating synthetic match lambda and Using call for pattern %+A" pat // TODO Delete - let consumeExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) - let consumeExpr = mkSynCall "MapUsing" bindRange [SynExpr.Ident(id); consumeExpr ] - SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, consumeExpr, id.idRange, SequencePointAtTarget)], spBind, bindRange) + let mapUsingBodyExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) // TODO Where should we be suppressing sequence points? + let mapUsingExpr = mkSynCall "MapUsing" bindRange [ SynExpr.Ident(id); mapUsingBodyExpr ] + SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, mapUsingExpr, id.idRange, SequencePointAtTarget)], spBind, bindRange) | true, _ -> // TODO Support explicitly typed names on the LHS of a use!/anduse! error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // TODO Change error to mention `anduse!` From dd27210f1b43fddce0fd891b7a7ae825be5a6a39 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 27 Sep 2018 14:30:14 +0100 Subject: [PATCH 174/255] Add logging to check order of events for disposal --- scratch/data/SimpleBuilder.fsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx index 2d2ead9963..6266ac2e65 100644 --- a/scratch/data/SimpleBuilder.fsx +++ b/scratch/data/SimpleBuilder.fsx @@ -162,7 +162,11 @@ type TraceOptBuilder() = member __.MapUsing(disposable:#System.IDisposable, body) = printfn "Using disposable %O" disposable - let body' = fun () -> body disposable + let body' = fun () -> + printfn ">Running body" + let res = body disposable + printfn " printfn "Disposing %O" disposable disposable.Dispose()) @@ -175,17 +179,19 @@ type FakeDisposable = interface System.IDisposable with member __.Dispose() = let (FakeDisposable x) = __ - printf "\"Disposed\" %+A" x + printfn "\"Disposed\" %+A" x () +printfn "\n\nStarting Using experiment...\n\n" + let simpleFooUsing : string option = traceOpt { use! xUsing = Some (FakeDisposable 1) anduse! yUsing = Some (FakeDisposable 2) - return (sprintf "xUsing = %+A, yUsing = %+A" xUsing yUsing) + return (printfn "Calling return expr - nothing should have been disposed yet!"; sprintf "xUsing = %+A, yUsing = %+A" xUsing yUsing) } -printfn "simplefooUsing: \"%+A\"" simpleFooUsing +printfn "simplefooUsing: \"%+A\"\n\n" simpleFooUsing let fooUsing : string option = traceOpt { @@ -195,7 +201,7 @@ let fooUsing : string option = return (let (FakeDisposable x) = xUsing in sprintf "Unwrapped x = %d" x) } -printfn "fooUsing: \"%+A\"" fooUsing +printfn "fooUsing: \"%+A\"\n\n" fooUsing (* INVALID: Not a simple name on the LHS let fooUsing2 : int option = From 120e024a03afbec337c41c1299cea97420d0afbb Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 27 Sep 2018 14:31:35 +0100 Subject: [PATCH 175/255] Move `builder.MapUsing` calls inside match lambdas This ensures disposal happens after the function implied by `return` is called, rather than after the closures that define it are created --- src/fsharp/TypeChecker.fs | 42 ++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 0ddcfba4fb..9c7e9cf335 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8091,29 +8091,35 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // 'Apply' correspond to their lambda. let desugared = - // We construct match lambdas to do any of the pattern matching that - // appears on the LHS of a binding, adding a call to builder.Using(...) + // Insert calls to builder.MapUsing(...) to handle resources // if required + let usings = + bindingsBottomToTop + |> List.fold (fun acc (spBind, isUse, _, pat, rhsExpr, m) -> + match isUse, pat with + | true, SynPat.Named (SynPat.Wild _, id, false, _, _) + | true, SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) -> + let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range + if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "MapUsing" builderTy) + then error(Error(FSComp.SR.tcRequireBuilderMethod("MapUsing"), m)) + printfn "Creating synthetic match lambda and Using call for pattern %+A" pat // TODO Delete + let mapUsingBodyExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) // TODO Where should we be suppressing sequence points? + mkSynCall "MapUsing" bindRange [ SynExpr.Ident(id); mapUsingBodyExpr ] + | true, _ -> + // TODO Support explicitly typed names on the LHS of a use!/anduse! + error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // TODO Change error to mention `anduse!` + | false, _ -> + acc + ) returnExpr + + // We construct match lambdas to do any of the pattern matching that + // appears on the LHS of a binding bindingsBottomToTop - |> List.fold (fun acc (spBind, isUse, _, pat, rhsExpr, m) -> - match isUse, pat with - | true, SynPat.Named (SynPat.Wild _, id, false, _, _) - | true, SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) -> - let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range - if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "MapUsing" builderTy) - then error(Error(FSComp.SR.tcRequireBuilderMethod("MapUsing"), m)) - printfn "Creating synthetic match lambda and Using call for pattern %+A" pat // TODO Delete - let mapUsingBodyExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) // TODO Where should we be suppressing sequence points? - let mapUsingExpr = mkSynCall "MapUsing" bindRange [ SynExpr.Ident(id); mapUsingBodyExpr ] - SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, mapUsingExpr, id.idRange, SequencePointAtTarget)], spBind, bindRange) - | true, _ -> - // TODO Support explicitly typed names on the LHS of a use!/anduse! - error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // TODO Change error to mention `anduse!` - | false, _ -> + |> List.fold (fun acc (spBind, _, _, pat, _, _) -> let innerRange = returnExpr.Range printfn "Creating synthetic match lambda for pattern %+A" pat // TODO Delete SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) - ) returnExpr + ) usings // We take the nested lambdas and put the return back, _outside_ them // to give an f : F<'a -> 'b -> 'c -> ...> as a series of Apply calls expects |> (fun f -> From 6a4ea591c86ce509dcb4a036663da9b2b9a4e994 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 27 Sep 2018 16:40:14 +0100 Subject: [PATCH 176/255] Sketch out an applicative form of Eventually --- .../ComputationExprLibrary.fs | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/ComputationExpressions/ComputationExprLibrary.fs b/tests/fsharpqa/Source/CodeGen/EmittedIL/ComputationExpressions/ComputationExprLibrary.fs index 90ae5e8665..658acc6241 100644 --- a/tests/fsharpqa/Source/CodeGen/EmittedIL/ComputationExpressions/ComputationExprLibrary.fs +++ b/tests/fsharpqa/Source/CodeGen/EmittedIL/ComputationExpressions/ComputationExprLibrary.fs @@ -49,6 +49,17 @@ module Eventually = | Done x -> k x | NotYetDone work -> NotYetDone (fun () -> bind k (work())) + let rec apply f e = + match f, e with + | Done f, Done x -> Done (f x) + | Done _, NotYetDone work -> NotYetDone (fun () -> apply f (work())) + | NotYetDone work, _ -> NotYetDone (fun () -> apply (work()) e) + + let rec map f e = + match e with + | Done x -> Done (f x) + | NotYetDone work -> NotYetDone (fun () -> map f (work())) + let fold f acc seq = (Done acc,seq) ||> Seq.fold (fun acc x -> acc |> bind (fun acc -> f acc x)) @@ -74,6 +85,12 @@ module Eventually = let tryWith e handler = catch e |> bind (function Result v -> Done v | Exception e -> handler e) + + let mapUsing (resource:System.IDisposable) f = + try + f resource + finally + resource.Dispose() let rec doWhile f e = if f() then e |> bind (fun () -> doWhile f e) else Eventually.Done () @@ -86,17 +103,19 @@ module Eventually = tryFinally (delay (fun () -> loop ie)) (fun _ -> ie.Dispose()) type EventuallyBuilder() = - member x.Bind(e,k) = Eventually.bind k e - member x.Return(v) = Eventually.Done v - member x.ReturnFrom(e:Eventually<_>) = e - member x.Combine(e1,e2) = e1 |> Eventually.bind (fun () -> e2) - member x.TryWith(e,handler) = Eventually.tryWith e handler - member x.TryFinally(e,compensation) = Eventually.tryFinally e compensation - member x.Using(resource:System.IDisposable,e) = Eventually.tryFinally (e resource) resource.Dispose - member x.While(gd,e) = Eventually.doWhile gd e - member x.For(xs,f) = Eventually.doFor xs f - member x.Delay(f) = Eventually.delay f - member x.Zero() = Eventually.Done () + member __.Bind(e,k) = Eventually.bind k e + member __.Apply(f,x) = Eventually.apply f x + member __.Return(v) = Eventually.Done v + member __.ReturnFrom(e:Eventually<_>) = e + member __.Combine(e1,e2) = e1 |> Eventually.bind (fun () -> e2) + member __.TryWith(e,handler) = Eventually.tryWith e handler + member __.TryFinally(e,compensation) = Eventually.tryFinally e compensation + member __.Using(resource:System.IDisposable,e) = Eventually.tryFinally (e resource) resource.Dispose + member __.MapUsing(resource:System.IDisposable,f) = Eventually.mapUsing resource f + member __.While(gd,e) = Eventually.doWhile gd e + member __.For(xs,f) = Eventually.doFor xs f + member __.Delay(f) = Eventually.delay f + member __.Zero() = Eventually.Done () [] From a6de7f35df2e22a768a2ef1167eb86b5d8d5b55d Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 27 Sep 2018 16:44:42 +0100 Subject: [PATCH 177/255] Remove old debugging failwith and comments --- src/fsharp/pars.fsy | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index fcc3f3a744..7e46fd3ef7 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -451,7 +451,7 @@ let rangeOfLongIdent(lid:LongIdent) = %nonassoc paren_pat_attribs %left OR BAR_BAR JOIN_IN %left AND /* check */ -%left AND_BANG /* TODO check */ +%left AND_BANG /* check */ %left AMP AMP_AMP %nonassoc pat_conj %nonassoc expr_not @@ -3006,7 +3006,7 @@ recover: /* EXPERIMENT */ binders: - | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let /* TODO the 'IN' in this position sort of implies that the variable is in scope in any subsequence AND! RHSs - which is not true! */ + | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) let m = unionRanges (rhs parseState 1) $8.Range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } @@ -3022,7 +3022,6 @@ binders: let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) // TODO Check this range let m = $4.Range.EndRange // zero-width range - failwith "Inserting implicit zero from OBINDER!" // TODO Remove SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } From 6b6c3447ea6545d77a170bd2ba88855cd9b1b595 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 28 Sep 2018 13:31:02 +0100 Subject: [PATCH 178/255] Remove debug printfns and comments --- src/fsharp/TypeChecker.fs | 3 --- src/fsharp/pars.fsy | 7 ------- 2 files changed, 10 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 9c7e9cf335..c5372edcd8 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8102,7 +8102,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "MapUsing" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("MapUsing"), m)) - printfn "Creating synthetic match lambda and Using call for pattern %+A" pat // TODO Delete let mapUsingBodyExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) // TODO Where should we be suppressing sequence points? mkSynCall "MapUsing" bindRange [ SynExpr.Ident(id); mapUsingBodyExpr ] | true, _ -> @@ -8117,7 +8116,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv bindingsBottomToTop |> List.fold (fun acc (spBind, _, _, pat, _, _) -> let innerRange = returnExpr.Range - printfn "Creating synthetic match lambda for pattern %+A" pat // TODO Delete SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) ) usings // We take the nested lambdas and put the return back, _outside_ them @@ -8125,7 +8123,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv |> (fun f -> if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env returnRange ad "Return" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Return"), returnRange)) - printfn "Creating synthetic Return call" // TODO Delete mkSynCall "Return" returnRange [f]) // We wrap the return in a call to Apply for each lambda |> wrapInCallsToApply diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 7e46fd3ef7..1eb01ce23f 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3003,8 +3003,6 @@ recover: | error { debugPrint("recovering via error"); true } | EOF { debugPrint("recovering via EOF"); false } -/* EXPERIMENT */ - binders: | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) @@ -3024,9 +3022,7 @@ binders: let m = $4.Range.EndRange // zero-width range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } - morebinders: - | AND_BANG headBindingPattern EQUALS typedSeqExprBlock IN morebinders %prec expr_let /* TODO Check precedence */ { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *) let m = rhs parseState 1 (* TODO Pretty sure this is wrong *) @@ -3041,9 +3037,6 @@ morebinders: | %prec prec_no_more_attr_bindings /* TODO Make a prec_no_more_andbang_bindings or something */ { [] } -/* END EXPERIMENT */ - - declExpr: | defnBindings IN typedSeqExpr %prec expr_let { mkLocalBindings (unionRanges (rhs2 parseState 1 2) $3.Range,$1,$3) } From 4a61eaf3988c9b623b48a3557ff13ddb14a7a81d Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 28 Sep 2018 14:23:52 +0100 Subject: [PATCH 179/255] Create new error message for applicative CEs that who body isn't an immediate `return ...` --- src/fsharp/FSComp.txt | 1 + src/fsharp/TypeChecker.fs | 12 ++++++------ src/fsharp/xlf/FSComp.txt.cs.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.de.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.en.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.es.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.fr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.it.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ja.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ko.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pl.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ru.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.tr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 5 +++++ 16 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 86b7661552..d96e5a7b87 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1441,3 +1441,4 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3236,chkNoSpanLikeValueFromExpression,"A Span or IsByRefLike value returned from the expression cannot be used at ths point. This is to ensure the address of the local value does not escape its scope." 3237,tastCantTakeAddressOfExpression,"Cannot take the address of the value returned from the expression. Assign the returned value to a let-bound value before taking the address." 3242,tcTypeDoesNotInheritAttribute,"This type does not inherit Attribute, it will not work correctly with other .NET languages." +3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index c5372edcd8..5a0cc212b7 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8041,6 +8041,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let rhsExpr = if isFromSource then mkSourceExpr rhsExpr else rhsExpr Some(translatedCtxt (mkSynCall "Bind" bindRange [rhsExpr; consumeExpr])) + // 'use! pat = e1 ... in e2' where 'pat' is not a simple name --> error + | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> + error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) + // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> // build.Apply( // build.Apply( @@ -8129,12 +8133,8 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some (translatedCtxt desugared) - // 'use! pat = e1 ... in e2' where 'pat' is not a simple name --> error - | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, _, _innerComp) -> - error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) - - | SynExpr.LetOrUseAndBang _ -> - error(new Exception("let! ... and! ... computation expressions must immediately return a value")) // TODO Make more helpful + | SynExpr.LetOrUseAndBang (_, _, _, _, _, _, _, innerComp) -> + error(Error(FSComp.SR.tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn(), innerComp.Range)) | SynExpr.Match (spMatch, expr, clauses, m) -> let mMatch = match spMatch with SequencePointAtBinding mMatch -> mMatch | _ -> m diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index a55f03383b..4cd6fe9721 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index f1694c1097..0b9d27c8b0 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.en.xlf b/src/fsharp/xlf/FSComp.txt.en.xlf index 214bc542ec..e0df58a87e 100644 --- a/src/fsharp/xlf/FSComp.txt.en.xlf +++ b/src/fsharp/xlf/FSComp.txt.en.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index e553d7e814..ce87bc6a70 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index ace9b0593e..68a22a9b57 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index da96371da2..48aa939f49 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 65ace49365..e63712274b 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index 22fbe5d88e..bec676c560 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 23ffa36bdd..c43e7d3e1f 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 5e22244bf7..79550acff6 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index 34090ba964..4d114fbf97 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index 42e1af2de9..d2e673737b 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index ba86113d4d..88aefe5900 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 84d6951594..75a33ffdde 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -7067,6 +7067,11 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + \ No newline at end of file From d49142c35d9c321ba0947c000498559737fbcb8a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 28 Sep 2018 14:28:41 +0100 Subject: [PATCH 180/255] Remove unneeded debug printfn --- src/fsharp/TypeChecker.fs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 5a0cc212b7..8da9dae3f1 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8080,7 +8080,6 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let newPendingApplies = (fun consumeExpr -> - printfn "Creating synthetic Apply call for pattern %+A" pat // TODO Delete mkSynCall "Apply" bindRange [consumeExpr; rhsExpr]) >> pendingApplies // TODO Swap order of args to Apply? constructAppliesForBindings newPendingApplies remainingBindings From f1d3ed7b359e6012e3a39bb0a77a0a95295e596b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 28 Sep 2018 14:54:07 +0100 Subject: [PATCH 181/255] Add more specific error for pattern matching on the LHS of a use! ... anduse! ... binding --- src/fsharp/FSComp.txt | 1 + src/fsharp/TypeChecker.fs | 4 ++-- src/fsharp/xlf/FSComp.txt.cs.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.de.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.en.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.es.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.fr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.it.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ja.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ko.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pl.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ru.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.tr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 5 +++++ 16 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index d96e5a7b87..8485ce17cb 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1442,3 +1442,4 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3237,tastCantTakeAddressOfExpression,"Cannot take the address of the value returned from the expression. Assign the returned value to a let-bound value before taking the address." 3242,tcTypeDoesNotInheritAttribute,"This type does not inherit Attribute, it will not work correctly with other .NET languages." 3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." +3244,tcInvalidAndUseBangBinding,"Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '." diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 8da9dae3f1..4382166aa3 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8070,7 +8070,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let rec constructAppliesForBindings (pendingApplies : SynExpr -> SynExpr) (bindings : (SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list) = match bindings with - | (spBind, _, isFromSource, pat, rhsExpr, _) :: remainingBindings -> + | (spBind, _, isFromSource, _, rhsExpr, _) :: remainingBindings -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) // TODO "Apply may not be used in queries" if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env bindRange ad "Apply" builderTy) @@ -8109,7 +8109,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv mkSynCall "MapUsing" bindRange [ SynExpr.Ident(id); mapUsingBodyExpr ] | true, _ -> // TODO Support explicitly typed names on the LHS of a use!/anduse! - error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // TODO Change error to mention `anduse!` + error(Error(FSComp.SR.tcInvalidAndUseBangBinding(), pat.Range)) | false, _ -> acc ) returnExpr diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 4cd6fe9721..78103623b9 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index 0b9d27c8b0..c4b9564ca1 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.en.xlf b/src/fsharp/xlf/FSComp.txt.en.xlf index e0df58a87e..9a8467d456 100644 --- a/src/fsharp/xlf/FSComp.txt.en.xlf +++ b/src/fsharp/xlf/FSComp.txt.en.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index ce87bc6a70..0bdbbba4a6 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index 68a22a9b57..492a7318c0 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 48aa939f49..ba1bd9319f 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index e63712274b..370e38a1d9 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index bec676c560..f1007d1f7b 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index c43e7d3e1f..3535f6bd5d 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 79550acff6..3433d1b76c 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index 4d114fbf97..4a3e930614 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index d2e673737b..229c6a6012 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 88aefe5900..ef301c82a9 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 75a33ffdde..f1f2b52948 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -7072,6 +7072,11 @@ Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + \ No newline at end of file From 06aa8f09c30015ce4ddc0cc0b160f8dd70d056fa Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 1 Oct 2018 10:54:30 +0100 Subject: [PATCH 182/255] Sketch new error msg tests for applicative CEs --- ...E_LetBangAndBangNotTerminatedWithReturn.fs | 13 ++ ..._PatternOnLhsOfUseBangAndUseBangBinding.fs | 12 ++ .../builderlib.fs | 123 ++++++++++++++++++ .../ApplicativeComputationExpressions/env.lst | 4 + 4 files changed, 152 insertions(+) create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_PatternOnLhsOfUseBangAndUseBangBinding.fs create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs new file mode 100644 index 0000000000..9d740ef7dc --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs @@ -0,0 +1,13 @@ +// #ErrorMessages +//Expecting 'return' but saw something else. +//Applicative computation expressions must be of the form 'let! = +//and! = and! ... and! = return '. + +namespace ApplicativeComputationExpressions + + eventually { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + and! y = Eventually.Done 6 + let _ = 42 // Invalid: We expect a return to terminate the the let! ... and! ... sequence + return x + y + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_PatternOnLhsOfUseBangAndUseBangBinding.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_PatternOnLhsOfUseBangAndUseBangBinding.fs new file mode 100644 index 0000000000..171e0d0db8 --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_PatternOnLhsOfUseBangAndUseBangBinding.fs @@ -0,0 +1,12 @@ +// #ErrorMessages +//Pattern matching is not allowed on the left-hand side of the equals. +//'use! ... anduse! ...' bindings must be of the form +//'use! = ' or 'anduse! = '. + +namespace ApplicativeComputationExpressions + + eventually { + use! (x,_) = Eventually.Done (FakeDisposable 3, 19) + anduse! y = Eventually.Done (FakeDisposable 11) + return x + y + } diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs new file mode 100644 index 0000000000..baf0439f2a --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs @@ -0,0 +1,123 @@ +namespace ApplicativeComputationExpressions + +type Eventually<'T> = + | Done of 'T + | NotYetDone of (unit -> Eventually<'T>) + +type ResultOrException<'tresult> = + | Result of 'tresult + | Exception of System.Exception + +[] +module Eventually = + let rec box e = + match e with + | Done x -> Done (Operators.box x) + | NotYetDone (work) -> NotYetDone (fun () -> box (work())) + + let rec force e = + match e with + | Done x -> x + | NotYetDone (work) -> force (work()) + + let repeatedlyProgressUntilDoneOrTimeShareOver timeShareInMilliseconds runner e = + let sw = new System.Diagnostics.Stopwatch() + let rec runTimeShare e = + runner (fun () -> + sw.Reset() + sw.Start(); + let rec loop(e) = + match e with + | Done _ -> e + | NotYetDone (work) -> + if sw.ElapsedMilliseconds > timeShareInMilliseconds then + sw.Stop(); + NotYetDone(fun () -> runTimeShare e) + else + loop(work()) + loop(e)) + runTimeShare e + + let rec bind k e = + match e with + | Done x -> k x + | NotYetDone work -> NotYetDone (fun () -> bind k (work())) + + let rec apply f e = + match f, e with + | Done f, Done x -> Done (f x) + | Done _, NotYetDone work -> NotYetDone (fun () -> apply f (work())) + | NotYetDone work, _ -> NotYetDone (fun () -> apply (work()) e) + + let rec map f e = + match e with + | Done x -> Done (f x) + | NotYetDone work -> NotYetDone (fun () -> map f (work())) + + let fold f acc seq = + (Done acc,seq) ||> Seq.fold (fun acc x -> acc |> bind (fun acc -> f acc x)) + + let rec catch e = + match e with + | Done x -> Done(Result x) + | NotYetDone work -> + NotYetDone (fun () -> + let res = try Result(work()) with | e -> Exception e + match res with + | Result cont -> catch cont + | Exception e -> Done(Exception e)) + + let delay f = NotYetDone (fun () -> f()) + + let tryFinally e compensation = + catch (e) + |> bind (fun res -> compensation(); + match res with + | Result v -> Eventually.Done v + | Exception e -> raise e) + + let tryWith e handler = + catch e + |> bind (function Result v -> Done v | Exception e -> handler e) + + let mapUsing (resource:System.IDisposable) f = + try + f resource + finally + resource.Dispose() + + let rec doWhile f e = + if f() then e |> bind (fun () -> doWhile f e) else Eventually.Done () + + let doFor (xs: seq<_>) f = + let rec loop (ie:System.Collections.Generic.IEnumerator<_>) = + if ie.MoveNext() then f ie.Current |> bind (fun () -> loop ie) + else Eventually.Done () + let ie = xs.GetEnumerator() + tryFinally (delay (fun () -> loop ie)) (fun _ -> ie.Dispose()) + +type EventuallyBuilder() = + member __.Bind(e,k) = Eventually.bind k e + member __.Apply(f,x) = Eventually.apply f x + member __.Return(v) = Eventually.Done v + member __.ReturnFrom(e:Eventually<_>) = e + member __.Combine(e1,e2) = e1 |> Eventually.bind (fun () -> e2) + member __.TryWith(e,handler) = Eventually.tryWith e handler + member __.TryFinally(e,compensation) = Eventually.tryFinally e compensation + member __.Using(resource:System.IDisposable,e) = Eventually.tryFinally (e resource) resource.Dispose + member __.MapUsing(resource:System.IDisposable,f) = Eventually.mapUsing resource f + member __.While(gd,e) = Eventually.doWhile gd e + member __.For(xs,f) = Eventually.doFor xs f + member __.Delay(f) = Eventually.delay f + member __.Zero() = Eventually.Done () + + +[] +module TheEventuallyBuilder = + let eventually = new EventuallyBuilder() + +type FakeDisposable = + FakeDisposable of int + with + interface System.IDisposable with + member __.Dispose() = () // No-op disposal is precisely what makes this a fake diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst new file mode 100644 index 0000000000..8c9d61f847 --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst @@ -0,0 +1,4 @@ + SOURCE=builderlib.fs SCFLAGS="-a --optimize-" COMPILE_ONLY=1 # builderlib.fs (setup) + + SOURCE=E_LetBangAndBangNotTerminatedWithReturn.fs SCFLAGS="-r:ComputationExprLibrary.dll" + SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:ComputationExprLibrary.dll" From 56bd67b9b639a9d35b2d1eedd921e9ca31053caf Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 1 Oct 2018 10:57:12 +0100 Subject: [PATCH 183/255] Add new applicative CE error messages to FSharp.Compiler.Private --- src/buildfromsource/FSharp.Compiler.Private/FSComp.fs | 8 ++++++++ src/buildfromsource/FSharp.Compiler.Private/FSComp.resx | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs b/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs index f6fc5a409e..e8eeee53b8 100644 --- a/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs +++ b/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs @@ -4357,6 +4357,12 @@ type internal SR private() = /// This type does not inherit Attribute, it will not work correctly with other .NET languages. /// (Originally from ..\FSComp.txt:1443) static member tcTypeDoesNotInheritAttribute() = (3242, GetStringFunc("tcTypeDoesNotInheritAttribute",",,,") ) + /// Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. + /// (Originally from ..\FSComp.txt:1444) + static member tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn() = (3243, GetStringFunc("tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn",",,,") ) + /// Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '. + /// (Originally from ..\FSComp.txt:1445) + static member tcInvalidAndUseBangBinding() = (3244, GetStringFunc("tcInvalidAndUseBangBinding",",,,") ) /// Call this method once to validate that all known resources are valid; throws if not static member RunStartupValidation() = @@ -5774,4 +5780,6 @@ type internal SR private() = ignore(GetString("chkNoSpanLikeValueFromExpression")) ignore(GetString("tastCantTakeAddressOfExpression")) ignore(GetString("tcTypeDoesNotInheritAttribute")) + ignore(GetString("tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn")) + ignore(GetString("tcInvalidAndUseBangBinding")) () diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx b/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx index be8589241b..36f7545f11 100644 --- a/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx +++ b/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx @@ -4360,4 +4360,10 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. + + Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + \ No newline at end of file From 34b139244f9d0cd8f3f9498a2e25c8c6955a911d Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 1 Oct 2018 10:59:36 +0100 Subject: [PATCH 184/255] Add new applicative CE tests to main test.lst --- tests/fsharpqa/Source/test.lst | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fsharpqa/Source/test.lst b/tests/fsharpqa/Source/test.lst index 67199ab113..cbdc1917e2 100644 --- a/tests/fsharpqa/Source/test.lst +++ b/tests/fsharpqa/Source/test.lst @@ -267,6 +267,7 @@ Misc01 Warnings Misc01 ErrorMessages\NameResolution Misc01 ErrorMessages\UnitGenericAbstractType Misc01 ErrorMessages\ConfusingTypeName +Misc01 ErrorMessages\ApplicativeComputationExpressions Misc02 Libraries\Portable Misc02 Misc From b845f520071e3ec5198100cb6ce641852c89d792 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 1 Oct 2018 12:03:42 +0100 Subject: [PATCH 185/255] Make new applicative CE error msg tests pass locally --- .../E_LetBangAndBangNotTerminatedWithReturn.fs | 6 +++--- .../E_PatternOnLhsOfUseBangAndUseBangBinding.fs | 6 +++--- .../ErrorMessages/ApplicativeComputationExpressions/env.lst | 6 ++---- tests/fsharpqa/Source/test.lst | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs index 9d740ef7dc..c0d9ef189f 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs @@ -1,10 +1,10 @@ // #ErrorMessages -//Expecting 'return' but saw something else. -//Applicative computation expressions must be of the form 'let! = -//and! = and! ... and! = return '. +//Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. namespace ApplicativeComputationExpressions +module E_LetBangAndBangNotTerminatedWithReturn = + eventually { let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) and! y = Eventually.Done 6 diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_PatternOnLhsOfUseBangAndUseBangBinding.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_PatternOnLhsOfUseBangAndUseBangBinding.fs index 171e0d0db8..4bf4bb64e9 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_PatternOnLhsOfUseBangAndUseBangBinding.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_PatternOnLhsOfUseBangAndUseBangBinding.fs @@ -1,10 +1,10 @@ // #ErrorMessages -//Pattern matching is not allowed on the left-hand side of the equals. -//'use! ... anduse! ...' bindings must be of the form -//'use! = ' or 'anduse! = '. +//Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '. namespace ApplicativeComputationExpressions +module E_PatternOnLhsOfUseBangAndUseBangBinding = + eventually { use! (x,_) = Eventually.Done (FakeDisposable 3, 19) anduse! y = Eventually.Done (FakeDisposable 11) diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst index 8c9d61f847..b62f3467e9 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst @@ -1,4 +1,2 @@ - SOURCE=builderlib.fs SCFLAGS="-a --optimize-" COMPILE_ONLY=1 # builderlib.fs (setup) - - SOURCE=E_LetBangAndBangNotTerminatedWithReturn.fs SCFLAGS="-r:ComputationExprLibrary.dll" - SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:ComputationExprLibrary.dll" + SOURCE=E_LetBangAndBangNotTerminatedWithReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" + SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" diff --git a/tests/fsharpqa/Source/test.lst b/tests/fsharpqa/Source/test.lst index cbdc1917e2..25f9ddd9b0 100644 --- a/tests/fsharpqa/Source/test.lst +++ b/tests/fsharpqa/Source/test.lst @@ -267,7 +267,7 @@ Misc01 Warnings Misc01 ErrorMessages\NameResolution Misc01 ErrorMessages\UnitGenericAbstractType Misc01 ErrorMessages\ConfusingTypeName -Misc01 ErrorMessages\ApplicativeComputationExpressions +Misc01,AppCE ErrorMessages\ApplicativeComputationExpressions Misc02 Libraries\Portable Misc02 Misc From f88bcce38ae77125d4289854c24ca8aa84d2a43a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 1 Oct 2018 14:44:48 +0100 Subject: [PATCH 186/255] Sketch unit tests which require support for applicative CEs --- .../ApplicativeComputationExpressions.fs | 145 ++++++++++++++++++ .../FSharp.Compiler.UnitTests.fsproj | 5 +- 2 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 tests/FSharp.Compiler.UnitTests/ApplicativeComputationExpressions.fs diff --git a/tests/FSharp.Compiler.UnitTests/ApplicativeComputationExpressions.fs b/tests/FSharp.Compiler.UnitTests/ApplicativeComputationExpressions.fs new file mode 100644 index 0000000000..5beec43590 --- /dev/null +++ b/tests/FSharp.Compiler.UnitTests/ApplicativeComputationExpressions.fs @@ -0,0 +1,145 @@ +namespace FSharp.Compiler.UnitTests + +open NUnit.Framework + +/// Used for tracking what operations a Trace builder was asked to perform +type TraceOp = + | Apply of arg : obj // We only capture the arg here, because function equality is awkward + | Return // Similarly, we don't capture the arg here for reasons of function equality pain + | EnterUsing of resource : obj + | StartUsingBody of resource : obj + | EndUsingBody of resource : obj + | ExitUsing of resource : obj + +/// A pseudo identity functor +type 'a Trace = + Trace of 'a + with + override this.ToString () = + sprintf "%+A" this + +/// A builder which records what operations it is asked to perform +type TraceBuilder() = + + let mutable trace : TraceOp list = [] + + member __.GetTrace () = trace + + member __.Apply((Trace f), (Trace x) as xTrace) = + trace <- Apply xTrace :: trace + Trace (f x) + + member __.Return(x) = + trace <- Return :: trace + Trace x + + member __.MapTryFinally(body, compensation) = + try + body () + finally + compensation () + + /// Doesn't actually do any disposing here, since we are just interesting + /// in checking the order of events in this test + member __.MapUsing(resource(*:#System.IDisposable*), body) = + trace <- EnterUsing resource :: trace + let body' = fun () -> + trace <- StartUsingBody resource :: trace + let res = body resource + trace <- EndUsingBody resource :: trace + res + __.MapTryFinally(body', fun () -> + trace <- ExitUsing resource :: trace + (*resource.Dispose()*) + ()) + +[] +module ApplicativeComputationExpressionDesugaring = + + [] + let ``let! ... and! ... return ... desugars to calls to builder.Apply and builder.Return to compute its result`` () = + let trace = TraceBuilder() + + let ceResult : int Trace = + trace { + let! x = Trace 1 + and! y = Trace 2 + return 1 + 2 + } + + Assert.IsTrue( + trace.GetTrace () = + [ + Apply 2 + Apply 1 + Return + ] + ) + + Assert.areEqual(Trace 3, ceResult) + + [] + let ``use! ... anduse! ... return ... desugars to calls to builder.Apply, builder.Return and builder.MapUsing to compute its result`` () = + let trace = TraceBuilder() + + let ceResult : int Trace = + trace { + use! x = Trace 1 + anduse! y = Trace 2 + return 1 + 2 + } + + Assert.IsTrue( + trace.GetTrace () = + [ + Apply 2 + Apply 1 + Return + EnterUsing 1 + EnterUsing 2 + StartUsingBody 1 + StartUsingBody 2 + EndUsingBody 2 + EndUsingBody 1 + ExitUsing 2 + ExitUsing 1 + ] + ) + + Assert.areEqual(Trace 3, ceResult) + + [] + let ``Mixed use of managed resource args and not desugars to calls to MapUsing only where implied by the CE syntax`` () = + let trace = TraceBuilder() + + let ceResult : int Trace = + trace { + use! x = Trace 1 + and! y = Trace 2 // Explicitly _not_ treated as a resource to be managed + anduse! z = Trace 3 + return 1 + 2 + 3 + } + + Assert.IsTrue( + trace.GetTrace () = + [ + Apply 3 + Apply 2 + Apply 1 + Return + EnterUsing 1 + //EnterUsing 2 + EnterUsing 3 + StartUsingBody 1 + //StartUsingBody 2 + StartUsingBody 3 + EndUsingBody 3 + //EndUsingBody 2 + EndUsingBody 1 + ExitUsing 3 + //ExitUsing 2 + ExitUsing 1 + ] + ) + + Assert.areEqual(Trace 6, ceResult) diff --git a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj index 06b8a4b422..99c07c66c9 100644 --- a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj +++ b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj @@ -44,7 +44,7 @@ - ..\..\..\packages\System.ValueTuple.$(SystemValueTuplePackageVersion)\lib\netstandard1.0\System.ValueTuple.dll + ..\..\..\packages\System.ValueTuple.$(SystemValueTuplePackageVersion)\lib\netstandard1.0\System.ValueTuple.dll @@ -54,6 +54,7 @@ + @@ -66,4 +67,4 @@ - \ No newline at end of file + From 9826ea0ac13be3978d645b087aa1716703d59968 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 1 Oct 2018 15:28:56 +0100 Subject: [PATCH 187/255] Move happy-path applicative CE tests to their own fsharpqa directory --- .../ApplicativeComputationExpressions.fs | 145 ------------------ .../FSharp.Compiler.UnitTests.fsproj | 3 +- .../LetBangAndBangDesugarsCorrectly.fs | 16 ++ .../MixOfUseAndLetDesugarsCorrectly.fs | 18 +++ .../Source/ComputationExpressions/TraceLib.fs | 50 ++++++ .../UseBangAndUseBangDesugarsCorrectly.fs | 16 ++ .../Source/ComputationExpressions/env.lst | 3 + tests/fsharpqa/Source/test.lst | 1 + 8 files changed, 105 insertions(+), 147 deletions(-) delete mode 100644 tests/FSharp.Compiler.UnitTests/ApplicativeComputationExpressions.fs create mode 100644 tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs create mode 100644 tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs create mode 100644 tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs create mode 100644 tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs create mode 100644 tests/fsharpqa/Source/ComputationExpressions/env.lst diff --git a/tests/FSharp.Compiler.UnitTests/ApplicativeComputationExpressions.fs b/tests/FSharp.Compiler.UnitTests/ApplicativeComputationExpressions.fs deleted file mode 100644 index 5beec43590..0000000000 --- a/tests/FSharp.Compiler.UnitTests/ApplicativeComputationExpressions.fs +++ /dev/null @@ -1,145 +0,0 @@ -namespace FSharp.Compiler.UnitTests - -open NUnit.Framework - -/// Used for tracking what operations a Trace builder was asked to perform -type TraceOp = - | Apply of arg : obj // We only capture the arg here, because function equality is awkward - | Return // Similarly, we don't capture the arg here for reasons of function equality pain - | EnterUsing of resource : obj - | StartUsingBody of resource : obj - | EndUsingBody of resource : obj - | ExitUsing of resource : obj - -/// A pseudo identity functor -type 'a Trace = - Trace of 'a - with - override this.ToString () = - sprintf "%+A" this - -/// A builder which records what operations it is asked to perform -type TraceBuilder() = - - let mutable trace : TraceOp list = [] - - member __.GetTrace () = trace - - member __.Apply((Trace f), (Trace x) as xTrace) = - trace <- Apply xTrace :: trace - Trace (f x) - - member __.Return(x) = - trace <- Return :: trace - Trace x - - member __.MapTryFinally(body, compensation) = - try - body () - finally - compensation () - - /// Doesn't actually do any disposing here, since we are just interesting - /// in checking the order of events in this test - member __.MapUsing(resource(*:#System.IDisposable*), body) = - trace <- EnterUsing resource :: trace - let body' = fun () -> - trace <- StartUsingBody resource :: trace - let res = body resource - trace <- EndUsingBody resource :: trace - res - __.MapTryFinally(body', fun () -> - trace <- ExitUsing resource :: trace - (*resource.Dispose()*) - ()) - -[] -module ApplicativeComputationExpressionDesugaring = - - [] - let ``let! ... and! ... return ... desugars to calls to builder.Apply and builder.Return to compute its result`` () = - let trace = TraceBuilder() - - let ceResult : int Trace = - trace { - let! x = Trace 1 - and! y = Trace 2 - return 1 + 2 - } - - Assert.IsTrue( - trace.GetTrace () = - [ - Apply 2 - Apply 1 - Return - ] - ) - - Assert.areEqual(Trace 3, ceResult) - - [] - let ``use! ... anduse! ... return ... desugars to calls to builder.Apply, builder.Return and builder.MapUsing to compute its result`` () = - let trace = TraceBuilder() - - let ceResult : int Trace = - trace { - use! x = Trace 1 - anduse! y = Trace 2 - return 1 + 2 - } - - Assert.IsTrue( - trace.GetTrace () = - [ - Apply 2 - Apply 1 - Return - EnterUsing 1 - EnterUsing 2 - StartUsingBody 1 - StartUsingBody 2 - EndUsingBody 2 - EndUsingBody 1 - ExitUsing 2 - ExitUsing 1 - ] - ) - - Assert.areEqual(Trace 3, ceResult) - - [] - let ``Mixed use of managed resource args and not desugars to calls to MapUsing only where implied by the CE syntax`` () = - let trace = TraceBuilder() - - let ceResult : int Trace = - trace { - use! x = Trace 1 - and! y = Trace 2 // Explicitly _not_ treated as a resource to be managed - anduse! z = Trace 3 - return 1 + 2 + 3 - } - - Assert.IsTrue( - trace.GetTrace () = - [ - Apply 3 - Apply 2 - Apply 1 - Return - EnterUsing 1 - //EnterUsing 2 - EnterUsing 3 - StartUsingBody 1 - //StartUsingBody 2 - StartUsingBody 3 - EndUsingBody 3 - //EndUsingBody 2 - EndUsingBody 1 - ExitUsing 3 - //ExitUsing 2 - ExitUsing 1 - ] - ) - - Assert.areEqual(Trace 6, ceResult) diff --git a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj index 99c07c66c9..5e682e8692 100644 --- a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj +++ b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj @@ -54,7 +54,6 @@ - @@ -67,4 +66,4 @@ - + \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs new file mode 100644 index 0000000000..c456b440ed --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs @@ -0,0 +1,16 @@ +// #Misc + +//val result : Trace int = Trace 3 +//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ] + +let result, trace = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + let! x = Trace 1 + and! y = Trace 2 + return 1 + 2 + } + + ceResult, tracer.GetTrace () \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs new file mode 100644 index 0000000000..d3f878977a --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs @@ -0,0 +1,18 @@ +// #Misc + +//val result : Trace int = Trace 6 +//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; StartUsingBody 1 ; EndUsingBody 1 ; ExitUsing 1 ] + + +let result, trace = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + use! x = Trace 1 + and! y = Trace 2 // Explicitly _not_ treated as a resource to be managed + anduse! z = Trace 3 + return 1 + 2 + 3 + } + + ceResult, tracer.GetTrace () \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs b/tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs new file mode 100644 index 0000000000..f030028bb3 --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs @@ -0,0 +1,50 @@ +/// Used for tracking what operations a Trace builder was asked to perform +type TraceOp = + | Apply of arg : obj // We only capture the arg here, because function equality is awkward + | Return // Similarly, we don't capture the arg here for reasons of function equality pain + | EnterUsing of resource : obj + | StartUsingBody of resource : obj + | EndUsingBody of resource : obj + | ExitUsing of resource : obj + +/// A pseudo identity functor +type 'a Trace = + Trace of 'a + with + override this.ToString () = + sprintf "%+A" this + +/// A builder which records what operations it is asked to perform +type TraceBuilder() = + + let mutable trace : TraceOp list = [] + + member __.GetTrace () = trace + + member __.Apply((Trace f), (Trace x) as xTrace) = + trace <- Apply xTrace :: trace + Trace (f x) + + member __.Return(x) = + trace <- Return :: trace + Trace x + + member __.MapTryFinally(body, compensation) = + try + body () + finally + compensation () + + /// Doesn't actually do any disposing here, since we are just interesting + /// in checking the order of events in this test + member __.MapUsing(resource(*:#System.IDisposable*), body) = + trace <- EnterUsing resource :: trace + let body' = fun () -> + trace <- StartUsingBody resource :: trace + let res = body resource + trace <- EndUsingBody resource :: trace + res + __.MapTryFinally(body', fun () -> + trace <- ExitUsing resource :: trace + (*resource.Dispose()*) + ()) diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs new file mode 100644 index 0000000000..e59a34185b --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs @@ -0,0 +1,16 @@ +// #Misc + +//val result : Trace int = Trace 3 +//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; EnterUsing 2 ; StartUsingBody 1 ; StartUsingBody 2 ; EndUsingBody 2 ; EndUsingBody 1 ; ExitUsing 2 ; ExitUsing 1 ] + +let result, trace = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + use! x = Trace 1 + anduse! y = Trace 2 + return 1 + 2 + } + + ceResult, tracer.GetTrace () diff --git a/tests/fsharpqa/Source/ComputationExpressions/env.lst b/tests/fsharpqa/Source/ComputationExpressions/env.lst new file mode 100644 index 0000000000..853078be79 --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/env.lst @@ -0,0 +1,3 @@ + SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="-r:TraceLib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library TraceLib.fs" + SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="-r:TraceLib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library TraceLib.fs" + SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="-r:TraceLib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library TraceLib.fs" diff --git a/tests/fsharpqa/Source/test.lst b/tests/fsharpqa/Source/test.lst index 25f9ddd9b0..5b7cb0a2d6 100644 --- a/tests/fsharpqa/Source/test.lst +++ b/tests/fsharpqa/Source/test.lst @@ -268,6 +268,7 @@ Misc01 ErrorMessages\NameResolution Misc01 ErrorMessages\UnitGenericAbstractType Misc01 ErrorMessages\ConfusingTypeName Misc01,AppCE ErrorMessages\ApplicativeComputationExpressions +Misc01,AppCE ComputationExpressions Misc02 Libraries\Portable Misc02 Misc From 9816252924207aa2255d2ba1d427e711aae9c5c8 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 1 Oct 2018 16:44:44 +0100 Subject: [PATCH 188/255] Move happy-path applicative CE tests towards a fsx style --- .../fsharpqa/Source/CodeGen/EmittedIL/.il.bsl | 0 .../LetBangAndBangDesugarsCorrectly.fs | 24 ++++++++------- .../MixOfUseAndLetDesugarsCorrectly.fs | 29 +++++++++++-------- .../Source/ComputationExpressions/TraceLib.fs | 2 ++ .../UseBangAndUseBangDesugarsCorrectly.fs | 26 ++++++++++------- 5 files changed, 49 insertions(+), 32 deletions(-) delete mode 100644 tests/fsharpqa/Source/CodeGen/EmittedIL/.il.bsl diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/.il.bsl b/tests/fsharpqa/Source/CodeGen/EmittedIL/.il.bsl deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs index c456b440ed..4a347b076f 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs +++ b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs @@ -1,16 +1,20 @@ -// #Misc +// #Misc #AppCE //val result : Trace int = Trace 3 //val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ] -let result, trace = - let tracer = TraceBuilder() +namespace ComputationsExpressions.Test - let ceResult : int Trace = - tracer { - let! x = Trace 1 - and! y = Trace 2 - return 1 + 2 - } +module LetBangAndBangDesugarsCorrectly = - ceResult, tracer.GetTrace () \ No newline at end of file + let result, trace = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + let! x = Trace 1 + and! y = Trace 2 + return 1 + 2 + } + + ceResult, tracer.GetTrace () \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs index d3f878977a..e91f169e47 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs +++ b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs @@ -1,18 +1,23 @@ -// #Misc +// #Misc #AppCE //val result : Trace int = Trace 6 //val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; StartUsingBody 1 ; EndUsingBody 1 ; ExitUsing 1 ] +namespace ComputationsExpressions.Test -let result, trace = - let tracer = TraceBuilder() +module MixOfUseAndLetDesugarsCorrectly = - let ceResult : int Trace = - tracer { - use! x = Trace 1 - and! y = Trace 2 // Explicitly _not_ treated as a resource to be managed - anduse! z = Trace 3 - return 1 + 2 + 3 - } - - ceResult, tracer.GetTrace () \ No newline at end of file + let result, trace = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + use! x = Trace 1 + and! y = Trace 2 // Explicitly _not_ treated as a resource to be managed + anduse! z = Trace 3 + return 1 + 2 + 3 + } + + ceResult, tracer.GetTrace () + + printf "%+A, %+A" result trace \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs b/tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs index f030028bb3..3dd582b26a 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs +++ b/tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs @@ -1,3 +1,5 @@ +namespace ComputationsExpressions.Test + /// Used for tracking what operations a Trace builder was asked to perform type TraceOp = | Apply of arg : obj // We only capture the arg here, because function equality is awkward diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs index e59a34185b..6443acd1d2 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs +++ b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs @@ -1,16 +1,22 @@ -// #Misc +// #Misc #AppCE //val result : Trace int = Trace 3 //val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; EnterUsing 2 ; StartUsingBody 1 ; StartUsingBody 2 ; EndUsingBody 2 ; EndUsingBody 1 ; ExitUsing 2 ; ExitUsing 1 ] -let result, trace = - let tracer = TraceBuilder() +namespace ComputationsExpressions.Test - let ceResult : int Trace = - tracer { - use! x = Trace 1 - anduse! y = Trace 2 - return 1 + 2 - } +module UseBangAndUseBangDesugarsCorrectly = - ceResult, tracer.GetTrace () + let result, trace = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + use! x = Trace 1 + anduse! y = Trace 2 + return 1 + 2 + } + + ceResult, tracer.GetTrace () + + printf "%+A, %+A" result trace From bd93b046bcf8bf36b13755c1d4dcfa43f4bd8524 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 1 Oct 2018 17:37:07 +0100 Subject: [PATCH 189/255] Make new CE tests closer to existing ones --- .../LetBangAndBangDesugarsCorrectly.fs | 20 ---------------- .../LetBangAndBangDesugarsCorrectly.fsx | 20 ++++++++++++++++ .../MixOfUseAndLetDesugarsCorrectly.fs | 23 ------------------- .../MixOfUseAndLetDesugarsCorrectly.fsx | 21 +++++++++++++++++ .../UseBangAndUseBangDesugarsCorrectly.fs | 22 ------------------ .../UseBangAndUseBangDesugarsCorrectly.fsx | 20 ++++++++++++++++ .../Source/ComputationExpressions/env.lst | 6 ++--- 7 files changed, 64 insertions(+), 68 deletions(-) delete mode 100644 tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs create mode 100644 tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx delete mode 100644 tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs create mode 100644 tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx delete mode 100644 tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs create mode 100644 tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs deleted file mode 100644 index 4a347b076f..0000000000 --- a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs +++ /dev/null @@ -1,20 +0,0 @@ -// #Misc #AppCE - -//val result : Trace int = Trace 3 -//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ] - -namespace ComputationsExpressions.Test - -module LetBangAndBangDesugarsCorrectly = - - let result, trace = - let tracer = TraceBuilder() - - let ceResult : int Trace = - tracer { - let! x = Trace 1 - and! y = Trace 2 - return 1 + 2 - } - - ceResult, tracer.GetTrace () \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx new file mode 100644 index 0000000000..603ca3c901 --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx @@ -0,0 +1,20 @@ +// #Misc #AppCE + +//val result : Trace int = Trace 3 +//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ] + +open ComputationsExpressions.Test + +let result, trace = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + let! x = Trace 1 + and! y = Trace 2 + return 1 + 2 + } + + ceResult, tracer.GetTrace () +;; +#q;; \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs deleted file mode 100644 index e91f169e47..0000000000 --- a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs +++ /dev/null @@ -1,23 +0,0 @@ -// #Misc #AppCE - -//val result : Trace int = Trace 6 -//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; StartUsingBody 1 ; EndUsingBody 1 ; ExitUsing 1 ] - -namespace ComputationsExpressions.Test - -module MixOfUseAndLetDesugarsCorrectly = - - let result, trace = - let tracer = TraceBuilder() - - let ceResult : int Trace = - tracer { - use! x = Trace 1 - and! y = Trace 2 // Explicitly _not_ treated as a resource to be managed - anduse! z = Trace 3 - return 1 + 2 + 3 - } - - ceResult, tracer.GetTrace () - - printf "%+A, %+A" result trace \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx new file mode 100644 index 0000000000..94a11f01a9 --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx @@ -0,0 +1,21 @@ +// #Misc #AppCE + +//val result : Trace int = Trace 6 +//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; StartUsingBody 1 ; EndUsingBody 1 ; ExitUsing 1 ] + +open ComputationsExpressions.Test + +let result, trace = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + use! x = Trace 1 + and! y = Trace 2 // Explicitly _not_ treated as a resource to be managed + anduse! z = Trace 3 + return 1 + 2 + 3 + } + + ceResult, tracer.GetTrace () +;; +#q;; \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs deleted file mode 100644 index 6443acd1d2..0000000000 --- a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs +++ /dev/null @@ -1,22 +0,0 @@ -// #Misc #AppCE - -//val result : Trace int = Trace 3 -//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; EnterUsing 2 ; StartUsingBody 1 ; StartUsingBody 2 ; EndUsingBody 2 ; EndUsingBody 1 ; ExitUsing 2 ; ExitUsing 1 ] - -namespace ComputationsExpressions.Test - -module UseBangAndUseBangDesugarsCorrectly = - - let result, trace = - let tracer = TraceBuilder() - - let ceResult : int Trace = - tracer { - use! x = Trace 1 - anduse! y = Trace 2 - return 1 + 2 - } - - ceResult, tracer.GetTrace () - - printf "%+A, %+A" result trace diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx new file mode 100644 index 0000000000..025f174e4f --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx @@ -0,0 +1,20 @@ +// #Misc #AppCE + +//val result : Trace int = Trace 3 +//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; EnterUsing 2 ; StartUsingBody 1 ; StartUsingBody 2 ; EndUsingBody 2 ; EndUsingBody 1 ; ExitUsing 2 ; ExitUsing 1 ] + +open ComputationsExpressions.Test + +let result, trace = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + use! x = Trace 1 + anduse! y = Trace 2 + return 1 + 2 + } + + ceResult, tracer.GetTrace () +;; +#q;; diff --git a/tests/fsharpqa/Source/ComputationExpressions/env.lst b/tests/fsharpqa/Source/ComputationExpressions/env.lst index 853078be79..92c032685e 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ComputationExpressions/env.lst @@ -1,3 +1,3 @@ - SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="-r:TraceLib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library TraceLib.fs" - SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="-r:TraceLib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library TraceLib.fs" - SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="-r:TraceLib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library TraceLib.fs" + SOURCE=LetBangAndBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:TraceLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # let! ... and! ... desugars correctly + SOURCE=UseBangAndUseBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:TraceLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # use! ... anduse! ... desugars correctly + SOURCE=MixOfUseAndLetDesugarsCorrectly.fsx SCFLAGS="--nologo -r:TraceLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # use! ... and! ... anduse! ... desugars correctly \ No newline at end of file From eb6e16e3e0e2173cc214cdcf0298357c90987ff7 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 1 Oct 2018 18:41:10 +0100 Subject: [PATCH 190/255] Reshuffle new tests in somewhat desparate hopes for a cache miss --- .../{TraceLib.fs => ApplicativeBuilderLib.fs} | 0 tests/fsharpqa/Source/ComputationExpressions/env.lst | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename tests/fsharpqa/Source/ComputationExpressions/{TraceLib.fs => ApplicativeBuilderLib.fs} (100%) diff --git a/tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs b/tests/fsharpqa/Source/ComputationExpressions/ApplicativeBuilderLib.fs similarity index 100% rename from tests/fsharpqa/Source/ComputationExpressions/TraceLib.fs rename to tests/fsharpqa/Source/ComputationExpressions/ApplicativeBuilderLib.fs diff --git a/tests/fsharpqa/Source/ComputationExpressions/env.lst b/tests/fsharpqa/Source/ComputationExpressions/env.lst index 92c032685e..21d453a1c8 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ComputationExpressions/env.lst @@ -1,3 +1,3 @@ - SOURCE=LetBangAndBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:TraceLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # let! ... and! ... desugars correctly - SOURCE=UseBangAndUseBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:TraceLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # use! ... anduse! ... desugars correctly - SOURCE=MixOfUseAndLetDesugarsCorrectly.fsx SCFLAGS="--nologo -r:TraceLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # use! ... and! ... anduse! ... desugars correctly \ No newline at end of file + SOURCE=LetBangAndBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # let! ... and! ... desugars correctly + SOURCE=UseBangAndUseBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # use! ... anduse! ... desugars correctly + SOURCE=MixOfUseAndLetDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # use! ... and! ... anduse! ... desugars correctly \ No newline at end of file From a7832b6e476d6fcd7b798c3ec5cacecbf7335512 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 2 Oct 2018 13:16:34 +0100 Subject: [PATCH 191/255] Make new applicative CE tests match FSI output correctly --- .../ComputationExpressions/ApplicativeBuilderLib.fs | 12 ++++++------ .../LetBangAndBangDesugarsCorrectly.fsx | 6 +++--- .../MixOfUseAndLetDesugarsCorrectly.fsx | 8 +++++--- .../UseBangAndUseBangDesugarsCorrectly.fsx | 8 +++++--- tests/fsharpqa/Source/ComputationExpressions/env.lst | 6 +++--- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/tests/fsharpqa/Source/ComputationExpressions/ApplicativeBuilderLib.fs b/tests/fsharpqa/Source/ComputationExpressions/ApplicativeBuilderLib.fs index 3dd582b26a..8f0526403c 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/ApplicativeBuilderLib.fs +++ b/tests/fsharpqa/Source/ComputationExpressions/ApplicativeBuilderLib.fs @@ -1,9 +1,9 @@ -namespace ComputationsExpressions.Test +namespace ApplicativeBuilderLib /// Used for tracking what operations a Trace builder was asked to perform type TraceOp = - | Apply of arg : obj // We only capture the arg here, because function equality is awkward - | Return // Similarly, we don't capture the arg here for reasons of function equality pain + | Apply + | Return | EnterUsing of resource : obj | StartUsingBody of resource : obj | EndUsingBody of resource : obj @@ -21,10 +21,10 @@ type TraceBuilder() = let mutable trace : TraceOp list = [] - member __.GetTrace () = trace + member __.GetTrace () = List.rev trace - member __.Apply((Trace f), (Trace x) as xTrace) = - trace <- Apply xTrace :: trace + member __.Apply((Trace f) as fTrace, (Trace x) as xTrace) = + trace <- Apply :: trace Trace (f x) member __.Return(x) = diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx index 603ca3c901..0d0f3ba490 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx +++ b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx @@ -1,9 +1,9 @@ // #Misc #AppCE -//val result : Trace int = Trace 3 -//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ] +//val result : int ApplicativeBuilderLib.Trace = Trace 3 +//val trace : ApplicativeBuilderLib.TraceOp list = .Return; Apply; Apply. -open ComputationsExpressions.Test +open ApplicativeBuilderLib let result, trace = let tracer = TraceBuilder() diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx index 94a11f01a9..d6909f8659 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx +++ b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx @@ -1,9 +1,11 @@ // #Misc #AppCE -//val result : Trace int = Trace 6 -//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; StartUsingBody 1 ; EndUsingBody 1 ; ExitUsing 1 ] +//val result : int ApplicativeBuilderLib.Trace = Trace 6 +//val trace : ApplicativeBuilderLib.TraceOp list = +// .Return; Apply; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 3; +// StartUsingBody 3; EndUsingBody 3; ExitUsing 3; EndUsingBody 1; ExitUsing 1. -open ComputationsExpressions.Test +open ApplicativeBuilderLib let result, trace = let tracer = TraceBuilder() diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx index 025f174e4f..e57fae9573 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx +++ b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx @@ -1,9 +1,11 @@ // #Misc #AppCE -//val result : Trace int = Trace 3 -//val trace : TraceOp list = [ Apply 2 ; Apply 1 ; Return ; EnterUsing 1 ; EnterUsing 2 ; StartUsingBody 1 ; StartUsingBody 2 ; EndUsingBody 2 ; EndUsingBody 1 ; ExitUsing 2 ; ExitUsing 1 ] +//val result : int ApplicativeBuilderLib.Trace = Trace 3 +//val trace : ApplicativeBuilderLib.TraceOp list = +// .Return; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 2; +// StartUsingBody 2; EndUsingBody 2; ExitUsing 2; EndUsingBody 1; ExitUsing 1. -open ComputationsExpressions.Test +open ApplicativeBuilderLib let result, trace = let tracer = TraceBuilder() diff --git a/tests/fsharpqa/Source/ComputationExpressions/env.lst b/tests/fsharpqa/Source/ComputationExpressions/env.lst index 21d453a1c8..fea79faa46 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ComputationExpressions/env.lst @@ -1,3 +1,3 @@ - SOURCE=LetBangAndBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # let! ... and! ... desugars correctly - SOURCE=UseBangAndUseBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # use! ... anduse! ... desugars correctly - SOURCE=MixOfUseAndLetDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a TraceLib.fs" # use! ... and! ... anduse! ... desugars correctly \ No newline at end of file + SOURCE=LetBangAndBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # let! ... and! ... desugars correctly + SOURCE=UseBangAndUseBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... anduse! ... desugars correctly + SOURCE=MixOfUseAndLetDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... and! ... anduse! ... desugars correctly \ No newline at end of file From e4cb70e5e5f7eb8d18aec9b2c76c30b8d49136e4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 2 Oct 2018 13:48:23 +0100 Subject: [PATCH 192/255] Accept empty ine output by FSI --- .../ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx | 1 + .../ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx | 1 + .../UseBangAndUseBangDesugarsCorrectly.fsx | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx index 0d0f3ba490..05f08f3ef5 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx +++ b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx @@ -1,5 +1,6 @@ // #Misc #AppCE +//^$ //val result : int ApplicativeBuilderLib.Trace = Trace 3 //val trace : ApplicativeBuilderLib.TraceOp list = .Return; Apply; Apply. diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx index d6909f8659..a4097d95ff 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx +++ b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx @@ -1,5 +1,6 @@ // #Misc #AppCE +//^$ //val result : int ApplicativeBuilderLib.Trace = Trace 6 //val trace : ApplicativeBuilderLib.TraceOp list = // .Return; Apply; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 3; diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx index e57fae9573..9166d288af 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx +++ b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx @@ -1,5 +1,6 @@ // #Misc #AppCE +//^$ //val result : int ApplicativeBuilderLib.Trace = Trace 3 //val trace : ApplicativeBuilderLib.TraceOp list = // .Return; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 2; From 408b4cd6801c7e912af9c2752bd02bc08d5038e4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 2 Oct 2018 15:09:49 +0100 Subject: [PATCH 193/255] Move to less noisy Expects syntax --- tests/fsharpqa/Source/.run.pl.swp | Bin 0 -> 4096 bytes .../LetBangAndBangDesugarsCorrectly.fsx | 5 ++--- .../MixOfUseAndLetDesugarsCorrectly.fsx | 1 - .../UseBangAndUseBangDesugarsCorrectly.fsx | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) create mode 100644 tests/fsharpqa/Source/.run.pl.swp diff --git a/tests/fsharpqa/Source/.run.pl.swp b/tests/fsharpqa/Source/.run.pl.swp new file mode 100644 index 0000000000000000000000000000000000000000..84ee222e47b62b5b46cba7ede9c1527a15f86e04 GIT binary patch literal 4096 zcmYc?2=nw+u+TGP00IF9hHtFB(IBRaI|D;VelCbf5V*Jodx!W3=mz;ay9EdM;#BW! zr5{?HT2!nLG+5syKe;qFHLs*t-z_*Jv8cc~Kd+=HGpSg=EVH;YF(<7UB&c7KS`1VH z;S?t72j`a-C8z2amFDRc^$ -//val result : int ApplicativeBuilderLib.Trace = Trace 3 -//val trace : ApplicativeBuilderLib.TraceOp list = .Return; Apply; Apply. +//#Expects: Success: val result : int ApplicativeBuilderLib.Trace = Trace 3 +//#Expects: Success: val trace : ApplicativeBuilderLib.TraceOp list = .Return; Apply; Apply. open ApplicativeBuilderLib diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx index a4097d95ff..d6909f8659 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx +++ b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx @@ -1,6 +1,5 @@ // #Misc #AppCE -//^$ //val result : int ApplicativeBuilderLib.Trace = Trace 6 //val trace : ApplicativeBuilderLib.TraceOp list = // .Return; Apply; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 3; diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx index 9166d288af..e57fae9573 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx +++ b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx @@ -1,6 +1,5 @@ // #Misc #AppCE -//^$ //val result : int ApplicativeBuilderLib.Trace = Trace 3 //val trace : ApplicativeBuilderLib.TraceOp list = // .Return; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 2; From c2d2cede077db7740ca58ae029fa663834da2045 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 2 Oct 2018 15:24:45 +0100 Subject: [PATCH 194/255] Move new applicative CE tests from scripts to normal source files --- tests/fsharpqa/Source/.run.pl.swp | Bin 4096 -> 24576 bytes .../LetBangAndBangDesugarsCorrectly.fs | 22 ++++++++++++++++ .../LetBangAndBangDesugarsCorrectly.fsx | 20 --------------- .../MixOfUseAndLetDesugarsCorrectly.fs | 24 ++++++++++++++++++ .../MixOfUseAndLetDesugarsCorrectly.fsx | 23 ----------------- .../UseBangAndUseBangDesugarsCorrectly.fs | 23 +++++++++++++++++ .../UseBangAndUseBangDesugarsCorrectly.fsx | 22 ---------------- .../Source/ComputationExpressions/env.lst | 6 ++--- 8 files changed, 72 insertions(+), 68 deletions(-) create mode 100644 tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs delete mode 100644 tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx create mode 100644 tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs delete mode 100644 tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx create mode 100644 tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs delete mode 100644 tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx diff --git a/tests/fsharpqa/Source/.run.pl.swp b/tests/fsharpqa/Source/.run.pl.swp index 84ee222e47b62b5b46cba7ede9c1527a15f86e04..681af8f4def68f79d152da60acd16c0c35074b20 100644 GIT binary patch literal 24576 zcmeI4Yj7mhb%4iq47=bEj0wS&a&j3>R-;9lSuLNjXkjREVLA5uNVr|rJ?_I;fD=zGpR&C-FBQ#0!Anc)P#OB0Eg-gwVxxtutf zNEF?gT>g*0iG1OBac+KSVJ%NzrEUa41M$WDK zO=ro=t~p-Is#ZJ+%=)%R3UhP2l`XifX4%d*TlGw%x@$RcL@a?=0y|0IlEgI!hY$26 zuTpQh?8Ht9;;>i(u>@iX#1e=l5KADIKrDe+0`nZH_Prk71+NSE3BL`l z-yPilnEN%ZUk>j7i~HNSzMTi;nICa~Gygst+`r7d%l)-vEb(uIx59(L{m*cH5*`cg z-@x@aT*if@kAJZQVhO|&h$Rq9AeKNZfmi~u1Y!xq5{M-bOCXlOPfr5Yaw4&Z>=b&q zod3Uze}3?D_yNER@CsOz75}kC*d=2A9zrJ0r=5H zq=9e27vQg99cCa48Mp}kV{anyB77YlgZIL3Lk(u(GI$>0JOCes`{6#g7b=j3e%J%w zypT4+U%`X$0Nep*;0O%Dei(pnT#!io1$+q3!&z8_$XI!n5#2_y~L$HlYls z;H_{ayo@2`X?P0$96k+a;56I*?gAlC;Y6-G<|c~x)Py-q@fj8 zLY(@Fit0K-9^*}(uvN>G7)$l*0mJG_jd*D(Te8sX}qQG&ehav)Z>$U6pb?oqDc0t&=i` z8JQi4#>_x$hcG_-)Raec5vOI!8q%dpJ&q(2Jy|k5hZQAf)29y%U4Q24fyIou2>#7V zJ<_S+!()dAsAa0e1Gh5fZXMNC>8>Af2c&$2Qy2N|&{(NK(W%)YC@&~nrS0>`#7pZ* z6~%5zfzsY6B*?#L(N|@Q&So6?PK~0-b&4rEIX!-~K&n(}G~s1Wt20**4I%9MvQ>5K zw%lh`c2sSTN@aEM*=@+qS+mVNJV9Bi2`)zonj0- z)vB_VmsQQBk-^}rwER}n9;CCFCib$uoI%o)PL&~~+?KB@Rcl51o!PwX*86=`6ES&q zok>U*NC!D*Rx+xyjRV1I7B+w?B~@~#W&|y4w;~9L8WG&EE{;!4N1Z0Jhyr%VN7gD?EmXg6Im~L^Vv|o#Wp=Milt=Jz>ZILdO)B_J zx4trK)oj`$6M``>)j648n3}w~P+XXsJt|B4mKwWLWs3_Z@>z$`*{2FN(yhpF-TQ{ZuX@FG#6DlHw&-XrNZ)F%*uEJs#qous`Qnx= z96Gb8xm!w!s6A+g}m5K231fM2D%yA4c-z|xkjb-9ku;jtJ&mjJ0FBf3-uUaOsRoU`J0eG z`gdyXRB3Ab(6x%v=cmVulXD9*r7&<%CrOx;{FU(WnTZ`z>(Ie=%57OnKu7%cTucrf zHM{BUhq@}oeMbhTre?3dsViQG3`!6GO4#z!G0R&;C~4Wvb&B1*lJoiFrToIe+=4Wa z_8;u&oOY^H;|sHrNKy^&nyPRjm&+FlI+Y%ZIQE)uUbCt#+hfw#h|RPs_DIHXKT9pEt2e^o_cnr3j61}T@!`! zy>_=0Q9;-2mLe>qSFTY_f-Xj)eX3VhG6kb??Qb-Kp}xafjHr8hSFe4gnYoD_d)35F zOi%CFn_w?ru+*A*Z68{*a|`*xiRq%`(JMebs&eCn-1tOZ50zf|b?z5; zAD5H77GH5l&a*y+33hLX>^z&chqBNk#FIDXw5Fo7kaqO1EtUIwx;lpjBk#O+qO!Lw z5!;Gu4o1l&r;6wF<|N8fcTOZ4uW#h$PxL13(v@`0*prh;4}#qKvZ>?L+{j45EuXV} zbI%*sDRa$GV`_;Hz3Bg+L+5-Ny;SsnIfMTfbo#Hrqwp~}2fqeazz@*nzXxB0_rYcG z5_Q3B(e3tw})i=BAA$q^?qIZN$`SRB4pUwn0wzPgvu&t4kvzla`MyBHwJf z%@NVQ*({*8vwyF|e3 zP#Ryz9YgcZ6;CYWN0KUI6!)Fuw}?(+^zm2KwgjgRU3<;JSI9ueV=Gb0n3X&9bi^|t z$~$pVS8makQeEoPW~zbWURp(y;xbSvgQjljT;<802y$tajd^5Sb#;5U1~SP~GP7$U zQ|Gqg$`$gIA38&%u=a>amoasyU5E- zyX}8D`v?j^P@U>d*vsv*m@#4pRx?)foY>37jy|X)2!+`4M#K`S_}NuYo47*pmVCD) zMs_vEfDQwVjZ1pONP7um*Eyw4EOI^(^hC|_v4AbvWlI!0%yuq zt=Psij{#ekZyKox^iVRSMl`ZRFkwSw*@@zNgs?46OB)H~^>Krz3eupWwW6?VnDq$( zEGAPDUJ4uOEhe>m)F~3}-cdUVF-WpNEIwUU(<8z=r}Xz>ScCtKcGd0b78a`9BPI!U|ji-^U*CPw*Ig3^rjA z#^4=r1^gHpJ_mmW55XtlLHJG3@-~;5e78`FYv1izzuM z5U;3g`^vnZO|%>lnCR%LGuj*um;@cyJxPKbq#W$k5f(o+$d*HmJW= zSBdJ=$``IX+KU2b9&|BtGUs-9Wi8p9^w{kAar}|4MJLnQ_Q~JE+=+!;eyrlUnTi(* zeJr`ycIsNPnnWeTXFRuN<8ebiRp*>N5(dvNj$g-WIF$9`6o2as_&hapwNVqE1*a(5}<@P$I!F(+)Xd zGbA-m89c^+qTP4vG2yroDNC$qF`}t(wxYystL~?hsdCLP_)Ru{QgU$DR_Em$n#95H zplH`g8To;%Dqwtbf_F{=}=L+J5q zXJ=roL3T#J4Z2%e6FwE_XZo?ZR#WT7IG%BqOA5+PYb8A}D=}UB6#s>{LqjT0UgB?q zk4?GhG-SMCz|r^NKG$TILDa8WeV6s2Tos49;?zujdhV$4ObGgR^OG|u>$bX~0m zOAJK~s?MUg3yKPRNo*a{?uwLd%p1WCjz|)&MMj}a1aY@>1HU2dM%c0bWit3Xy;>uP zNHMS;rc>sawESb4B0&x<)|EHjI1PA_?z+ji<88h<1&n_gTO&%MR<{i$qfILs(& zOSoo+nUMREg=ZMmOh;is&ay0qPC`Ku=|D$4z35$i20gW8!m%!HBF{)Y7jLu`T-2l= zwL@Cdt*Ia>=_1K@$}Qb1ws7qT+n}AOOdHQGChtfsCI>`Elv0PB4oDn8qTQxWWN$eQ;fZqOiu?pog~ral=*+NS!H}S5>t3zOm7pq2a&w@+k;XD%0ZoL^aeO6ev?uS`Gsktu^PUxIFGMCvwzR9Yy)U=u#dUImZY%6N_;-jaf2Brb{r6yCuc;sSg7_!>NhKdbxuZ-EQoVRZN3fxFiVJ>Ip)yY~Ofu3h$B@0jItDm8`P#rG*=$%58Y z7scSv!gm1enL}-S21Jkc7EgHgjaGeSyuKV%Q-CYcDVqfEn)TWDFk53{=9oTEcR3wq@0I>necbI_yaJK{ diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs new file mode 100644 index 0000000000..ffa31101e6 --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs @@ -0,0 +1,22 @@ +// #Misc #AppCE + +//#Expects: Success +// << OUTPUT +//Trace 3, \\[Return; Apply; Apply\\] +//OUTPUT + +open ApplicativeBuilderLib + +module LetBang = + + let () = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + let! x = Trace 1 + and! y = Trace 2 + return 1 + 2 + } + + printf "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx deleted file mode 100644 index a2e879dc3b..0000000000 --- a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fsx +++ /dev/null @@ -1,20 +0,0 @@ -// #Misc #AppCE - -//#Expects: Success: val result : int ApplicativeBuilderLib.Trace = Trace 3 -//#Expects: Success: val trace : ApplicativeBuilderLib.TraceOp list = .Return; Apply; Apply. - -open ApplicativeBuilderLib - -let result, trace = - let tracer = TraceBuilder() - - let ceResult : int Trace = - tracer { - let! x = Trace 1 - and! y = Trace 2 - return 1 + 2 - } - - ceResult, tracer.GetTrace () -;; -#q;; \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs new file mode 100644 index 0000000000..978f61ae98 --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs @@ -0,0 +1,24 @@ +// #Misc #AppCE + +//#Expects: Success +// << OUTPUT +//Trace 6, \\[Return; Apply; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 3; +// StartUsingBody 3; EndUsingBody 3; ExitUsing 3; EndUsingBody 1; ExitUsing 1\\] +//OUTPUT + +open ApplicativeBuilderLib + +module UseAndLet = + + let () = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + use! x = Trace 1 + and! y = Trace 2 // Explicitly _not_ treated as a resource to be managed + anduse! z = Trace 3 + return 1 + 2 + 3 + } + + printf "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx deleted file mode 100644 index d6909f8659..0000000000 --- a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fsx +++ /dev/null @@ -1,23 +0,0 @@ -// #Misc #AppCE - -//val result : int ApplicativeBuilderLib.Trace = Trace 6 -//val trace : ApplicativeBuilderLib.TraceOp list = -// .Return; Apply; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 3; -// StartUsingBody 3; EndUsingBody 3; ExitUsing 3; EndUsingBody 1; ExitUsing 1. - -open ApplicativeBuilderLib - -let result, trace = - let tracer = TraceBuilder() - - let ceResult : int Trace = - tracer { - use! x = Trace 1 - and! y = Trace 2 // Explicitly _not_ treated as a resource to be managed - anduse! z = Trace 3 - return 1 + 2 + 3 - } - - ceResult, tracer.GetTrace () -;; -#q;; \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs new file mode 100644 index 0000000000..950c877b99 --- /dev/null +++ b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs @@ -0,0 +1,23 @@ +// #Misc #AppCE + +//#Expects: Success +// << OUTPUT +//Trace 3, \\[Return; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 2; +// StartUsingBody 2; EndUsingBody 2; ExitUsing 2; EndUsingBody 1; ExitUsing 1\\] +//OUTPUT + +open ApplicativeBuilderLib + +module UseBang = + + let () = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + use! x = Trace 1 + anduse! y = Trace 2 + return 1 + 2 + } + + printf "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx deleted file mode 100644 index e57fae9573..0000000000 --- a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fsx +++ /dev/null @@ -1,22 +0,0 @@ -// #Misc #AppCE - -//val result : int ApplicativeBuilderLib.Trace = Trace 3 -//val trace : ApplicativeBuilderLib.TraceOp list = -// .Return; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 2; -// StartUsingBody 2; EndUsingBody 2; ExitUsing 2; EndUsingBody 1; ExitUsing 1. - -open ApplicativeBuilderLib - -let result, trace = - let tracer = TraceBuilder() - - let ceResult : int Trace = - tracer { - use! x = Trace 1 - anduse! y = Trace 2 - return 1 + 2 - } - - ceResult, tracer.GetTrace () -;; -#q;; diff --git a/tests/fsharpqa/Source/ComputationExpressions/env.lst b/tests/fsharpqa/Source/ComputationExpressions/env.lst index fea79faa46..0034361e75 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ComputationExpressions/env.lst @@ -1,3 +1,3 @@ - SOURCE=LetBangAndBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # let! ... and! ... desugars correctly - SOURCE=UseBangAndUseBangDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... anduse! ... desugars correctly - SOURCE=MixOfUseAndLetDesugarsCorrectly.fsx SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" FSIMODE=PIPE PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... and! ... anduse! ... desugars correctly \ No newline at end of file + SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # let! ... and! ... desugars correctly + SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... anduse! ... desugars correctly + SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... and! ... anduse! ... desugars correctly \ No newline at end of file From dc6be5f7391161e1a96c3239ba3ac387d9a034ca Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 2 Oct 2018 15:38:46 +0100 Subject: [PATCH 195/255] Make new happy-path applicative CE tests pass locally --- .../LetBangAndBangDesugarsCorrectly.fs | 4 ++-- .../MixOfUseAndLetDesugarsCorrectly.fs | 6 +++--- .../UseBangAndUseBangDesugarsCorrectly.fs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs index ffa31101e6..71556c23ba 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs +++ b/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs @@ -2,7 +2,7 @@ //#Expects: Success // << OUTPUT -//Trace 3, \\[Return; Apply; Apply\\] +//Trace 3, .Return; Apply; Apply. //OUTPUT open ApplicativeBuilderLib @@ -19,4 +19,4 @@ module LetBang = return 1 + 2 } - printf "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file + printfn "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs index 978f61ae98..49503e2731 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs +++ b/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs @@ -2,8 +2,8 @@ //#Expects: Success // << OUTPUT -//Trace 6, \\[Return; Apply; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 3; -// StartUsingBody 3; EndUsingBody 3; ExitUsing 3; EndUsingBody 1; ExitUsing 1\\] +//Trace 6, .Return; Apply; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 3; +// StartUsingBody 3; EndUsingBody 3; ExitUsing 3; EndUsingBody 1; ExitUsing 1. //OUTPUT open ApplicativeBuilderLib @@ -21,4 +21,4 @@ module UseAndLet = return 1 + 2 + 3 } - printf "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file + printfn "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs index 950c877b99..cbddd566e8 100644 --- a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs +++ b/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs @@ -2,8 +2,8 @@ //#Expects: Success // << OUTPUT -//Trace 3, \\[Return; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 2; -// StartUsingBody 2; EndUsingBody 2; ExitUsing 2; EndUsingBody 1; ExitUsing 1\\] +//Trace 3, .Return; Apply; Apply; EnterUsing 1; StartUsingBody 1; EnterUsing 2; +// StartUsingBody 2; EndUsingBody 2; ExitUsing 2; EndUsingBody 1; ExitUsing 1. //OUTPUT open ApplicativeBuilderLib @@ -20,4 +20,4 @@ module UseBang = return 1 + 2 } - printf "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file + printfn "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file From 155a35098ced1a0539a1d25199e210bcc16b2ea8 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 2 Oct 2018 16:44:54 +0100 Subject: [PATCH 196/255] Move new applicative CE tests aling side existing CE tests --- tests/fsharpqa/Source/ComputationExpressions/env.lst | 3 --- .../ComputationExpressions/ApplicativeBuilderLib.fs | 0 .../ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs | 0 .../ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs | 0 .../UseBangAndUseBangDesugarsCorrectly.fs | 0 .../Expressions/DataExpressions/ComputationExpressions/env.lst | 3 +++ tests/fsharpqa/Source/test.lst | 3 +-- 7 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 tests/fsharpqa/Source/ComputationExpressions/env.lst rename tests/fsharpqa/Source/{ => Conformance/Expressions/DataExpressions}/ComputationExpressions/ApplicativeBuilderLib.fs (100%) rename tests/fsharpqa/Source/{ => Conformance/Expressions/DataExpressions}/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs (100%) rename tests/fsharpqa/Source/{ => Conformance/Expressions/DataExpressions}/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs (100%) rename tests/fsharpqa/Source/{ => Conformance/Expressions/DataExpressions}/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs (100%) diff --git a/tests/fsharpqa/Source/ComputationExpressions/env.lst b/tests/fsharpqa/Source/ComputationExpressions/env.lst deleted file mode 100644 index 0034361e75..0000000000 --- a/tests/fsharpqa/Source/ComputationExpressions/env.lst +++ /dev/null @@ -1,3 +0,0 @@ - SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # let! ... and! ... desugars correctly - SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... anduse! ... desugars correctly - SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... and! ... anduse! ... desugars correctly \ No newline at end of file diff --git a/tests/fsharpqa/Source/ComputationExpressions/ApplicativeBuilderLib.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs similarity index 100% rename from tests/fsharpqa/Source/ComputationExpressions/ApplicativeBuilderLib.fs rename to tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs diff --git a/tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs similarity index 100% rename from tests/fsharpqa/Source/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs rename to tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs diff --git a/tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs similarity index 100% rename from tests/fsharpqa/Source/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs rename to tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/MixOfUseAndLetDesugarsCorrectly.fs diff --git a/tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs similarity index 100% rename from tests/fsharpqa/Source/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs rename to tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/UseBangAndUseBangDesugarsCorrectly.fs diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst index f9dfe24dbc..18ce677b3d 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst @@ -24,6 +24,9 @@ # Executing Workflows SOURCE=RunAndDelay01.fs # RunAndDelay01.fs + SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # let! ... and! ... desugars correctly + SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... anduse! ... desugars correctly + SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... and! ... anduse! ... desugars correctly # Misc ReqRetail SOURCE=Capacity01.fs # Capacity01.fs diff --git a/tests/fsharpqa/Source/test.lst b/tests/fsharpqa/Source/test.lst index 5b7cb0a2d6..cbdc1917e2 100644 --- a/tests/fsharpqa/Source/test.lst +++ b/tests/fsharpqa/Source/test.lst @@ -267,8 +267,7 @@ Misc01 Warnings Misc01 ErrorMessages\NameResolution Misc01 ErrorMessages\UnitGenericAbstractType Misc01 ErrorMessages\ConfusingTypeName -Misc01,AppCE ErrorMessages\ApplicativeComputationExpressions -Misc01,AppCE ComputationExpressions +Misc01 ErrorMessages\ApplicativeComputationExpressions Misc02 Libraries\Portable Misc02 Misc From fb33b7e4a7c01a268a694f369fcc07af8e4f2e53 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 2 Oct 2018 18:34:41 +0100 Subject: [PATCH 197/255] Include and! LHS patterns when searching for idents --- src/fsharp/service/ServiceAssemblyContent.fs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/fsharp/service/ServiceAssemblyContent.fs b/src/fsharp/service/ServiceAssemblyContent.fs index dfd0096fe7..4828937b2c 100644 --- a/src/fsharp/service/ServiceAssemblyContent.fs +++ b/src/fsharp/service/ServiceAssemblyContent.fs @@ -707,13 +707,11 @@ module ParsedInput = | SynExpr.JoinIn (e1, _, e2, _) -> List.iter walkExpr [e1; e2] | SynExpr.LetOrUseAndBang (_, _, _, pat, e1, _, es, e2) -> walkPat pat - [ - yield e1 - for (_,_,_,_,eAndBang,_) in es do - yield eAndBang - yield e2 - ] - |> List.iter walkExpr + walkExpr e1 + for (_,_,_,patAndBang,eAndBang,_) in es do + walkPat patAndBang + walkExpr eAndBang + walkExpr e2 | SynExpr.TraitCall (ts, sign, e, _) -> List.iter walkTypar ts walkMemberSig sign From c80c216d134c5cb28fcf7d1f943707273b0da0ad Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 2 Oct 2018 18:34:41 +0100 Subject: [PATCH 198/255] Include and! LHS patterns when searching for idents --- src/fsharp/service/ServiceAssemblyContent.fs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/fsharp/service/ServiceAssemblyContent.fs b/src/fsharp/service/ServiceAssemblyContent.fs index dfd0096fe7..4828937b2c 100644 --- a/src/fsharp/service/ServiceAssemblyContent.fs +++ b/src/fsharp/service/ServiceAssemblyContent.fs @@ -707,13 +707,11 @@ module ParsedInput = | SynExpr.JoinIn (e1, _, e2, _) -> List.iter walkExpr [e1; e2] | SynExpr.LetOrUseAndBang (_, _, _, pat, e1, _, es, e2) -> walkPat pat - [ - yield e1 - for (_,_,_,_,eAndBang,_) in es do - yield eAndBang - yield e2 - ] - |> List.iter walkExpr + walkExpr e1 + for (_,_,_,patAndBang,eAndBang,_) in es do + walkPat patAndBang + walkExpr eAndBang + walkExpr e2 | SynExpr.TraitCall (ts, sign, e, _) -> List.iter walkTypar ts walkMemberSig sign From 284df84b3c0b9ea5061608eb8544b4c164d1b90c Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 2 Oct 2018 18:41:25 +0100 Subject: [PATCH 199/255] Allow break points at and! seq point bindings --- src/fsharp/service/ServiceUntypedParse.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index f94d3b9dd8..f457c6f59f 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -315,7 +315,8 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: Ast.ParsedInput op | SynExpr.LetOrUseAndBang (spBind,_,_,_,e1,_,es,e2) -> yield! walkBindSeqPt spBind yield! walkExpr true e1 - for (_,_,_,_,eAndBang,_) in es do + for (andBangSpBind,_,_,_,eAndBang,_) in es do + yield! walkBindSeqPt andBangSpBind // TODO Check this is valid & useful yield! walkExpr true eAndBang yield! walkExpr true e2 From 2fc66d5d397afebdaa4dcc1354a94b6496f1bf67 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 3 Oct 2018 11:14:11 +0100 Subject: [PATCH 200/255] Tweak vanilla let! ... and! ... test to show two functors "wrapping" different types --- .../LetBangAndBangDesugarsCorrectly.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs index 71556c23ba..8fa1f22e07 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs @@ -14,9 +14,9 @@ module LetBang = let ceResult : int Trace = tracer { - let! x = Trace 1 - and! y = Trace 2 - return 1 + 2 + let! x = Trace 3 + and! y = Trace true + return if y then x else -1 } printfn "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file From a162c67486abc84b6df917775b4c0713b25a69aa Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 3 Oct 2018 11:46:00 +0100 Subject: [PATCH 201/255] Speculatively recover from errors a bit earlier when parsing bindings in a CE --- src/fsharp/pars.fsy | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 1eb01ce23f..09538921ac 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3022,6 +3022,13 @@ binders: let m = $4.Range.EndRange // zero-width range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } + | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let // TODO Fix CompletionInDifferentEnvs3 + { // error recovery that allows intellisense when writing incomplete computation expressions + let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) + let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) // TODO Check this range + let m = $4.Range.EndRange // zero-width range + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } + morebinders: | AND_BANG headBindingPattern EQUALS typedSeqExprBlock IN morebinders %prec expr_let /* TODO Check precedence */ { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *) From 5819594bceeb1896440e651df51fe332c2c04e51 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 3 Oct 2018 14:13:44 +0100 Subject: [PATCH 202/255] Give full completion list when an expected entry cannot be found --- vsintegration/tests/Salsa/SalsaUtils.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/tests/Salsa/SalsaUtils.fs b/vsintegration/tests/Salsa/SalsaUtils.fs index d9de85bcad..b11802a35a 100644 --- a/vsintegration/tests/Salsa/SalsaUtils.fs +++ b/vsintegration/tests/Salsa/SalsaUtils.fs @@ -216,7 +216,7 @@ module internal VsOpsUtils = printfn "Failed to find expected value %s in " membername let MAX = 25 printfn "Completion list = %s" (if completions.Length > MAX then sprintf "%A ... and more" completions.[0..MAX] else sprintf "%A" completions) - Assert.Fail(sprintf "Couldn't find '%s' in completion list: %+A" membername (completions |> Array.map (fun (CompletionItem(name,_,_,_,_)) -> name))) + Assert.Fail(sprintf "Couldn't find '%s' in completion list:\n%s\n\n" membername (completions |> Array.map (fun (CompletionItem(name,_,_,_,_)) -> name) |> String.concat "\n")) // TODO Revert /// Verify the completion list does not contain a member with the given name let AssertCompListDoesNotContain(completions : CompletionItem[], membername) = From 85bf4f68818f730445c1e91bd5543c361e075b83 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 3 Oct 2018 14:18:04 +0100 Subject: [PATCH 203/255] Don't discard applicative bindings if the later body has an error --- src/fsharp/pars.fsy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 09538921ac..d13fe60f65 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3020,7 +3020,7 @@ binders: let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) // TODO Check this range let m = $4.Range.EndRange // zero-width range - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, $7, SynExpr.ImplicitZero m) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let // TODO Fix CompletionInDifferentEnvs3 { // error recovery that allows intellisense when writing incomplete computation expressions From b5010b5677bd67fdd3270a71e4c776313ca5eb73 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 3 Oct 2018 14:20:29 +0100 Subject: [PATCH 204/255] Add TODO note about parse errors somewhere in the middle of an and! sequence --- src/fsharp/pars.fsy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index d13fe60f65..98b4e6518d 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3041,6 +3041,8 @@ morebinders: let m = rhs parseState 1 (* TODO Pretty sure this is wrong *) (spBind,$1,true,$2,$4,m) :: $7 } + /* TODO What if there's an error halfway through the list of and! bindings? Probably okay since each and! is out of scope of the other */ + | %prec prec_no_more_attr_bindings /* TODO Make a prec_no_more_andbang_bindings or something */ { [] } From 3b1af4a58bc4a71d4993c1881d6067ce1db64541 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 3 Oct 2018 15:12:07 +0100 Subject: [PATCH 205/255] Comment out older error handling parser case for let! ... and! ... to explore its effect on code completion --- src/fsharp/pars.fsy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 98b4e6518d..fcd1f1d3e9 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3015,12 +3015,14 @@ binders: let m = unionRanges (rhs parseState 1) $8.Range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } + /* -- TODO: Reintroduce? | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) // TODO Check this range let m = $4.Range.EndRange // zero-width range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, $7, SynExpr.ImplicitZero m) } + */ | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let // TODO Fix CompletionInDifferentEnvs3 { // error recovery that allows intellisense when writing incomplete computation expressions From af288cbb1959ebf1e96bfabe1c0d5391a0fa25a8 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 4 Oct 2018 11:55:47 +0100 Subject: [PATCH 206/255] Add legacy language service tests for completion in an applicative CE --- .../Tests.LanguageService.Completion.fs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs b/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs index a16adb7450..8762bcacae 100644 --- a/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs +++ b/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs @@ -3336,6 +3336,27 @@ let x = query { for bbbb in abbbbc(*D0*) do ["b"] ["i"] + [] + member public this.``CompletionInDifferentEnvs5``() = + AssertCtrlSpaceCompleteContains + ["let foo = async { return 2 }" + "let bar = async { return 5 }" + "async { let! x = foo" + " and! y = bar" + " return "] + "return " + ["x";"y"] + [] + + [] + member public this.``CompletionInDifferentEnvs6``() = + AssertCtrlSpaceCompleteContains + ["let count = async { return 6 }" + "async { let! x = foo" + " and! y = "] + "and! y = " + ["count"] + [] (**) [] From 1d0d07546cec88817d8e204e49d0b5da943cd519 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 4 Oct 2018 12:49:03 +0100 Subject: [PATCH 207/255] Fix and refine legacy completion tests for applicative CEs --- .../Tests.LanguageService.Completion.fs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs b/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs index 8762bcacae..43511aae25 100644 --- a/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs +++ b/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs @@ -3349,18 +3349,19 @@ let x = query { for bbbb in abbbbc(*D0*) do [] [] - member public this.``CompletionInDifferentEnvs6``() = + member public this.``CompletionInDifferentEnvs6``() = AssertCtrlSpaceCompleteContains - ["let count = async { return 6 }" + ["let foo = async { return 6 }" + "let bar = async { return 6 }" "async { let! x = foo" " and! y = "] "and! y = " - ["count"] - [] + ["foo";"bar"] + ["x"] (**) [] - member public this.``Bug229433.AfterMismatchedParensCauseWeirdParseTreeAndExceptionDuringTypecheck``() = + member public this.``Bug229433.AfterMismatchedParensCauseWeirdParseTreeAndExceptionDuringTypecheck``() = AssertAutoCompleteContains [ """ type T() = member this.Bar() = () From 8fec2ca7860cf8ad5a0f6c582c0dcb9dea723c06 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Thu, 4 Oct 2018 14:11:58 +0100 Subject: [PATCH 208/255] Get rid of redundant completion list --- vsintegration/tests/Salsa/SalsaUtils.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/tests/Salsa/SalsaUtils.fs b/vsintegration/tests/Salsa/SalsaUtils.fs index b11802a35a..d9de85bcad 100644 --- a/vsintegration/tests/Salsa/SalsaUtils.fs +++ b/vsintegration/tests/Salsa/SalsaUtils.fs @@ -216,7 +216,7 @@ module internal VsOpsUtils = printfn "Failed to find expected value %s in " membername let MAX = 25 printfn "Completion list = %s" (if completions.Length > MAX then sprintf "%A ... and more" completions.[0..MAX] else sprintf "%A" completions) - Assert.Fail(sprintf "Couldn't find '%s' in completion list:\n%s\n\n" membername (completions |> Array.map (fun (CompletionItem(name,_,_,_,_)) -> name) |> String.concat "\n")) // TODO Revert + Assert.Fail(sprintf "Couldn't find '%s' in completion list: %+A" membername (completions |> Array.map (fun (CompletionItem(name,_,_,_,_)) -> name))) /// Verify the completion list does not contain a member with the given name let AssertCompListDoesNotContain(completions : CompletionItem[], membername) = From 05bc252ddf67717e9f5964f1893d43737088c472 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 5 Oct 2018 10:26:47 +0100 Subject: [PATCH 209/255] rm swap file --- tests/fsharpqa/Source/.run.pl.swp | Bin 24576 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/fsharpqa/Source/.run.pl.swp diff --git a/tests/fsharpqa/Source/.run.pl.swp b/tests/fsharpqa/Source/.run.pl.swp deleted file mode 100644 index 681af8f4def68f79d152da60acd16c0c35074b20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 zcmeI4Yj7mhb%4iq47=bEj0wS&a&j3>R-;9lSuLNjXkjREVLA5uNVr|rJ?_I;fD=zGpR&C-FBQ#0!Anc)P#OB0Eg-gwVxxtutf zNEF?gT>g*0iG1OBac+KSVJ%NzrEUa41M$WDK zO=ro=t~p-Is#ZJ+%=)%R3UhP2l`XifX4%d*TlGw%x@$RcL@a?=0y|0IlEgI!hY$26 zuTpQh?8Ht9;;>i(u>@iX#1e=l5KADIKrDe+0`nZH_Prk71+NSE3BL`l z-yPilnEN%ZUk>j7i~HNSzMTi;nICa~Gygst+`r7d%l)-vEb(uIx59(L{m*cH5*`cg z-@x@aT*if@kAJZQVhO|&h$Rq9AeKNZfmi~u1Y!xq5{M-bOCXlOPfr5Yaw4&Z>=b&q zod3Uze}3?D_yNER@CsOz75}kC*d=2A9zrJ0r=5H zq=9e27vQg99cCa48Mp}kV{anyB77YlgZIL3Lk(u(GI$>0JOCes`{6#g7b=j3e%J%w zypT4+U%`X$0Nep*;0O%Dei(pnT#!io1$+q3!&z8_$XI!n5#2_y~L$HlYls z;H_{ayo@2`X?P0$96k+a;56I*?gAlC;Y6-G<|c~x)Py-q@fj8 zLY(@Fit0K-9^*}(uvN>G7)$l*0mJG_jd*D(Te8sX}qQG&ehav)Z>$U6pb?oqDc0t&=i` z8JQi4#>_x$hcG_-)Raec5vOI!8q%dpJ&q(2Jy|k5hZQAf)29y%U4Q24fyIou2>#7V zJ<_S+!()dAsAa0e1Gh5fZXMNC>8>Af2c&$2Qy2N|&{(NK(W%)YC@&~nrS0>`#7pZ* z6~%5zfzsY6B*?#L(N|@Q&So6?PK~0-b&4rEIX!-~K&n(}G~s1Wt20**4I%9MvQ>5K zw%lh`c2sSTN@aEM*=@+qS+mVNJV9Bi2`)zonj0- z)vB_VmsQQBk-^}rwER}n9;CCFCib$uoI%o)PL&~~+?KB@Rcl51o!PwX*86=`6ES&q zok>U*NC!D*Rx+xyjRV1I7B+w?B~@~#W&|y4w;~9L8WG&EE{;!4N1Z0Jhyr%VN7gD?EmXg6Im~L^Vv|o#Wp=Milt=Jz>ZILdO)B_J zx4trK)oj`$6M``>)j648n3}w~P+XXsJt|B4mKwWLWs3_Z@>z$`*{2FN(yhpF-TQ{ZuX@FG#6DlHw&-XrNZ)F%*uEJs#qous`Qnx= z96Gb8xm!w!s6A+g}m5K231fM2D%yA4c-z|xkjb-9ku;jtJ&mjJ0FBf3-uUaOsRoU`J0eG z`gdyXRB3Ab(6x%v=cmVulXD9*r7&<%CrOx;{FU(WnTZ`z>(Ie=%57OnKu7%cTucrf zHM{BUhq@}oeMbhTre?3dsViQG3`!6GO4#z!G0R&;C~4Wvb&B1*lJoiFrToIe+=4Wa z_8;u&oOY^H;|sHrNKy^&nyPRjm&+FlI+Y%ZIQE)uUbCt#+hfw#h|RPs_DIHXKT9pEt2e^o_cnr3j61}T@!`! zy>_=0Q9;-2mLe>qSFTY_f-Xj)eX3VhG6kb??Qb-Kp}xafjHr8hSFe4gnYoD_d)35F zOi%CFn_w?ru+*A*Z68{*a|`*xiRq%`(JMebs&eCn-1tOZ50zf|b?z5; zAD5H77GH5l&a*y+33hLX>^z&chqBNk#FIDXw5Fo7kaqO1EtUIwx;lpjBk#O+qO!Lw z5!;Gu4o1l&r;6wF<|N8fcTOZ4uW#h$PxL13(v@`0*prh;4}#qKvZ>?L+{j45EuXV} zbI%*sDRa$GV`_;Hz3Bg+L+5-Ny;SsnIfMTfbo#Hrqwp~}2fqeazz@*nzXxB0_rYcG z5_Q3B(e3tw})i=BAA$q^?qIZN$`SRB4pUwn0wzPgvu&t4kvzla`MyBHwJf z%@NVQ*({*8vwyF|e3 zP#Ryz9YgcZ6;CYWN0KUI6!)Fuw}?(+^zm2KwgjgRU3<;JSI9ueV=Gb0n3X&9bi^|t z$~$pVS8makQeEoPW~zbWURp(y;xbSvgQjljT;<802y$tajd^5Sb#;5U1~SP~GP7$U zQ|Gqg$`$gIA38&%u=a>amoasyU5E- zyX}8D`v?j^P@U>d*vsv*m@#4pRx?)foY>37jy|X)2!+`4M#K`S_}NuYo47*pmVCD) zMs_vEfDQwVjZ1pONP7um*Eyw4EOI^(^hC|_v4AbvWlI!0%yuq zt=Psij{#ekZyKox^iVRSMl`ZRFkwSw*@@zNgs?46OB)H~^>Krz3eupWwW6?VnDq$( zEGAPDUJ4uOEhe>m)F~3}-cdUVF-WpNEIwUU(<8z=r}Xz>ScCtKcGd0b78a`9BPI!U|ji-^U*CPw*Ig3^rjA z#^4=r1^gHpJ_mmW55XtlLHJG3@-~;5e78`FYv1izzuM z5U;3g`^vnZO|%>lnCR%LGuj*um;@cyJxPKbq#W$k5f(o+$d*HmJW= zSBdJ=$``IX+KU2b9&|BtGUs-9Wi8p9^w{kAar}|4MJLnQ_Q~JE+=+!;eyrlUnTi(* zeJr`ycIsNPnnWeTXFRuN<8ebiRp*>N5(dvNj$g-WIF$9`6o2as_&hapwNVqE1*a(5}<@P$I!F(+)Xd zGbA-m89c^+qTP4vG2yroDNC$qF`}t(wxYystL~?hsdCLP_)Ru{QgU$DR_Em$n#95H zplH`g8To;%Dqwtbf_F{=}=L+J5q zXJ=roL3T#J4Z2%e6FwE_XZo?ZR#WT7IG%BqOA5+PYb8A}D=}UB6#s>{LqjT0UgB?q zk4?GhG-SMCz|r^NKG$TILDa8WeV6s2Tos49;?zujdhV$4ObGgR^OG|u>$bX~0m zOAJK~s?MUg3yKPRNo*a{?uwLd%p1WCjz|)&MMj}a1aY@>1HU2dM%c0bWit3Xy;>uP zNHMS;rc>sawESb4B0&x<)|EHjI1PA_?z+ji<88h<1&n_gTO&%MR<{i$qfILs(& zOSoo+nUMREg=ZMmOh;is&ay0qPC`Ku=|D$4z35$i20gW8!m%!HBF{)Y7jLu`T-2l= zwL@Cdt*Ia>=_1K@$}Qb1ws7qT+n}AOOdHQGChtfsCI>`Elv0PB4oDn8qTQxWWN$eQ;fZqOiu?pog~ral=*+NS!H}S5>t3zOm7pq2a&w@+k;XD%0ZoL^aeO6ev?uS`Gsktu^PUxIFGMCvwzR9Yy)U=u#dUImZY%6N_;-jaf2Brb{r6yCuc;sSg7_!>NhKdbxuZ-EQoVRZN3fxFiVJ>Ip)yY~Ofu3h$B@0jItDm8`P#rG*=$%58Y z7scSv!gm1enL}-S21Jkc7EgHgjaGeSyuKV%Q-CYcDVqfEn)TWDFk53{=9oTEcR3wq@0I>necbI_ Date: Mon, 8 Oct 2018 11:41:14 +0100 Subject: [PATCH 210/255] Remove experimental script --- scratch/data/SimpleBuilder.fsx | 216 --------------------------------- 1 file changed, 216 deletions(-) delete mode 100644 scratch/data/SimpleBuilder.fsx diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx deleted file mode 100644 index 6266ac2e65..0000000000 --- a/scratch/data/SimpleBuilder.fsx +++ /dev/null @@ -1,216 +0,0 @@ - -[] -type OptionalBuilder = - - member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = - match fOpt, xOpt with - | Some f, Some x -> Some (f x) - | _ -> None - - member __.Return(x : 'a) : 'a option = Some x - - // Below is only needed to make yield keyword work - - member __.YieldFrom(x : 'a) : 'a = x - - member __.Delay(f : unit -> 'a) : 'a = f () // If you type `Delay : 'a -> 'a` explicitly, it builds but things get weird. Fast. - - member __.Zero() : unit option = Some () - - member __.Combine(xOpt : 'a option, yOpt : 'a option) : 'a option = - match xOpt with - | Some _ -> xOpt - | None -> yOpt - -let opt = OptionalBuilder() - -let fOpt = Some (sprintf "f (x = %d) (y = %s) (z = %f)") -let xOpt = Some 1 -let yOpt = Some "A" -let zOpt = Some 3.0 - -let superSimpleExampleDesugared : bool option = - opt.Apply( - opt.Apply( - opt.Return( - (fun x -> - (fun y -> - x || y - ) - ) - ), - Some true), - Some false) - -printfn "Super simple example desugared: \"%+A\"" superSimpleExampleDesugared - -let superSimpleExample : bool option = - opt { - let! (x : bool) = Some true - and! (y : bool) = Some false - return x || y - } - -printfn "Super simple example: \"%+A\"" superSimpleExample - -let foo : string option = - opt { - let! f = fOpt - and! x = xOpt - and! y = yOpt - and! z = zOpt - return (f x y z) - } - -printfn "foo: \"%+A\"" foo - -let bar : int option = - opt { - let! x = None - and! y = Some 1 - and! z = Some 2 - return x + y + z + 1 - } - -printfn "bar: %+A" bar - -type 'a SingleCaseDu = SingleCaseDu of 'a - -let baz : int option = - opt { - let! x = Some 5 - and! (SingleCaseDu y) = Some (SingleCaseDu 40) - and! (z,_) = Some (300, "whatever") - return (let w = 10000 in w + x + y + z + 2000) - } - -printfn "baz: %+A" baz - -let quux : int option = - opt { - let! a = - opt { // Takes the first yield!'d value that is not None, i.e. takes the second block in this case - // You caan view yield! (in this builder) to mean a prioritied list of values, where the highest priority Some wins - yield! - opt { - let! x = Some 11 - and! y = None - and! z = Some 2 - return x + y + z + 1 // Bails out because y is None - } - yield! - opt { - let! x = Some -3 - and! y = Some 1 - and! z = Some 2 - return x + y + z + 4 // Computes result because all args are Some - } - } - and! b = - opt { - let! x = Some 9 - and! _ = Some "IGNORED" // Inner value goes unused, but we'd still bail out if this value were None - and! z = Some 4 - return x + z - 10 - } - and! c = - opt { - let! x = Some 0 - and! y = Some 1 - and! z = Some 2 - return (x + y + z + 1) - } - and! d = baz // Makes use of an optional value from outside the computation expression (bailing out with None if it is None, as with any other value) - return a * b * c * d // Computes this value because all args are some - // Another way to view this is let! ... and! ... is a bit like a conjunction, and yield! ... yield! ... is a bit like a disjunction - } - -printfn "quux: %+A" quux - -type TraceOptBuilder() = - - member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = - match fOpt, xOpt with - | Some f, Some x -> - printfn "Applying %+A to %+A" f x - Some (f x) - | _ -> - printfn "Applying with None. Exiting." - None - - member __.Return(x) = Some x - - member __.Yield(x) = Some x - - member __.Zero() = - printfn "Zero" - Some () - - member __.Combine(xOpt, yOpt) = - let res = - match xOpt with - | Some _ -> - xOpt - | None -> - yOpt - printfn "Combining %+A with %+A to get %+A" xOpt yOpt res - res - - member __.MapTryFinally(body, compensation) = - try body() - finally compensation() - - member __.MapUsing(disposable:#System.IDisposable, body) = - printfn "Using disposable %O" disposable - let body' = fun () -> - printfn ">Running body" - let res = body disposable - printfn " - printfn "Disposing %O" disposable - disposable.Dispose()) - -let traceOpt = TraceOptBuilder() - -type FakeDisposable = - FakeDisposable of int - with - interface System.IDisposable with - member __.Dispose() = - let (FakeDisposable x) = __ - printfn "\"Disposed\" %+A" x - () - -printfn "\n\nStarting Using experiment...\n\n" - -let simpleFooUsing : string option = - traceOpt { - use! xUsing = Some (FakeDisposable 1) - anduse! yUsing = Some (FakeDisposable 2) - return (printfn "Calling return expr - nothing should have been disposed yet!"; sprintf "xUsing = %+A, yUsing = %+A" xUsing yUsing) - } - -printfn "simplefooUsing: \"%+A\"\n\n" simpleFooUsing - -let fooUsing : string option = - traceOpt { - use! xUsing = Some (FakeDisposable 1) - anduse! yUsing = Some (FakeDisposable 2) - anduse! zUsing = Some (FakeDisposable 3) - return (let (FakeDisposable x) = xUsing in sprintf "Unwrapped x = %d" x) - } - -printfn "fooUsing: \"%+A\"\n\n" fooUsing - -(* INVALID: Not a simple name on the LHS -let fooUsing2 : int option = - traceOpt { - use! (FakeDisposable x) = Some (FakeDisposable 1) - anduse! (FakeDisposable y) = Some (FakeDisposable 2) - anduse! (FakeDisposable z) = Some (FakeDisposable 3) - return x * y + z - } - -printfn "fooUsing2: \"%+A\"" fooUsing -*) \ No newline at end of file From f6097eed6c80c1dc66ec9da3e5f5cf33865b7fbf Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 8 Oct 2018 17:29:55 +0100 Subject: [PATCH 211/255] Fix error message that said "return" was expected. "and!"/"anduse!" are equally valid in that position. --- scratch/data/SimpleBuilder.fsx | 216 ++++++++++++++++++ src/fsharp/FSComp.txt | 2 +- ...E_LetBangAndBangNotTerminatedWithReturn.fs | 2 +- 3 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 scratch/data/SimpleBuilder.fsx diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx new file mode 100644 index 0000000000..6266ac2e65 --- /dev/null +++ b/scratch/data/SimpleBuilder.fsx @@ -0,0 +1,216 @@ + +[] +type OptionalBuilder = + + member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = + match fOpt, xOpt with + | Some f, Some x -> Some (f x) + | _ -> None + + member __.Return(x : 'a) : 'a option = Some x + + // Below is only needed to make yield keyword work + + member __.YieldFrom(x : 'a) : 'a = x + + member __.Delay(f : unit -> 'a) : 'a = f () // If you type `Delay : 'a -> 'a` explicitly, it builds but things get weird. Fast. + + member __.Zero() : unit option = Some () + + member __.Combine(xOpt : 'a option, yOpt : 'a option) : 'a option = + match xOpt with + | Some _ -> xOpt + | None -> yOpt + +let opt = OptionalBuilder() + +let fOpt = Some (sprintf "f (x = %d) (y = %s) (z = %f)") +let xOpt = Some 1 +let yOpt = Some "A" +let zOpt = Some 3.0 + +let superSimpleExampleDesugared : bool option = + opt.Apply( + opt.Apply( + opt.Return( + (fun x -> + (fun y -> + x || y + ) + ) + ), + Some true), + Some false) + +printfn "Super simple example desugared: \"%+A\"" superSimpleExampleDesugared + +let superSimpleExample : bool option = + opt { + let! (x : bool) = Some true + and! (y : bool) = Some false + return x || y + } + +printfn "Super simple example: \"%+A\"" superSimpleExample + +let foo : string option = + opt { + let! f = fOpt + and! x = xOpt + and! y = yOpt + and! z = zOpt + return (f x y z) + } + +printfn "foo: \"%+A\"" foo + +let bar : int option = + opt { + let! x = None + and! y = Some 1 + and! z = Some 2 + return x + y + z + 1 + } + +printfn "bar: %+A" bar + +type 'a SingleCaseDu = SingleCaseDu of 'a + +let baz : int option = + opt { + let! x = Some 5 + and! (SingleCaseDu y) = Some (SingleCaseDu 40) + and! (z,_) = Some (300, "whatever") + return (let w = 10000 in w + x + y + z + 2000) + } + +printfn "baz: %+A" baz + +let quux : int option = + opt { + let! a = + opt { // Takes the first yield!'d value that is not None, i.e. takes the second block in this case + // You caan view yield! (in this builder) to mean a prioritied list of values, where the highest priority Some wins + yield! + opt { + let! x = Some 11 + and! y = None + and! z = Some 2 + return x + y + z + 1 // Bails out because y is None + } + yield! + opt { + let! x = Some -3 + and! y = Some 1 + and! z = Some 2 + return x + y + z + 4 // Computes result because all args are Some + } + } + and! b = + opt { + let! x = Some 9 + and! _ = Some "IGNORED" // Inner value goes unused, but we'd still bail out if this value were None + and! z = Some 4 + return x + z - 10 + } + and! c = + opt { + let! x = Some 0 + and! y = Some 1 + and! z = Some 2 + return (x + y + z + 1) + } + and! d = baz // Makes use of an optional value from outside the computation expression (bailing out with None if it is None, as with any other value) + return a * b * c * d // Computes this value because all args are some + // Another way to view this is let! ... and! ... is a bit like a conjunction, and yield! ... yield! ... is a bit like a disjunction + } + +printfn "quux: %+A" quux + +type TraceOptBuilder() = + + member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = + match fOpt, xOpt with + | Some f, Some x -> + printfn "Applying %+A to %+A" f x + Some (f x) + | _ -> + printfn "Applying with None. Exiting." + None + + member __.Return(x) = Some x + + member __.Yield(x) = Some x + + member __.Zero() = + printfn "Zero" + Some () + + member __.Combine(xOpt, yOpt) = + let res = + match xOpt with + | Some _ -> + xOpt + | None -> + yOpt + printfn "Combining %+A with %+A to get %+A" xOpt yOpt res + res + + member __.MapTryFinally(body, compensation) = + try body() + finally compensation() + + member __.MapUsing(disposable:#System.IDisposable, body) = + printfn "Using disposable %O" disposable + let body' = fun () -> + printfn ">Running body" + let res = body disposable + printfn " + printfn "Disposing %O" disposable + disposable.Dispose()) + +let traceOpt = TraceOptBuilder() + +type FakeDisposable = + FakeDisposable of int + with + interface System.IDisposable with + member __.Dispose() = + let (FakeDisposable x) = __ + printfn "\"Disposed\" %+A" x + () + +printfn "\n\nStarting Using experiment...\n\n" + +let simpleFooUsing : string option = + traceOpt { + use! xUsing = Some (FakeDisposable 1) + anduse! yUsing = Some (FakeDisposable 2) + return (printfn "Calling return expr - nothing should have been disposed yet!"; sprintf "xUsing = %+A, yUsing = %+A" xUsing yUsing) + } + +printfn "simplefooUsing: \"%+A\"\n\n" simpleFooUsing + +let fooUsing : string option = + traceOpt { + use! xUsing = Some (FakeDisposable 1) + anduse! yUsing = Some (FakeDisposable 2) + anduse! zUsing = Some (FakeDisposable 3) + return (let (FakeDisposable x) = xUsing in sprintf "Unwrapped x = %d" x) + } + +printfn "fooUsing: \"%+A\"\n\n" fooUsing + +(* INVALID: Not a simple name on the LHS +let fooUsing2 : int option = + traceOpt { + use! (FakeDisposable x) = Some (FakeDisposable 1) + anduse! (FakeDisposable y) = Some (FakeDisposable 2) + anduse! (FakeDisposable z) = Some (FakeDisposable 3) + return x * y + z + } + +printfn "fooUsing2: \"%+A\"" fooUsing +*) \ No newline at end of file diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 42b3666c1a..9402a8048d 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1441,5 +1441,5 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3236,chkNoSpanLikeValueFromExpression,"A Span or IsByRefLike value returned from the expression cannot be used at ths point. This is to ensure the address of the local value does not escape its scope." 3237,tastCantTakeAddressOfExpression,"Cannot take the address of the value returned from the expression. Assign the returned value to a let-bound value before taking the address." 3242,tcTypeDoesNotInheritAttribute,"This type does not inherit Attribute, it will not work correctly with other .NET languages." -3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." +3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'return', 'and!' or 'anduse!' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." 3244,tcInvalidAndUseBangBinding,"Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '." diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs index c0d9ef189f..4623acda00 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. +//Expecting 'return', 'and!' or 'anduse!' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. namespace ApplicativeComputationExpressions From 7238ab4981e024f750a5ea1a8bab7be07eb2c63c Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 8 Oct 2018 18:01:40 +0100 Subject: [PATCH 212/255] Change order of keywords as they appear in FS3243 to match order they can appear in applicative CE syntax --- src/fsharp/FSComp.txt | 2 +- src/fsharp/xlf/FSComp.txt.cs.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.de.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.en.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.es.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.fr.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.it.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.ja.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.ko.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.pl.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.ru.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.tr.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 4 ++-- .../E_LetBangAndBangNotTerminatedWithReturn.fs | 2 +- 16 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 9402a8048d..179440b70a 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1441,5 +1441,5 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3236,chkNoSpanLikeValueFromExpression,"A Span or IsByRefLike value returned from the expression cannot be used at ths point. This is to ensure the address of the local value does not escape its scope." 3237,tastCantTakeAddressOfExpression,"Cannot take the address of the value returned from the expression. Assign the returned value to a let-bound value before taking the address." 3242,tcTypeDoesNotInheritAttribute,"This type does not inherit Attribute, it will not work correctly with other .NET languages." -3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'return', 'and!' or 'anduse!' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." +3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." 3244,tcInvalidAndUseBangBinding,"Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '." diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 6e9102d549..c3a547d451 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index f5a4e74b42..db755141fd 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.en.xlf b/src/fsharp/xlf/FSComp.txt.en.xlf index 3d0acb4926..b57dce7d98 100644 --- a/src/fsharp/xlf/FSComp.txt.en.xlf +++ b/src/fsharp/xlf/FSComp.txt.en.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index 80df6263a5..2a163b586a 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index 3c5d22ac1a..2db96e25c5 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 3a5b52daee..3ca97aa2af 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 65fe6182eb..d0f61a247d 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index db6191d0bb..0555f12317 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 52dfb908a4..3ca970c136 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index d96ab901b4..7b991b43f7 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index c1adfbf398..b297d8e9b9 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index 848a32d709..18755d63c8 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index e5fdb5a240..8dbaff09ec 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 559a687a2d..433b7dd9cc 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -7068,8 +7068,8 @@ - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs index 4623acda00..fc04bcb75f 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//Expecting 'return', 'and!' or 'anduse!' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. +//Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. namespace ApplicativeComputationExpressions From b243d3351f75242e0a5d024a97ee2c2b04d26665 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 11:42:30 +0100 Subject: [PATCH 213/255] Expand on applicative error tests so that they map nicely to the examples in the RFC --- .../E_DoBangInLetBangAndBangBlock.fs | 13 +++++++++++++ .../E_LetBangAfterAndBang.fs | 13 +++++++++++++ ...ithReturn.fs => E_LetBetweenAndBangAndReturn.fs} | 4 ++-- .../E_LetBetweenLetBangAndAndBang.fs | 13 +++++++++++++ .../E_MoreAfterReturn.fs | 13 +++++++++++++ .../ApplicativeComputationExpressions/E_NoReturn.fs | 11 +++++++++++ .../E_YieldInsteadOfReturn.fs | 12 ++++++++++++ .../ApplicativeComputationExpressions/env.lst | 9 +++++++-- 8 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAfterAndBang.fs rename tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/{E_LetBangAndBangNotTerminatedWithReturn.fs => E_LetBetweenAndBangAndReturn.fs} (76%) create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenLetBangAndAndBang.fs create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_NoReturn.fs create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_YieldInsteadOfReturn.fs diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs new file mode 100644 index 0000000000..1b22448979 --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs @@ -0,0 +1,13 @@ +// #ErrorMessages +//Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. + +namespace ApplicativeComputationExpressions + +module E_DoBangInLetBangAndBangBlock = + + eventually { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + do! Eventually.Done () + and! y = Eventually.Done 6 + return x + y + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAfterAndBang.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAfterAndBang.fs new file mode 100644 index 0000000000..6fd6d208d2 --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAfterAndBang.fs @@ -0,0 +1,13 @@ +// #ErrorMessages +//Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. + +namespace ApplicativeComputationExpressions + +module E_LetBangAfterAndBang = + + eventually { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + and! y = Eventually.Done 6 + let! z = Eventually.NotYetDone (fun () -> Eventually.Done 11) + return x + y + z + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenAndBangAndReturn.fs similarity index 76% rename from tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs rename to tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenAndBangAndReturn.fs index fc04bcb75f..f1c40a3678 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBangAndBangNotTerminatedWithReturn.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenAndBangAndReturn.fs @@ -3,11 +3,11 @@ namespace ApplicativeComputationExpressions -module E_LetBangAndBangNotTerminatedWithReturn = +module E_LetBetweenAndBangAndReturn = eventually { let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) and! y = Eventually.Done 6 - let _ = 42 // Invalid: We expect a return to terminate the the let! ... and! ... sequence + let _ = 42 return x + y } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenLetBangAndAndBang.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenLetBangAndAndBang.fs new file mode 100644 index 0000000000..b44a47ca0c --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenLetBangAndAndBang.fs @@ -0,0 +1,13 @@ +// #ErrorMessages +//Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. + +namespace ApplicativeComputationExpressions + +module E_LetBetweenLetBangAndAndBang = + + eventually { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + let _ = 42 + and! y = Eventually.Done 6 + return x + y + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs new file mode 100644 index 0000000000..5436b8f65a --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs @@ -0,0 +1,13 @@ +// #ErrorMessages +//Saw unexpected expression after 'return'. Applicative computation expressions must be terminated with a 'return'. + +namespace ApplicativeComputationExpressions + +module E_MoreAfterReturn = + + eventually { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + and! y = Eventually.Done 6 + return x + y + let _ = 42 + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_NoReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_NoReturn.fs new file mode 100644 index 0000000000..9cf9b6b6b6 --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_NoReturn.fs @@ -0,0 +1,11 @@ +// #ErrorMessages +//Unexpected end of computation expression. Expected a 'return' to terminate this applicative computation expression. + +namespace ApplicativeComputationExpressions + +module E_NoReturn = + + eventually { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + and! y = Eventually.Done 6 + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_YieldInsteadOfReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_YieldInsteadOfReturn.fs new file mode 100644 index 0000000000..8f587a0906 --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_YieldInsteadOfReturn.fs @@ -0,0 +1,12 @@ +// #ErrorMessages +//'yield' is not valid in this position. Did you mean 'return' instead? + +namespace ApplicativeComputationExpressions + +module E_LetBangAndBangNotTerminatedWithReturn = + + eventually { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + and! y = Eventually.Done 6 + yield x + y + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst index b62f3467e9..17707916a4 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst @@ -1,2 +1,7 @@ - SOURCE=E_LetBangAndBangNotTerminatedWithReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" - SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" + SOURCE=E_DoBangInLetBangAndBangBlock.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" + SOURCE=E_LetBangAfterAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" + SOURCE=E_LetBetweenAndBangAndReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" + SOURCE=E_LetBetweenLetBangAndAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" + SOURCE=E_MoreAfterReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" + SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" + SOURCE=E_YieldInsteadOfReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" From e6c4e9e19ea377e2b107959ce81266eff021b996 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 12:29:58 +0100 Subject: [PATCH 214/255] Add missing test definition and comment --- .../ApplicativeComputationExpressions/env.lst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst index 17707916a4..f692f3bc0c 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst @@ -1,7 +1,8 @@ - SOURCE=E_DoBangInLetBangAndBangBlock.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" - SOURCE=E_LetBangAfterAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" - SOURCE=E_LetBetweenAndBangAndReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" - SOURCE=E_LetBetweenLetBangAndAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" - SOURCE=E_MoreAfterReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" - SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" - SOURCE=E_YieldInsteadOfReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" + SOURCE=E_DoBangInLetBangAndBangBlock.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # do! in a let! ... and! ... block + SOURCE=E_LetBangAfterAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # let! anfter an and! + SOURCE=E_LetBetweenAndBangAndReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # let between and! and return + SOURCE=E_LetBetweenLetBangAndAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # let between let! and and! + SOURCE=E_MoreAfterReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # Extra stuff after return + SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # Pattern matching in a use!/anduse! binding + SOURCE=E_YieldInsteadOfReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # yield instead of return + SOURCE=E_NoReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # No return after let! ... and! ... From c4cd6883e8bfd15900c9c4e1402a1fcd65211e4e Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 12:38:03 +0100 Subject: [PATCH 215/255] Give test file names with their descriptions to make them easier to find if/when they fail --- .../ApplicativeComputationExpressions/env.lst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst index f692f3bc0c..001490968b 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst @@ -1,8 +1,8 @@ - SOURCE=E_DoBangInLetBangAndBangBlock.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # do! in a let! ... and! ... block - SOURCE=E_LetBangAfterAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # let! anfter an and! - SOURCE=E_LetBetweenAndBangAndReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # let between and! and return - SOURCE=E_LetBetweenLetBangAndAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # let between let! and and! - SOURCE=E_MoreAfterReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # Extra stuff after return - SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # Pattern matching in a use!/anduse! binding - SOURCE=E_YieldInsteadOfReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # yield instead of return - SOURCE=E_NoReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # No return after let! ... and! ... + SOURCE=E_DoBangInLetBangAndBangBlock.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_DoBangInLetBangAndBangBlock.fs: do! in a let! ... and! ... block + SOURCE=E_LetBangAfterAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_LetBangAfterAndBang.fs: let! anfter an and! + SOURCE=E_LetBetweenAndBangAndReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_LetBetweenAndBangAndReturn.fs: let between and! and return + SOURCE=E_LetBetweenLetBangAndAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_LetBetweenLetBangAndAndBang.fs: let between let! and and! + SOURCE=E_MoreAfterReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_MoreAfterReturn.fs: Extra stuff after return + SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_PatternOnLhsOfUseBangAndUseBangBinding.fs: Pattern matching in a use!/anduse! binding + SOURCE=E_YieldInsteadOfReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_YieldInsteadOfReturn.fs: yield instead of return + SOURCE=E_NoReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_NoReturn.fs: No return after let! ... and! ... From 383aa5c95f598874ad71a3e50b72f0a591f7005b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 12:42:27 +0100 Subject: [PATCH 216/255] Make E_MoreAfterReturn a more plausible error case --- .../ApplicativeComputationExpressions/E_MoreAfterReturn.fs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs index 5436b8f65a..1ac5773c72 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs @@ -9,5 +9,8 @@ module E_MoreAfterReturn = let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) and! y = Eventually.Done 6 return x + y - let _ = 42 + let w = 42 + let! a = Eventually.Done -1 + and! b = Eventually.Done 30 + return a * b } \ No newline at end of file From cd1ef5f92194d5409907ced1e142e70dfdf5f39f Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 12:55:11 +0100 Subject: [PATCH 217/255] Add a new error message for yield instead of return in an applicative CE --- src/fsharp/FSComp.txt | 1 + src/fsharp/TypeChecker.fs | 4 ++++ src/fsharp/xlf/FSComp.txt.cs.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.de.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.en.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.es.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.fr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.it.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ja.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ko.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pl.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ru.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.tr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 5 +++++ .../ApplicativeComputationExpressions/E_MoreAfterReturn.fs | 2 +- .../E_YieldInsteadOfReturn.fs | 2 +- 18 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 179440b70a..8a6ec788e6 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1443,3 +1443,4 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3242,tcTypeDoesNotInheritAttribute,"This type does not inherit Attribute, it will not work correctly with other .NET languages." 3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." 3244,tcInvalidAndUseBangBinding,"Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '." +3245,tcYieldInsteadOfReturnInApplicativeComputationExpression,"'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead?" diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 95975070ce..7a675c6e76 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8046,6 +8046,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) + // 'let! pat1 = expr1 and! pat2 = expr2 ... and! patN = exprN in yield expr3' --> error + | SynExpr.LetOrUseAndBang(_, _, _, _, _, _, _::_, SynExpr.YieldOrReturn((isYield, _), _, yieldRange)) when isYield = true -> + error(Error(FSComp.SR.tcYieldInsteadOfReturnInApplicativeComputationExpression(), yieldRange)) + // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> // build.Apply( // build.Apply( diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index c3a547d451..76edfaeaf4 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index db755141fd..1586399a05 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.en.xlf b/src/fsharp/xlf/FSComp.txt.en.xlf index b57dce7d98..71bb837172 100644 --- a/src/fsharp/xlf/FSComp.txt.en.xlf +++ b/src/fsharp/xlf/FSComp.txt.en.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index 2a163b586a..63511b0df6 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index 2db96e25c5..a4829dfcb7 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 3ca97aa2af..468336c553 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index d0f61a247d..0a93d43f3c 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index 0555f12317..9466e3fb79 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 3ca970c136..8ce3e0d632 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 7b991b43f7..35c4ae6e68 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index b297d8e9b9..3fe2a9aeef 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index 18755d63c8..eab6cdf283 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 8dbaff09ec..1f1685f6cc 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 433b7dd9cc..b88fa43a99 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -7077,6 +7077,11 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs index 1ac5773c72..95f827142a 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//Saw unexpected expression after 'return'. Applicative computation expressions must be terminated with a 'return'. +//Saw unexpected expression after 'return'. Applicative computation expressions must be terminated with a 'return'. namespace ApplicativeComputationExpressions diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_YieldInsteadOfReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_YieldInsteadOfReturn.fs index 8f587a0906..6fc6546ac4 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_YieldInsteadOfReturn.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_YieldInsteadOfReturn.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//'yield' is not valid in this position. Did you mean 'return' instead? +//'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? namespace ApplicativeComputationExpressions From a2c8147ecfc3f287ccb551f6fde0fa0957e772ae Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 14:29:58 +0100 Subject: [PATCH 218/255] Add custom error messages for applicative computation expressions with no 'return' --- src/fsharp/FSComp.txt | 1 + src/fsharp/pars.fsy | 15 +++++++++++++++ src/fsharp/xlf/FSComp.txt.cs.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.de.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.en.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.es.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.fr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.it.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ja.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ko.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pl.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ru.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.tr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 5 +++++ 16 files changed, 86 insertions(+) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 8a6ec788e6..7d44101965 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1444,3 +1444,4 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." 3244,tcInvalidAndUseBangBinding,"Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '." 3245,tcYieldInsteadOfReturnInApplicativeComputationExpression,"'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead?" +3246,parsNoBodyInApplicativeComputationExpression,"No body given after bindings. Expected a 'return' to terminate this applicative computation expression." diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index fcd1f1d3e9..885bc1baab 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3015,6 +3015,21 @@ binders: let m = unionRanges (rhs parseState 1) $8.Range SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } + /* 'let! ... in and! ... in ...' with no return */ + | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders %prec expr_let + { let m = lhs parseState + reportParseErrorAt m (FSComp.SR.parsNoBodyInApplicativeComputationExpression()) + let spBind = SequencePointAtBinding(rhs2 parseState 1 5) + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7, SynExpr.ImplicitZero m) } + + /* 'let! ... and! ...' with no return */ + | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders %prec expr_let + { let m = lhs parseState + reportParseErrorAt m (FSComp.SR.parsNoBodyInApplicativeComputationExpression()) + $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error + let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) + SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7, SynExpr.ImplicitZero m) } + /* -- TODO: Reintroduce? | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 76edfaeaf4..05e4b41640 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index 1586399a05..8e0c39c3f7 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.en.xlf b/src/fsharp/xlf/FSComp.txt.en.xlf index 71bb837172..5a2f3f9f3c 100644 --- a/src/fsharp/xlf/FSComp.txt.en.xlf +++ b/src/fsharp/xlf/FSComp.txt.en.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index 63511b0df6..cd0ed829d8 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index a4829dfcb7..f3e14cf653 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 468336c553..c2d35caba0 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 0a93d43f3c..5b0f9cf397 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index 9466e3fb79..ad0a9efb9c 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 8ce3e0d632..378995264f 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 35c4ae6e68..0318f9faed 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index 3fe2a9aeef..18438d2cb0 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index eab6cdf283..2b76a681e0 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 1f1685f6cc..c23ff20bef 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index b88fa43a99..c83c3eaf08 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -7082,6 +7082,11 @@ 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + + \ No newline at end of file From 3ab2af879f9dce3080db7b478d85cb999c80286b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 15:21:48 +0100 Subject: [PATCH 219/255] Fix tests for some examples of common applicative CE mistakes --- src/fsharp/FSComp.txt | 2 +- src/fsharp/xlf/FSComp.txt.cs.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.de.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.en.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.es.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.fr.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.it.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.ja.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.ko.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.pl.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.ru.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.tr.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 4 ++-- src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 4 ++-- .../E_DoBangInLetBangAndBangBlock.fs | 7 ++++--- .../ApplicativeComputationExpressions/E_NoReturn.fs | 2 +- 17 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 7d44101965..4d08677225 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1444,4 +1444,4 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." 3244,tcInvalidAndUseBangBinding,"Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '." 3245,tcYieldInsteadOfReturnInApplicativeComputationExpression,"'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead?" -3246,parsNoBodyInApplicativeComputationExpression,"No body given after bindings. Expected a 'return' to terminate this applicative computation expression." +3246,parsNoBodyInApplicativeComputationExpression,"No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression." diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 05e4b41640..12f03a4d5b 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index 8e0c39c3f7..fc01456ae5 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.en.xlf b/src/fsharp/xlf/FSComp.txt.en.xlf index 5a2f3f9f3c..bca001aded 100644 --- a/src/fsharp/xlf/FSComp.txt.en.xlf +++ b/src/fsharp/xlf/FSComp.txt.en.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index cd0ed829d8..fd888d5f13 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index f3e14cf653..8bc2a29ea3 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index c2d35caba0..89a408ee1e 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 5b0f9cf397..2748f85a88 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index ad0a9efb9c..9963f0095e 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 378995264f..739cc79dc4 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 0318f9faed..c1b5137294 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index 18438d2cb0..e2d3539d75 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index 2b76a681e0..a21fb8bfb0 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index c23ff20bef..8ab13a9683 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index c83c3eaf08..3dc266d03d 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -7083,8 +7083,8 @@ - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. - No body given after bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs index 1b22448979..cf2ee59d54 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. +//Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. namespace ApplicativeComputationExpressions @@ -7,7 +7,8 @@ module E_DoBangInLetBangAndBangBlock = eventually { let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) - do! Eventually.Done () and! y = Eventually.Done 6 - return x + y + do! Eventually.Done () + and! z = Eventually.Done 4 + return x + y + z } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_NoReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_NoReturn.fs index 9cf9b6b6b6..78d6f51e10 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_NoReturn.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_NoReturn.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//Unexpected end of computation expression. Expected a 'return' to terminate this applicative computation expression. +//No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. namespace ApplicativeComputationExpressions From bc77c64d55e2617451d5ecb14a060616a28469f6 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 15:23:15 +0100 Subject: [PATCH 220/255] Give test file names to make locating failing tests easier --- .../DataExpressions/ComputationExpressions/env.lst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst index 18ce677b3d..1197881dc0 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst @@ -24,9 +24,9 @@ # Executing Workflows SOURCE=RunAndDelay01.fs # RunAndDelay01.fs - SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # let! ... and! ... desugars correctly - SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... anduse! ... desugars correctly - SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # use! ... and! ... anduse! ... desugars correctly + SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangDesugarsCorrectly.fs: let! ... and! ... desugars correctly + SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # UseBangAndUseBangDesugarsCorrectly.fs: use! ... anduse! ... desugars correctly + SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # MixOfUseAndLetDesugarsCorrectly.fs: use! ... and! ... anduse! ... desugars correctly # Misc ReqRetail SOURCE=Capacity01.fs # Capacity01.fs From b690bd15c62612916ea725e46801a3939aaab43a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 15:48:46 +0100 Subject: [PATCH 221/255] Add new error message for extra expressions being sequenced after return in an applicative CE --- src/fsharp/FSComp.txt | 1 + src/fsharp/TypeChecker.fs | 4 ++++ src/fsharp/xlf/FSComp.txt.cs.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.de.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.en.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.es.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.fr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.it.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ja.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ko.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pl.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.ru.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.tr.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 5 +++++ src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 5 +++++ .../ApplicativeComputationExpressions/E_MoreAfterReturn.fs | 2 +- 17 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 4d08677225..7eca399ba7 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1445,3 +1445,4 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3244,tcInvalidAndUseBangBinding,"Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '." 3245,tcYieldInsteadOfReturnInApplicativeComputationExpression,"'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead?" 3246,parsNoBodyInApplicativeComputationExpression,"No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression." +3247,tcMoreAfterReturnInApplicativeComputationExpression,"Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'." diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 7a675c6e76..1da4bd5751 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8050,6 +8050,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | SynExpr.LetOrUseAndBang(_, _, _, _, _, _, _::_, SynExpr.YieldOrReturn((isYield, _), _, yieldRange)) when isYield = true -> error(Error(FSComp.SR.tcYieldInsteadOfReturnInApplicativeComputationExpression(), yieldRange)) + // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3 ; moreBody' --> error + | SynExpr.LetOrUseAndBang(_, _, _, _, _, _, _::_, SynExpr.Sequential(_, _, SynExpr.YieldOrReturn((isYield, _), _, _), moreBodyExpr, _)) when isYield = false -> + error(Error(FSComp.SR.tcMoreAfterReturnInApplicativeComputationExpression(), moreBodyExpr.Range)) + // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> // build.Apply( // build.Apply( diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 12f03a4d5b..e6777220b7 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index fc01456ae5..89ceec0f6a 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.en.xlf b/src/fsharp/xlf/FSComp.txt.en.xlf index bca001aded..fa32866b51 100644 --- a/src/fsharp/xlf/FSComp.txt.en.xlf +++ b/src/fsharp/xlf/FSComp.txt.en.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index fd888d5f13..4aacf93e89 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index 8bc2a29ea3..d7a1d5c01a 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 89a408ee1e..ac3a8e671f 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 2748f85a88..46c3c03f65 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index 9963f0095e..2a9df0d655 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 739cc79dc4..064c41e521 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index c1b5137294..dcb11a1f67 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index e2d3539d75..c15d700665 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index a21fb8bfb0..7639b8399c 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 8ab13a9683..36298e9758 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 3dc266d03d..97433b2e26 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -7087,6 +7087,11 @@ No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs index 95f827142a..dfd878f4b0 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_MoreAfterReturn.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//Saw unexpected expression after 'return'. Applicative computation expressions must be terminated with a 'return'. +//Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. namespace ApplicativeComputationExpressions From f675fb7efafba7f5f60727bb47c2052e0db8a3d5 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 15:57:30 +0100 Subject: [PATCH 222/255] Change expected error messages for cases which look more monadic than applicative --- .../E_DoBangInLetBangAndBangBlock.fs | 3 ++- .../E_LetBetweenLetBangAndAndBang.fs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs index cf2ee59d54..f65fc1c970 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. +//Unexpected keyword 'and!' in expression namespace ApplicativeComputationExpressions @@ -8,6 +8,7 @@ module E_DoBangInLetBangAndBangBlock = eventually { let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) and! y = Eventually.Done 6 + // Up to this point this is a valid monadic computation expression, with no reason to suspect it might be an applicative. do! Eventually.Done () and! z = Eventually.Done 4 return x + y + z diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenLetBangAndAndBang.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenLetBangAndAndBang.fs index b44a47ca0c..0bded932de 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenLetBangAndAndBang.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_LetBetweenLetBangAndAndBang.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. +//Unexpected keyword 'and!' in expression namespace ApplicativeComputationExpressions @@ -8,6 +8,7 @@ module E_LetBetweenLetBangAndAndBang = eventually { let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) let _ = 42 + // Up to this point this is a valid monadic computation expression, with no reason to suspect it might be an applicative. and! y = Eventually.Done 6 return x + y } \ No newline at end of file From f2c94c266e088322b993766ac72003ff354b9947 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 17:09:34 +0100 Subject: [PATCH 223/255] 'Fix' test by chainging expectation: Case is currently somewhat ambiguous --- .../E_DoBangInLetBangAndBangBlock.fs | 4 ++-- tests/fsharpqa/Source/test.lst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs index f65fc1c970..6c2dff9f30 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_DoBangInLetBangAndBangBlock.fs @@ -1,5 +1,5 @@ // #ErrorMessages -//Unexpected keyword 'and!' in expression +//Unexpected keyword 'and!' in expression. Expected '}' or other token. namespace ApplicativeComputationExpressions @@ -9,7 +9,7 @@ module E_DoBangInLetBangAndBangBlock = let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) and! y = Eventually.Done 6 // Up to this point this is a valid monadic computation expression, with no reason to suspect it might be an applicative. - do! Eventually.Done () + do! Eventually.Done () // Still syntactically valid - immediate 'return' is forced by the type checker, not the parser and! z = Eventually.Done 4 return x + y + z } \ No newline at end of file diff --git a/tests/fsharpqa/Source/test.lst b/tests/fsharpqa/Source/test.lst index cbdc1917e2..2a8c2222fc 100644 --- a/tests/fsharpqa/Source/test.lst +++ b/tests/fsharpqa/Source/test.lst @@ -129,7 +129,7 @@ Conformance03 Conformance\Expressions\ControlFlowExpressions\TryCatch Conformance03 Conformance\Expressions\ControlFlowExpressions\TryFinally Conformance03 Conformance\Expressions\ControlFlowExpressions\While Conformance03 Conformance\Expressions\DataExpressions\AddressOf -Conformance03 Conformance\Expressions\DataExpressions\ComputationExpressions +Conformance03,AppCe Conformance\Expressions\DataExpressions\ComputationExpressions Conformance03 Conformance\Expressions\DataExpressions\ObjectExpressions Conformance03 Conformance\Expressions\DataExpressions\QueryExpressions Conformance03 Conformance\Expressions\DataExpressions\RangeExpressions @@ -267,7 +267,7 @@ Misc01 Warnings Misc01 ErrorMessages\NameResolution Misc01 ErrorMessages\UnitGenericAbstractType Misc01 ErrorMessages\ConfusingTypeName -Misc01 ErrorMessages\ApplicativeComputationExpressions +Misc01,AppCe ErrorMessages\ApplicativeComputationExpressions Misc02 Libraries\Portable Misc02 Misc From 3850eb4b61accea6d241347f10df9a68c262f546 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 17:18:23 +0100 Subject: [PATCH 224/255] s/MapUsing/ApplyUsing/ --- src/fsharp/TypeChecker.fs | 10 +++++----- .../ComputationExpressions/ComputationExprLibrary.fs | 4 ++-- .../ComputationExpressions/ApplicativeBuilderLib.fs | 2 +- .../ApplicativeComputationExpressions/builderlib.fs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 1da4bd5751..ab616c6dbd 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8103,7 +8103,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // 'Apply' correspond to their lambda. let desugared = - // Insert calls to builder.MapUsing(...) to handle resources + // Insert calls to builder.ApplyUsing(...) to handle resources // if required let usings = bindingsBottomToTop @@ -8112,10 +8112,10 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | true, SynPat.Named (SynPat.Wild _, id, false, _, _) | true, SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range - if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "MapUsing" builderTy) - then error(Error(FSComp.SR.tcRequireBuilderMethod("MapUsing"), m)) - let mapUsingBodyExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) // TODO Where should we be suppressing sequence points? - mkSynCall "MapUsing" bindRange [ SynExpr.Ident(id); mapUsingBodyExpr ] + if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "ApplyUsing" builderTy) + then error(Error(FSComp.SR.tcRequireBuilderMethod("ApplyUsing"), m)) + let applyUsingBodyExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) // TODO Where should we be suppressing sequence points? + mkSynCall "ApplyUsing" bindRange [ SynExpr.Ident(id); applyUsingBodyExpr ] | true, _ -> // TODO Support explicitly typed names on the LHS of a use!/anduse! error(Error(FSComp.SR.tcInvalidAndUseBangBinding(), pat.Range)) diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/ComputationExpressions/ComputationExprLibrary.fs b/tests/fsharpqa/Source/CodeGen/EmittedIL/ComputationExpressions/ComputationExprLibrary.fs index 658acc6241..48b2d7229c 100644 --- a/tests/fsharpqa/Source/CodeGen/EmittedIL/ComputationExpressions/ComputationExprLibrary.fs +++ b/tests/fsharpqa/Source/CodeGen/EmittedIL/ComputationExpressions/ComputationExprLibrary.fs @@ -86,7 +86,7 @@ module Eventually = catch e |> bind (function Result v -> Done v | Exception e -> handler e) - let mapUsing (resource:System.IDisposable) f = + let applyUsing (resource:System.IDisposable) f = try f resource finally @@ -111,7 +111,7 @@ type EventuallyBuilder() = member __.TryWith(e,handler) = Eventually.tryWith e handler member __.TryFinally(e,compensation) = Eventually.tryFinally e compensation member __.Using(resource:System.IDisposable,e) = Eventually.tryFinally (e resource) resource.Dispose - member __.MapUsing(resource:System.IDisposable,f) = Eventually.mapUsing resource f + member __.ApplyUsing(resource:System.IDisposable,f) = Eventually.applyUsing resource f member __.While(gd,e) = Eventually.doWhile gd e member __.For(xs,f) = Eventually.doFor xs f member __.Delay(f) = Eventually.delay f diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs index 8f0526403c..21928bf14a 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs @@ -39,7 +39,7 @@ type TraceBuilder() = /// Doesn't actually do any disposing here, since we are just interesting /// in checking the order of events in this test - member __.MapUsing(resource(*:#System.IDisposable*), body) = + member __.ApplyUsing(resource(*:#System.IDisposable*), body) = trace <- EnterUsing resource :: trace let body' = fun () -> trace <- StartUsingBody resource :: trace diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs index baf0439f2a..ea226dfaaf 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs @@ -80,7 +80,7 @@ module Eventually = catch e |> bind (function Result v -> Done v | Exception e -> handler e) - let mapUsing (resource:System.IDisposable) f = + let applyUsing (resource:System.IDisposable) f = try f resource finally @@ -105,7 +105,7 @@ type EventuallyBuilder() = member __.TryWith(e,handler) = Eventually.tryWith e handler member __.TryFinally(e,compensation) = Eventually.tryFinally e compensation member __.Using(resource:System.IDisposable,e) = Eventually.tryFinally (e resource) resource.Dispose - member __.MapUsing(resource:System.IDisposable,f) = Eventually.mapUsing resource f + member __.ApplyUsing(resource:System.IDisposable,f) = Eventually.applyUsing resource f member __.While(gd,e) = Eventually.doWhile gd e member __.For(xs,f) = Eventually.doFor xs f member __.Delay(f) = Eventually.delay f From 7864b948a26b15997c8027716b8926f0e10c7ee4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 19:10:58 +0100 Subject: [PATCH 225/255] Remove some trailing whitespace --- src/fsharp/TypeChecker.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index ab616c6dbd..8e14321243 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8043,7 +8043,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some(translatedCtxt (mkSynCall "Bind" bindRange [rhsExpr; consumeExpr])) // 'use! pat = e1 ... in e2' where 'pat' is not a simple name --> error - | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> + | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // 'let! pat1 = expr1 and! pat2 = expr2 ... and! patN = exprN in yield expr3' --> error From 828ab6334862f0d1d4fd8183e1ec732fd7ea0938 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 20:09:59 +0100 Subject: [PATCH 226/255] Remove trailing newline - will that satisfy CI? --- src/fsharp/FSComp.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 7eca399ba7..306f86d2c0 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1445,4 +1445,4 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3244,tcInvalidAndUseBangBinding,"Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '." 3245,tcYieldInsteadOfReturnInApplicativeComputationExpression,"'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead?" 3246,parsNoBodyInApplicativeComputationExpression,"No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression." -3247,tcMoreAfterReturnInApplicativeComputationExpression,"Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'." +3247,tcMoreAfterReturnInApplicativeComputationExpression,"Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'." \ No newline at end of file From c1fef16d7bb1d2bfb64d97908c5fcc4f9a7402f1 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 20:34:26 +0100 Subject: [PATCH 227/255] Remove trailing whitespace --- src/fsharp/TypeChecker.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 8e14321243..21fdb75e64 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8054,7 +8054,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | SynExpr.LetOrUseAndBang(_, _, _, _, _, _, _::_, SynExpr.Sequential(_, _, SynExpr.YieldOrReturn((isYield, _), _, _), moreBodyExpr, _)) when isYield = false -> error(Error(FSComp.SR.tcMoreAfterReturnInApplicativeComputationExpression(), moreBodyExpr.Range)) - // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> + // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> // build.Apply( // build.Apply( // build.Return( From 89e1e0fbe77d56ce58147c849018ade7a7d429f7 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Tue, 9 Oct 2018 21:33:45 +0100 Subject: [PATCH 228/255] Promote error msgs to buildfromsource This should fix CI and local `build.cmd buildfromsouce` runs See https://github.com/Microsoft/visualfsharp/blob/master/DEVGUIDE.md#updating-fscompfs --- .../FSharp.Compiler.Private/FSComp.fs | 18 +++++++++++++++--- .../FSharp.Compiler.Private/FSComp.resx | 19 ++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs b/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs index e8eeee53b8..8078f372af 100644 --- a/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs +++ b/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs @@ -3190,10 +3190,10 @@ type internal SR private() = /// This operation accesses a mutable top-level value defined in another assembly in an unsupported way. The value cannot be accessed through its address. Consider copying the expression to a mutable local, e.g. 'let mutable x = ...', and if necessary assigning the value back after the completion of the operation /// (Originally from ..\FSComp.txt:1042) static member tastInvalidAddressOfMutableAcrossAssemblyBoundary() = (1188, GetStringFunc("tastInvalidAddressOfMutableAcrossAssemblyBoundary",",,,") ) - /// Type parameters must be placed directly adjacent to the type name, e.g. \"type C<'T>\", not type \"C <'T>\" + /// Remove spaces between the type name and type parameter, e.g. \"type C<'T>\", not type \"C <'T>\". Type parameters must be placed directly adjacent to the type name. /// (Originally from ..\FSComp.txt:1043) static member parsNonAdjacentTypars() = (1189, GetStringFunc("parsNonAdjacentTypars",",,,") ) - /// Type arguments must be placed directly adjacent to the type name, e.g. \"C<'T>\", not \"C <'T>\" + /// Remove spaces between the type name and type parameter, e.g. \"C<'T>\", not \"C <'T>\". Type parameters must be placed directly adjacent to the type name. /// (Originally from ..\FSComp.txt:1044) static member parsNonAdjacentTyargs() = (1190, GetStringFunc("parsNonAdjacentTyargs",",,,") ) /// The use of the type syntax 'int C' and 'C ' is not permitted here. Consider adjusting this type to be written in the form 'C' @@ -4357,12 +4357,21 @@ type internal SR private() = /// This type does not inherit Attribute, it will not work correctly with other .NET languages. /// (Originally from ..\FSComp.txt:1443) static member tcTypeDoesNotInheritAttribute() = (3242, GetStringFunc("tcTypeDoesNotInheritAttribute",",,,") ) - /// Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. + /// Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '. /// (Originally from ..\FSComp.txt:1444) static member tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn() = (3243, GetStringFunc("tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn",",,,") ) /// Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '. /// (Originally from ..\FSComp.txt:1445) static member tcInvalidAndUseBangBinding() = (3244, GetStringFunc("tcInvalidAndUseBangBinding",",,,") ) + /// 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + /// (Originally from ..\FSComp.txt:1446) + static member tcYieldInsteadOfReturnInApplicativeComputationExpression() = (3245, GetStringFunc("tcYieldInsteadOfReturnInApplicativeComputationExpression",",,,") ) + /// No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + /// (Originally from ..\FSComp.txt:1447) + static member parsNoBodyInApplicativeComputationExpression() = (3246, GetStringFunc("parsNoBodyInApplicativeComputationExpression",",,,") ) + /// Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + /// (Originally from ..\FSComp.txt:1448) + static member tcMoreAfterReturnInApplicativeComputationExpression() = (3247, GetStringFunc("tcMoreAfterReturnInApplicativeComputationExpression",",,,") ) /// Call this method once to validate that all known resources are valid; throws if not static member RunStartupValidation() = @@ -5782,4 +5791,7 @@ type internal SR private() = ignore(GetString("tcTypeDoesNotInheritAttribute")) ignore(GetString("tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn")) ignore(GetString("tcInvalidAndUseBangBinding")) + ignore(GetString("tcYieldInsteadOfReturnInApplicativeComputationExpression")) + ignore(GetString("parsNoBodyInApplicativeComputationExpression")) + ignore(GetString("tcMoreAfterReturnInApplicativeComputationExpression")) () diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx b/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx index d91af67c0a..84da20aae9 100644 --- a/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx +++ b/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx @@ -3082,7 +3082,7 @@ This number is outside the allowable range for 32-bit floats - This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0b0001 (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1us (uint16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger). + This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0b0001 (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger). This is not a valid byte literal @@ -3193,10 +3193,10 @@ This operation accesses a mutable top-level value defined in another assembly in an unsupported way. The value cannot be accessed through its address. Consider copying the expression to a mutable local, e.g. 'let mutable x = ...', and if necessary assigning the value back after the completion of the operation - Type parameters must be placed directly adjacent to the type name, e.g. \"type C<'T>\", not type \"C <'T>\" + Remove spaces between the type name and type parameter, e.g. \"type C<'T>\", not type \"C <'T>\". Type parameters must be placed directly adjacent to the type name. - Type arguments must be placed directly adjacent to the type name, e.g. \"C<'T>\", not \"C <'T>\" + Remove spaces between the type name and type parameter, e.g. \"C<'T>\", not \"C <'T>\". Type parameters must be placed directly adjacent to the type name. The use of the type syntax 'int C' and 'C <int>' is not permitted here. Consider adjusting this type to be written in the form 'C<int>' @@ -4361,9 +4361,18 @@ This type does not inherit Attribute, it will not work correctly with other .NET languages. - Expecting 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - + + 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file From d588f4011cd05c92a49fd3996f3974425b98c797 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 13:38:38 +0100 Subject: [PATCH 229/255] Add test for error msgs for missing builder methods --- .../E_ApplyNotDefinedOnBuilder.fs | 12 ++++++++ .../E_ApplyUsingNotDefinedOnBuilder.fs | 12 ++++++++ .../builderlib.fs | 29 +++++++++++++++++++ .../ApplicativeComputationExpressions/env.lst | 2 ++ 4 files changed, 55 insertions(+) create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyNotDefinedOnBuilder.fs create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyUsingNotDefinedOnBuilder.fs diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyNotDefinedOnBuilder.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyNotDefinedOnBuilder.fs new file mode 100644 index 0000000000..4cd5de524f --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyNotDefinedOnBuilder.fs @@ -0,0 +1,12 @@ +// #ErrorMessages +//'Apply' is not defined on the computation expression builder + +namespace ApplicativeComputationExpressions + +module E_ApplyNotDefinedOnBuilder = + + eventuallyNoApply { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + and! y = Eventually.Done 6 + return x + y + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyUsingNotDefinedOnBuilder.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyUsingNotDefinedOnBuilder.fs new file mode 100644 index 0000000000..4f42a15dbf --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyUsingNotDefinedOnBuilder.fs @@ -0,0 +1,12 @@ +// #ErrorMessages +//'ApplyUsing' is not defined on the computation expression builder + +namespace ApplicativeComputationExpressions + +module E_ApplyUsingNotDefinedOnBuilder = + + eventuallyNoApplyUsing { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + and! y = Eventually.Done 6 + return x + y + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs index ea226dfaaf..d68509b1b8 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs @@ -111,10 +111,39 @@ type EventuallyBuilder() = member __.Delay(f) = Eventually.delay f member __.Zero() = Eventually.Done () +type EventuallyNoApplyBuilder() = + member __.Bind(e,k) = Eventually.bind k e + member __.Return(v) = Eventually.Done v + member __.ReturnFrom(e:Eventually<_>) = e + member __.Combine(e1,e2) = e1 |> Eventually.bind (fun () -> e2) + member __.TryWith(e,handler) = Eventually.tryWith e handler + member __.TryFinally(e,compensation) = Eventually.tryFinally e compensation + member __.Using(resource:System.IDisposable,e) = Eventually.tryFinally (e resource) resource.Dispose + member __.ApplyUsing(resource:System.IDisposable,f) = Eventually.applyUsing resource f + member __.While(gd,e) = Eventually.doWhile gd e + member __.For(xs,f) = Eventually.doFor xs f + member __.Delay(f) = Eventually.delay f + member __.Zero() = Eventually.Done () + +type EventuallyNoApplyUsingBuilder() = + member __.Bind(e,k) = Eventually.bind k e + member __.Apply(f,x) = Eventually.apply f x + member __.Return(v) = Eventually.Done v + member __.ReturnFrom(e:Eventually<_>) = e + member __.Combine(e1,e2) = e1 |> Eventually.bind (fun () -> e2) + member __.TryWith(e,handler) = Eventually.tryWith e handler + member __.TryFinally(e,compensation) = Eventually.tryFinally e compensation + member __.Using(resource:System.IDisposable,e) = Eventually.tryFinally (e resource) resource.Dispose + member __.While(gd,e) = Eventually.doWhile gd e + member __.For(xs,f) = Eventually.doFor xs f + member __.Delay(f) = Eventually.delay f + member __.Zero() = Eventually.Done () [] module TheEventuallyBuilder = let eventually = new EventuallyBuilder() + let eventullyNoApply = new EventuallyNoApplyBuilder() + let eventullyNoApplyUsing = new EventuallyNoApplyUsingBuilder() type FakeDisposable = FakeDisposable of int diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst index 001490968b..1b633f1577 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst @@ -1,3 +1,5 @@ + SOURCE=E_ApplyNotDefinedOnBuilder.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_ApplyNotDefinedOnBuilder.fs: CE builder missing Apply + SOURCE=E_ApplyUsingNotDefinedOnBuilder.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_ApplyUsingNotDefinedOnBuilder.fs: CE builder missing ApplyUsing SOURCE=E_DoBangInLetBangAndBangBlock.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_DoBangInLetBangAndBangBlock.fs: do! in a let! ... and! ... block SOURCE=E_LetBangAfterAndBang.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_LetBangAfterAndBang.fs: let! anfter an and! SOURCE=E_LetBetweenAndBangAndReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_LetBetweenAndBangAndReturn.fs: let between and! and return From 39ce20aa26121ea6bdbafba9fc98b825776b6dce Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 14:11:26 +0100 Subject: [PATCH 230/255] Fix tests (locally) --- .../E_ApplyNotDefinedOnBuilder.fs | 13 +++++++------ .../E_ApplyUsingNotDefinedOnBuilder.fs | 13 +++++++------ .../ApplicativeComputationExpressions/builderlib.fs | 4 ++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyNotDefinedOnBuilder.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyNotDefinedOnBuilder.fs index 4cd5de524f..7503c3465e 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyNotDefinedOnBuilder.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyNotDefinedOnBuilder.fs @@ -1,12 +1,13 @@ // #ErrorMessages -//'Apply' is not defined on the computation expression builder +//This control construct may only be used if the computation expression builder defines a 'Apply' method namespace ApplicativeComputationExpressions module E_ApplyNotDefinedOnBuilder = - eventuallyNoApply { - let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) - and! y = Eventually.Done 6 - return x + y - } \ No newline at end of file + let example = + eventuallyNoApply { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + and! y = Eventually.Done 6 + return x + y + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyUsingNotDefinedOnBuilder.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyUsingNotDefinedOnBuilder.fs index 4f42a15dbf..2289931d25 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyUsingNotDefinedOnBuilder.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ApplyUsingNotDefinedOnBuilder.fs @@ -1,12 +1,13 @@ // #ErrorMessages -//'ApplyUsing' is not defined on the computation expression builder +//This control construct may only be used if the computation expression builder defines a 'ApplyUsing' method namespace ApplicativeComputationExpressions module E_ApplyUsingNotDefinedOnBuilder = - eventuallyNoApplyUsing { - let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) - and! y = Eventually.Done 6 - return x + y - } \ No newline at end of file + let example = + eventuallyNoApplyUsing { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + anduse! y = Eventually.Done (FakeDisposable -1) + return x + y + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs index d68509b1b8..0053233e07 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/builderlib.fs @@ -142,8 +142,8 @@ type EventuallyNoApplyUsingBuilder() = [] module TheEventuallyBuilder = let eventually = new EventuallyBuilder() - let eventullyNoApply = new EventuallyNoApplyBuilder() - let eventullyNoApplyUsing = new EventuallyNoApplyUsingBuilder() + let eventuallyNoApply = new EventuallyNoApplyBuilder() + let eventuallyNoApplyUsing = new EventuallyNoApplyUsingBuilder() type FakeDisposable = FakeDisposable of int From b09776a4e5b8304598cbd4d7512d83d00399709a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 15:22:55 +0100 Subject: [PATCH 231/255] Generalise Scope.LetOrUseBang given new syntax --- src/fsharp/service/ServiceStructure.fs | 6 +++--- src/fsharp/service/ServiceStructure.fsi | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fsharp/service/ServiceStructure.fs b/src/fsharp/service/ServiceStructure.fs index 941c6b1663..ac29501bf1 100644 --- a/src/fsharp/service/ServiceStructure.fs +++ b/src/fsharp/service/ServiceStructure.fs @@ -115,7 +115,7 @@ module Structure = | Attribute | Interface | HashDirective - | LetOrUseBang + | LetOrUseOrAndOrAndUseBang | TypeExtension | YieldOrReturn | YieldOrReturnBang @@ -164,7 +164,7 @@ module Structure = | Attribute -> "Attribute" | Interface -> "Interface" | HashDirective -> "HashDirective" - | LetOrUseBang -> "LetOrUseBang" + | LetOrUseOrAndOrAndUseBang -> "LetOrUseOrAndOrAndUseBang" | TypeExtension -> "TypeExtension" | YieldOrReturn -> "YieldOrReturn" | YieldOrReturnBang -> "YieldOrReturnBang" @@ -256,7 +256,7 @@ module Structure = // on the same line if there is an `=` the range will be adjusted during the // tooltip creation let r = Range.endToEnd pat.Range e.Range - rcheck Scope.LetOrUseBang Collapse.Below r r + rcheck Scope.LetOrUseOrAndOrAndUseBang Collapse.Below r r parseExpr e ) parseExpr eBody diff --git a/src/fsharp/service/ServiceStructure.fsi b/src/fsharp/service/ServiceStructure.fsi index a7a054a411..9372f74888 100644 --- a/src/fsharp/service/ServiceStructure.fsi +++ b/src/fsharp/service/ServiceStructure.fsi @@ -53,7 +53,7 @@ module public Structure = | Attribute | Interface | HashDirective - | LetOrUseBang + | LetOrUseOrAndOrAndUseBang | TypeExtension | YieldOrReturn | YieldOrReturnBang From fc9bb61a991e2d65ff6dcc1a43f49a2f7f04103a Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 15:23:56 +0100 Subject: [PATCH 232/255] Add missed file to generalise Scope.LetOrUseBang Given new let! ... and! ... syntax --- .../src/FSharp.Editor/Structure/BlockStructureService.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index af3341351a..bab55bea5b 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -54,7 +54,7 @@ module internal BlockStructure = | Scope.Quote | Scope.SpecialFunc | Scope.Lambda - | Scope.LetOrUseBang + | Scope.LetOrUseOrAndOrAndUseBang | Scope.Val | Scope.YieldOrReturn | Scope.YieldOrReturnBang @@ -108,7 +108,7 @@ module internal BlockStructure = | Scope.CompExprInternal | Scope.Quote | Scope.Lambda - | Scope.LetOrUseBang + | Scope.LetOrUseOrAndOrAndUseBang | Scope.Val | Scope.YieldOrReturn | Scope.YieldOrReturnBang From d2d426ef240a5869026bcb454f85bb4e3dc05543 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 15:28:25 +0100 Subject: [PATCH 233/255] Pull naming of SynExpr and Scope cases into line For new let! ... and! ... syntax --- src/fsharp/TypeChecker.fs | 34 +++++++++---------- src/fsharp/ast.fs | 12 +++---- src/fsharp/service/ServiceAssemblyContent.fs | 2 +- .../service/ServiceInterfaceStubGenerator.fs | 2 +- src/fsharp/service/ServiceParseTreeWalk.fs | 2 +- src/fsharp/service/ServiceStructure.fs | 6 ++-- src/fsharp/service/ServiceStructure.fsi | 2 +- src/fsharp/service/ServiceUntypedParse.fs | 4 +-- .../Structure/BlockStructureService.fs | 4 +-- 9 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 21fdb75e64..f12c8fab8f 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -3467,7 +3467,7 @@ let (|SimpleSemicolonSequence|_|) acceptDeprecated c = | SynExpr.ForEach (_, _, _, _, _, body, _) -> YieldFree body | SynExpr.YieldOrReturnFrom _ | SynExpr.YieldOrReturn _ - | SynExpr.LetOrUseAndBang _ + | SynExpr.LetOrUseOrAndBang _ | SynExpr.ImplicitZero _ | SynExpr.Do _ -> false | _ -> true @@ -3486,7 +3486,7 @@ let (|SimpleSemicolonSequence|_|) acceptDeprecated c = | SynExpr.LetOrUse _ | SynExpr.Do _ | SynExpr.MatchBang _ - | SynExpr.LetOrUseAndBang _ + | SynExpr.LetOrUseOrAndBang _ | SynExpr.ImplicitZero _ | SynExpr.While _ -> false | _ -> true @@ -6138,7 +6138,7 @@ and TcExprUndelayed cenv overallTy env tpenv (synExpr: SynExpr) = error(Error(FSComp.SR.tcConstructRequiresSequenceOrComputations(), m)) | SynExpr.DoBang (_, m) - | SynExpr.LetOrUseAndBang (range=m) -> + | SynExpr.LetOrUseOrAndBang (range=m) -> error(Error(FSComp.SR.tcConstructRequiresComputationExpression(), m)) | SynExpr.MatchBang (_, _, _, m) -> @@ -7882,7 +7882,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // Rebind using either for ... or let!.... let rebind = if maintainsVarSpaceUsingBind then - SynExpr.LetOrUseAndBang(NoSequencePointAtLetBinding, false, false, intoPat, dataCompAfterOp, intoPat.Range, [], contExpr) + SynExpr.LetOrUseOrAndBang(NoSequencePointAtLetBinding, false, false, intoPat, dataCompAfterOp, intoPat.Range, [], contExpr) else SynExpr.ForEach (NoSequencePointAtForLoop, SeqExprOnly false, false, intoPat, dataCompAfterOp, contExpr, intoPat.Range) @@ -7904,7 +7904,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // Rebind using either for ... or let!.... let rebind = if lastUsesBind then - SynExpr.LetOrUseAndBang(NoSequencePointAtLetBinding, false, false, varSpacePat, dataCompPrior, compClausesExpr.Range, [], compClausesExpr) + SynExpr.LetOrUseOrAndBang(NoSequencePointAtLetBinding, false, false, varSpacePat, dataCompPrior, compClausesExpr.Range, [], compClausesExpr) else SynExpr.ForEach (NoSequencePointAtForLoop, SeqExprOnly false, false, varSpacePat, dataCompPrior, compClausesExpr, compClausesExpr.Range) @@ -7953,7 +7953,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | SuppressSequencePointOnStmtOfSequential -> SequencePointAtBinding m | SuppressSequencePointOnExprOfSequential -> NoSequencePointAtDoBinding | SequencePointsAtSeq -> SequencePointAtBinding m - Some(trans true q varSpace (SynExpr.LetOrUseAndBang(sp, false, true, SynPat.Const(SynConst.Unit, rhsExpr.Range), rhsExpr, m, [], innerComp2)) translatedCtxt) + Some(trans true q varSpace (SynExpr.LetOrUseOrAndBang(sp, false, true, SynPat.Const(SynConst.Unit, rhsExpr.Range), rhsExpr, m, [], innerComp2)) translatedCtxt) // "expr; cexpr" is treated as sequential execution | _ -> Some (trans true q varSpace innerComp2 (fun holeFill -> translatedCtxt (SynExpr.Sequential(sp, true, innerComp1, holeFill, m)))) @@ -8009,7 +8009,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some (translatedCtxt (mkSynCall "Using" bindRange [rhsExpr; consumeExpr ])) // 'let! pat = expr in expr' --> build.Bind(e1, (function _argN -> match _argN with pat -> expr)) - | SynExpr.LetOrUseAndBang(spBind, false, isFromSource, pat, rhsExpr, _, [], innerComp) -> + | SynExpr.LetOrUseOrAndBang(spBind, false, isFromSource, pat, rhsExpr, _, [], innerComp) -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) @@ -8029,8 +8029,8 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv translatedCtxt (mkSynCall "Bind" bindRange [rhsExpr; consumeExpr]))) // 'use! pat = e1 in e2' --> build.Bind(e1, (function _argN -> match _argN with pat -> build.Using(x, (fun _argN -> match _argN with pat -> e2)))) - | SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, [], innerComp) - | SynExpr.LetOrUseAndBang(spBind, true, isFromSource, (SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) as pat), rhsExpr, _, [], innerComp) -> + | SynExpr.LetOrUseOrAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, [], innerComp) + | SynExpr.LetOrUseOrAndBang(spBind, true, isFromSource, (SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) as pat), rhsExpr, _, [], innerComp) -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) @@ -8043,15 +8043,15 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some(translatedCtxt (mkSynCall "Bind" bindRange [rhsExpr; consumeExpr])) // 'use! pat = e1 ... in e2' where 'pat' is not a simple name --> error - | SynExpr.LetOrUseAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> + | SynExpr.LetOrUseOrAndBang(_spBind, true, _isFromSource, pat, _rhsExpr, _, [], _innerComp) -> error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // 'let! pat1 = expr1 and! pat2 = expr2 ... and! patN = exprN in yield expr3' --> error - | SynExpr.LetOrUseAndBang(_, _, _, _, _, _, _::_, SynExpr.YieldOrReturn((isYield, _), _, yieldRange)) when isYield = true -> + | SynExpr.LetOrUseOrAndBang(_, _, _, _, _, _, _::_, SynExpr.YieldOrReturn((isYield, _), _, yieldRange)) when isYield = true -> error(Error(FSComp.SR.tcYieldInsteadOfReturnInApplicativeComputationExpression(), yieldRange)) // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3 ; moreBody' --> error - | SynExpr.LetOrUseAndBang(_, _, _, _, _, _, _::_, SynExpr.Sequential(_, _, SynExpr.YieldOrReturn((isYield, _), _, _), moreBodyExpr, _)) when isYield = false -> + | SynExpr.LetOrUseOrAndBang(_, _, _, _, _, _, _::_, SynExpr.Sequential(_, _, SynExpr.YieldOrReturn((isYield, _), _, _), moreBodyExpr, _)) when isYield = false -> error(Error(FSComp.SR.tcMoreAfterReturnInApplicativeComputationExpression(), moreBodyExpr.Range)) // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> @@ -8068,7 +8068,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // 'use! pat = e1 and! pat = e2 in e3' --> TODO // 'let! pat = e1 anduse! pat = e2 in e3' --> TODO // 'use! pat = e1 anduse! pat = e2 in e3' --> TODO - | SynExpr.LetOrUseAndBang(letSpBind, letIsUse, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, _), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! + | SynExpr.LetOrUseOrAndBang(letSpBind, letIsUse, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, _), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! let bindingsBottomToTop = let letBinding = @@ -8141,7 +8141,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some (translatedCtxt desugared) - | SynExpr.LetOrUseAndBang (_, _, _, _, _, _, _, innerComp) -> + | SynExpr.LetOrUseOrAndBang (_, _, _, _, _, _, _, innerComp) -> error(Error(FSComp.SR.tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn(), innerComp.Range)) | SynExpr.Match (spMatch, expr, clauses, m) -> @@ -8209,7 +8209,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv SynExpr.ImplicitZero m else SynExpr.YieldOrReturn((false, true), SynExpr.Const(SynConst.Unit, m), m) - trans true q varSpace (SynExpr.LetOrUseAndBang(NoSequencePointAtDoBinding, false, false, SynPat.Const(SynConst.Unit, mUnit), rhsExpr, m, [], bodyExpr)) translatedCtxt + trans true q varSpace (SynExpr.LetOrUseOrAndBang(NoSequencePointAtDoBinding, false, false, SynPat.Const(SynConst.Unit, mUnit), rhsExpr, m, [], bodyExpr)) translatedCtxt // "expr;" in final position is treated as { expr; zero } // Suppress the sequence point on the "zero" | _ -> @@ -8388,7 +8388,7 @@ and TcSequenceExpression cenv env tpenv comp overallTy m = //SEQPOINT NEEDED - we must consume spBind on this path Some(mkSeqUsing cenv env wholeExprMark bindPatTy genOuterTy inputExpr consumeExpr, tpenv) - | SynExpr.LetOrUseAndBang(_, _, _, _, _, m, _, _) -> + | SynExpr.LetOrUseOrAndBang(_, _, _, _, _, m, _, _) -> error(Error(FSComp.SR.tcUseForInSequenceExpression(), m)) | SynExpr.Match (spMatch, expr, clauses, _) -> @@ -9004,7 +9004,7 @@ and TcItemThen cenv overallTy env tpenv (item, mItem, rest, afterResolution) del | SynExpr.YieldOrReturn _ | SynExpr.YieldOrReturnFrom _ | SynExpr.MatchBang _ - | SynExpr.LetOrUseAndBang _ + | SynExpr.LetOrUseOrAndBang _ | SynExpr.DoBang _ | SynExpr.TraitCall _ -> false diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index 38df8243ee..07716ba606 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -697,7 +697,7 @@ and /// F# syntax: let! pat = expr anduse! ... and! ... and! pat = expr in expr /// F# syntax: use! pat = expr and! ... anduse! ... and! pat = expr in expr /// Computation expressions only - | LetOrUseAndBang of bindSeqPoint:SequencePointInfoForBinding * bool * bool * SynPat * rhs:SynExpr * range:range * andBangs:(SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * body:SynExpr // TODO Why is the range not the last arg anymore? Consider swapping that back? + | LetOrUseOrAndBang of bindSeqPoint:SequencePointInfoForBinding * bool * bool * SynPat * rhs:SynExpr * range:range * andBangs:(SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * body:SynExpr // TODO Why is the range not the last arg anymore? Consider swapping that back? /// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN | MatchBang of matchSeqPoint:SequencePointInfoForBinding * expr:SynExpr * clauses:SynMatchClause list * range:range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *) @@ -789,7 +789,7 @@ and | SynExpr.ImplicitZero (range=m) | SynExpr.YieldOrReturn (range=m) | SynExpr.YieldOrReturnFrom (range=m) - | SynExpr.LetOrUseAndBang (range=m) + | SynExpr.LetOrUseOrAndBang (range=m) | SynExpr.MatchBang (range=m) | SynExpr.DoBang (range=m) | SynExpr.Fixed (range=m) -> m @@ -851,7 +851,7 @@ and | SynExpr.ImplicitZero (range=m) | SynExpr.YieldOrReturn (range=m) | SynExpr.YieldOrReturnFrom (range=m) - | SynExpr.LetOrUseAndBang (range=m) + | SynExpr.LetOrUseOrAndBang (range=m) | SynExpr.MatchBang (range=m) | SynExpr.DoBang (range=m) -> m | SynExpr.DotGet (expr,_,lidwd,m) -> if lidwd.ThereIsAnExtraDotAtTheEnd then unionRanges expr.Range lidwd.RangeSansAnyExtraDot else m @@ -915,7 +915,7 @@ and | SynExpr.ImplicitZero (range=m) | SynExpr.YieldOrReturn (range=m) | SynExpr.YieldOrReturnFrom (range=m) - | SynExpr.LetOrUseAndBang (range=m) + | SynExpr.LetOrUseOrAndBang (range=m) | SynExpr.MatchBang (range=m) | SynExpr.DoBang (range=m) -> m // these are better than just .Range, and also commonly applicable inside queries @@ -1622,7 +1622,7 @@ let rec IsControlFlowExpression e = // Treat "ident { ... }" as a control flow expression | SynExpr.App (_, _, SynExpr.Ident _, SynExpr.CompExpr _,_) | SynExpr.IfThenElse _ - | SynExpr.LetOrUseAndBang _ + | SynExpr.LetOrUseOrAndBang _ | SynExpr.Match _ | SynExpr.TryWith _ | SynExpr.TryFinally _ @@ -2429,6 +2429,6 @@ let rec synExprContainsError inpExpr = | SynExpr.MatchBang (_,e,cl,_) -> walkExpr e || walkMatchClauses cl - | SynExpr.LetOrUseAndBang (rhs=e1;body=e2;andBangs=es) -> + | SynExpr.LetOrUseOrAndBang (rhs=e1;body=e2;andBangs=es) -> walkExpr e1 || walkExprs [ for (_,_,_,_,e,_) in es do yield e ] || walkExpr e2 walkExpr inpExpr diff --git a/src/fsharp/service/ServiceAssemblyContent.fs b/src/fsharp/service/ServiceAssemblyContent.fs index 4828937b2c..c8b42f8760 100644 --- a/src/fsharp/service/ServiceAssemblyContent.fs +++ b/src/fsharp/service/ServiceAssemblyContent.fs @@ -705,7 +705,7 @@ module ParsedInput = addLongIdentWithDots ident List.iter walkExpr [e1; e2; e3] | SynExpr.JoinIn (e1, _, e2, _) -> List.iter walkExpr [e1; e2] - | SynExpr.LetOrUseAndBang (_, _, _, pat, e1, _, es, e2) -> + | SynExpr.LetOrUseOrAndBang (_, _, _, pat, e1, _, es, e2) -> walkPat pat walkExpr e1 for (_,_,_,patAndBang,eAndBang,_) in es do diff --git a/src/fsharp/service/ServiceInterfaceStubGenerator.fs b/src/fsharp/service/ServiceInterfaceStubGenerator.fs index 7e720ddab2..5005ca6419 100644 --- a/src/fsharp/service/ServiceInterfaceStubGenerator.fs +++ b/src/fsharp/service/ServiceInterfaceStubGenerator.fs @@ -894,7 +894,7 @@ module internal InterfaceStubGenerator = | SynExpr.DoBang(synExpr, _range) -> walkExpr synExpr - | SynExpr.LetOrUseAndBang(_sequencePointInfoForBinding, _, _, _synPat, synExpr1, _range, synExprAndBangs, synExpr2) -> + | SynExpr.LetOrUseOrAndBang(_sequencePointInfoForBinding, _, _, _synPat, synExpr1, _range, synExprAndBangs, synExpr2) -> [ yield synExpr1 for (_,_,_,_,eAndBang,_) in synExprAndBangs do diff --git a/src/fsharp/service/ServiceParseTreeWalk.fs b/src/fsharp/service/ServiceParseTreeWalk.fs index e17e1485e0..c2cacfc570 100755 --- a/src/fsharp/service/ServiceParseTreeWalk.fs +++ b/src/fsharp/service/ServiceParseTreeWalk.fs @@ -436,7 +436,7 @@ module public AstTraversal = | SynExpr.ImplicitZero(_range) -> None | SynExpr.YieldOrReturn(_, synExpr, _range) -> traverseSynExpr synExpr | SynExpr.YieldOrReturnFrom(_, synExpr, _range) -> traverseSynExpr synExpr - | SynExpr.LetOrUseAndBang(_sequencePointInfoForBinding, _, _, synPat, synExpr, _range, andBangSynExprs, synExpr2) -> + | SynExpr.LetOrUseOrAndBang(_sequencePointInfoForBinding, _, _, synPat, synExpr, _range, andBangSynExprs, synExpr2) -> [ yield dive synPat synPat.Range traversePat yield dive synExpr synExpr.Range traverseSynExpr diff --git a/src/fsharp/service/ServiceStructure.fs b/src/fsharp/service/ServiceStructure.fs index ac29501bf1..c8b89b8e16 100644 --- a/src/fsharp/service/ServiceStructure.fs +++ b/src/fsharp/service/ServiceStructure.fs @@ -115,7 +115,7 @@ module Structure = | Attribute | Interface | HashDirective - | LetOrUseOrAndOrAndUseBang + | LetOrUseOrAndBang | TypeExtension | YieldOrReturn | YieldOrReturnBang @@ -164,7 +164,7 @@ module Structure = | Attribute -> "Attribute" | Interface -> "Interface" | HashDirective -> "HashDirective" - | LetOrUseOrAndOrAndUseBang -> "LetOrUseOrAndOrAndUseBang" + | LetOrUseOrAndBang -> "LetOrUseOrAndBang" | TypeExtension -> "TypeExtension" | YieldOrReturn -> "YieldOrReturn" | YieldOrReturnBang -> "YieldOrReturnBang" @@ -245,7 +245,7 @@ module Structure = | SynExpr.DoBang (e,r) -> rcheck Scope.Do Collapse.Below r <| Range.modStart 3 r parseExpr e - | SynExpr.LetOrUseAndBang (_,_,_,pat,eLet,_,es,eBody) -> + | SynExpr.LetOrUseOrAndBang (_,_,_,pat,eLet,_,es,eBody) -> [ yield eLet yield! [ for (_,_,_,_,eAndBang,_) in es do yield eAndBang ] diff --git a/src/fsharp/service/ServiceStructure.fsi b/src/fsharp/service/ServiceStructure.fsi index 9372f74888..ec6e8bbd29 100644 --- a/src/fsharp/service/ServiceStructure.fsi +++ b/src/fsharp/service/ServiceStructure.fsi @@ -53,7 +53,7 @@ module public Structure = | Attribute | Interface | HashDirective - | LetOrUseOrAndOrAndUseBang + | LetOrUseOrAndBang | TypeExtension | YieldOrReturn | YieldOrReturnBang diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index f457c6f59f..95c30078cd 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -312,7 +312,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: Ast.ParsedInput op yield! walkExpr false e2 yield! walkExpr false e3 - | SynExpr.LetOrUseAndBang (spBind,_,_,_,e1,_,es,e2) -> + | SynExpr.LetOrUseOrAndBang (spBind,_,_,_,e1,_,es,e2) -> yield! walkBindSeqPt spBind yield! walkExpr true e1 for (andBangSpBind,_,_,_,eAndBang,_) in es do @@ -875,7 +875,7 @@ module UntypedParseImpl = | SynExpr.Match(_, e, synMatchClauseList, _) | SynExpr.MatchBang(_, e, synMatchClauseList, _) -> walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkClause synMatchClauseList) - | SynExpr.LetOrUseAndBang(_, _, _, _, e1, _, es, e2) -> + | SynExpr.LetOrUseOrAndBang(_, _, _, _, e1, _, es, e2) -> [ yield e1 for (_,_,_,_,eAndBang,_) in es do diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index bab55bea5b..9ecea05172 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -54,7 +54,7 @@ module internal BlockStructure = | Scope.Quote | Scope.SpecialFunc | Scope.Lambda - | Scope.LetOrUseOrAndOrAndUseBang + | Scope.LetOrUseOrAndBang | Scope.Val | Scope.YieldOrReturn | Scope.YieldOrReturnBang @@ -108,7 +108,7 @@ module internal BlockStructure = | Scope.CompExprInternal | Scope.Quote | Scope.Lambda - | Scope.LetOrUseOrAndOrAndUseBang + | Scope.LetOrUseOrAndBang | Scope.Val | Scope.YieldOrReturn | Scope.YieldOrReturnBang From 472d8561d4efb1afee4536bfd2f653a89e2932af Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 15:31:21 +0100 Subject: [PATCH 234/255] Reduce guarantees for VS autocomplete Earlier let! bindings in applicative bings are now suggested in later and!/anduse! bindings - not ideal, but I think VS autocomplete generally doesn't attempt to be too smart about this. --- .../LegacyLanguageService/Tests.LanguageService.Completion.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs b/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs index 43511aae25..eaa30710c7 100644 --- a/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs +++ b/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.Completion.fs @@ -3357,7 +3357,7 @@ let x = query { for bbbb in abbbbc(*D0*) do " and! y = "] "and! y = " ["foo";"bar"] - ["x"] + [(*"x"*)] (**) [] From 802e1ce291760bb5496d62c91e827acb2b5122bd Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 15:37:30 +0100 Subject: [PATCH 235/255] Remove toy .fsx file --- scratch/data/SimpleBuilder.fsx | 216 --------------------------------- 1 file changed, 216 deletions(-) delete mode 100644 scratch/data/SimpleBuilder.fsx diff --git a/scratch/data/SimpleBuilder.fsx b/scratch/data/SimpleBuilder.fsx deleted file mode 100644 index 6266ac2e65..0000000000 --- a/scratch/data/SimpleBuilder.fsx +++ /dev/null @@ -1,216 +0,0 @@ - -[] -type OptionalBuilder = - - member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = - match fOpt, xOpt with - | Some f, Some x -> Some (f x) - | _ -> None - - member __.Return(x : 'a) : 'a option = Some x - - // Below is only needed to make yield keyword work - - member __.YieldFrom(x : 'a) : 'a = x - - member __.Delay(f : unit -> 'a) : 'a = f () // If you type `Delay : 'a -> 'a` explicitly, it builds but things get weird. Fast. - - member __.Zero() : unit option = Some () - - member __.Combine(xOpt : 'a option, yOpt : 'a option) : 'a option = - match xOpt with - | Some _ -> xOpt - | None -> yOpt - -let opt = OptionalBuilder() - -let fOpt = Some (sprintf "f (x = %d) (y = %s) (z = %f)") -let xOpt = Some 1 -let yOpt = Some "A" -let zOpt = Some 3.0 - -let superSimpleExampleDesugared : bool option = - opt.Apply( - opt.Apply( - opt.Return( - (fun x -> - (fun y -> - x || y - ) - ) - ), - Some true), - Some false) - -printfn "Super simple example desugared: \"%+A\"" superSimpleExampleDesugared - -let superSimpleExample : bool option = - opt { - let! (x : bool) = Some true - and! (y : bool) = Some false - return x || y - } - -printfn "Super simple example: \"%+A\"" superSimpleExample - -let foo : string option = - opt { - let! f = fOpt - and! x = xOpt - and! y = yOpt - and! z = zOpt - return (f x y z) - } - -printfn "foo: \"%+A\"" foo - -let bar : int option = - opt { - let! x = None - and! y = Some 1 - and! z = Some 2 - return x + y + z + 1 - } - -printfn "bar: %+A" bar - -type 'a SingleCaseDu = SingleCaseDu of 'a - -let baz : int option = - opt { - let! x = Some 5 - and! (SingleCaseDu y) = Some (SingleCaseDu 40) - and! (z,_) = Some (300, "whatever") - return (let w = 10000 in w + x + y + z + 2000) - } - -printfn "baz: %+A" baz - -let quux : int option = - opt { - let! a = - opt { // Takes the first yield!'d value that is not None, i.e. takes the second block in this case - // You caan view yield! (in this builder) to mean a prioritied list of values, where the highest priority Some wins - yield! - opt { - let! x = Some 11 - and! y = None - and! z = Some 2 - return x + y + z + 1 // Bails out because y is None - } - yield! - opt { - let! x = Some -3 - and! y = Some 1 - and! z = Some 2 - return x + y + z + 4 // Computes result because all args are Some - } - } - and! b = - opt { - let! x = Some 9 - and! _ = Some "IGNORED" // Inner value goes unused, but we'd still bail out if this value were None - and! z = Some 4 - return x + z - 10 - } - and! c = - opt { - let! x = Some 0 - and! y = Some 1 - and! z = Some 2 - return (x + y + z + 1) - } - and! d = baz // Makes use of an optional value from outside the computation expression (bailing out with None if it is None, as with any other value) - return a * b * c * d // Computes this value because all args are some - // Another way to view this is let! ... and! ... is a bit like a conjunction, and yield! ... yield! ... is a bit like a disjunction - } - -printfn "quux: %+A" quux - -type TraceOptBuilder() = - - member __.Apply(fOpt : ('a -> 'b) option, xOpt : 'a option) : 'b option = - match fOpt, xOpt with - | Some f, Some x -> - printfn "Applying %+A to %+A" f x - Some (f x) - | _ -> - printfn "Applying with None. Exiting." - None - - member __.Return(x) = Some x - - member __.Yield(x) = Some x - - member __.Zero() = - printfn "Zero" - Some () - - member __.Combine(xOpt, yOpt) = - let res = - match xOpt with - | Some _ -> - xOpt - | None -> - yOpt - printfn "Combining %+A with %+A to get %+A" xOpt yOpt res - res - - member __.MapTryFinally(body, compensation) = - try body() - finally compensation() - - member __.MapUsing(disposable:#System.IDisposable, body) = - printfn "Using disposable %O" disposable - let body' = fun () -> - printfn ">Running body" - let res = body disposable - printfn " - printfn "Disposing %O" disposable - disposable.Dispose()) - -let traceOpt = TraceOptBuilder() - -type FakeDisposable = - FakeDisposable of int - with - interface System.IDisposable with - member __.Dispose() = - let (FakeDisposable x) = __ - printfn "\"Disposed\" %+A" x - () - -printfn "\n\nStarting Using experiment...\n\n" - -let simpleFooUsing : string option = - traceOpt { - use! xUsing = Some (FakeDisposable 1) - anduse! yUsing = Some (FakeDisposable 2) - return (printfn "Calling return expr - nothing should have been disposed yet!"; sprintf "xUsing = %+A, yUsing = %+A" xUsing yUsing) - } - -printfn "simplefooUsing: \"%+A\"\n\n" simpleFooUsing - -let fooUsing : string option = - traceOpt { - use! xUsing = Some (FakeDisposable 1) - anduse! yUsing = Some (FakeDisposable 2) - anduse! zUsing = Some (FakeDisposable 3) - return (let (FakeDisposable x) = xUsing in sprintf "Unwrapped x = %d" x) - } - -printfn "fooUsing: \"%+A\"\n\n" fooUsing - -(* INVALID: Not a simple name on the LHS -let fooUsing2 : int option = - traceOpt { - use! (FakeDisposable x) = Some (FakeDisposable 1) - anduse! (FakeDisposable y) = Some (FakeDisposable 2) - anduse! (FakeDisposable z) = Some (FakeDisposable 3) - return x * y + z - } - -printfn "fooUsing2: \"%+A\"" fooUsing -*) \ No newline at end of file From fa734553fcc2bd7ec089ec2e850f8db2a7b4b6ec Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 10 Oct 2018 15:38:28 +0100 Subject: [PATCH 236/255] Remove reference to SimpleBuilder.fsx --- src/fsharp/Fsc/Fsc.fsproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/fsharp/Fsc/Fsc.fsproj b/src/fsharp/Fsc/Fsc.fsproj index 5224032505..b9cf2ca947 100644 --- a/src/fsharp/Fsc/Fsc.fsproj +++ b/src/fsharp/Fsc/Fsc.fsproj @@ -24,9 +24,6 @@ true $(OtherFlags) --warnon:1182 - - C:\Users\Tom\Documents\FSharpContribs\visualfsharp\scratch\data\SimpleBuilder.fsx - @@ -52,4 +49,4 @@ FSharp.Core - \ No newline at end of file + From ecad9936863535c6d5a4a3265862518dfcaf293e Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 15:53:10 +0100 Subject: [PATCH 237/255] Simplify pattern matching using named DU case args --- src/fsharp/TypeChecker.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index f12c8fab8f..be8b742368 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8047,11 +8047,11 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // 'let! pat1 = expr1 and! pat2 = expr2 ... and! patN = exprN in yield expr3' --> error - | SynExpr.LetOrUseOrAndBang(_, _, _, _, _, _, _::_, SynExpr.YieldOrReturn((isYield, _), _, yieldRange)) when isYield = true -> + | SynExpr.LetOrUseOrAndBang(andBangs=_::_; body=SynExpr.YieldOrReturn((isYield, _), _, yieldRange)) when isYield = true -> error(Error(FSComp.SR.tcYieldInsteadOfReturnInApplicativeComputationExpression(), yieldRange)) // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3 ; moreBody' --> error - | SynExpr.LetOrUseOrAndBang(_, _, _, _, _, _, _::_, SynExpr.Sequential(_, _, SynExpr.YieldOrReturn((isYield, _), _, _), moreBodyExpr, _)) when isYield = false -> + | SynExpr.LetOrUseOrAndBang(andBangs=_::_; body=SynExpr.Sequential(expr1=SynExpr.YieldOrReturn((isYield, _), _, _); expr2=moreBodyExpr)) when isYield = false -> error(Error(FSComp.SR.tcMoreAfterReturnInApplicativeComputationExpression(), moreBodyExpr.Range)) // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> From f640c21c9fc6dfc8dc4e1ab50289ab500ac22edb Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 16:05:01 +0100 Subject: [PATCH 238/255] Clean up comments and formatting around new let! ... and! ... stntax handling --- src/fsharp/TypeChecker.fs | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index be8b742368..d3a8c23e6d 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8047,28 +8047,45 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) // 'let! pat1 = expr1 and! pat2 = expr2 ... and! patN = exprN in yield expr3' --> error - | SynExpr.LetOrUseOrAndBang(andBangs=_::_; body=SynExpr.YieldOrReturn((isYield, _), _, yieldRange)) when isYield = true -> + | SynExpr.LetOrUseOrAndBang(andBangs=_::_; body=SynExpr.YieldOrReturn((true, _), _, yieldRange)) -> error(Error(FSComp.SR.tcYieldInsteadOfReturnInApplicativeComputationExpression(), yieldRange)) // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3 ; moreBody' --> error - | SynExpr.LetOrUseOrAndBang(andBangs=_::_; body=SynExpr.Sequential(expr1=SynExpr.YieldOrReturn((isYield, _), _, _); expr2=moreBodyExpr)) when isYield = false -> + | SynExpr.LetOrUseOrAndBang(andBangs=_::_; body=SynExpr.Sequential(expr1=SynExpr.YieldOrReturn((_, true), _, _); expr2=moreBodyExpr)) -> error(Error(FSComp.SR.tcMoreAfterReturnInApplicativeComputationExpression(), moreBodyExpr.Range)) // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3' --> // build.Apply( // build.Apply( // build.Return( - // (function _arg1 -> match _arg1 with pat1 -> - // (function _arg2 -> match _arg2 with pat2 -> + // (fun x -> + // (fun y -> // expr3 // ) // ) // ), expr1) // ), expr2) - // 'use! pat = e1 and! pat = e2 in e3' --> TODO - // 'let! pat = e1 anduse! pat = e2 in e3' --> TODO - // 'use! pat = e1 anduse! pat = e2 in e3' --> TODO - | SynExpr.LetOrUseOrAndBang(letSpBind, letIsUse, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((isYield, _), returnExpr, returnRange)) when isYield = false -> // TODO Handle use! / anduse! + // + // 'use! pat = e1 anduse! pat = e2 in e3' --> + // build.Apply( + // build.Apply( + // build.Return( + // (fun x -> + // (fun y -> + // ce.ApplyUsing(x, fun x -> + // ce.ApplyUsing(y, fun y -> + // expr3 + // ) + // ) + // ) + // ) + // ), e1) + // ), e2) + // + // Similarly for: + // 'use! pat = e1 and! pat = e2 in e3' + // 'let! pat = e1 anduse! pat = e2 in return e3' + | SynExpr.LetOrUseOrAndBang(letSpBind, letIsUse, letIsFromSource, letPat, letRhsExpr, letm, andBangBindings, SynExpr.YieldOrReturn((_, true), returnExpr, returnRange)) -> let bindingsBottomToTop = let letBinding = @@ -8110,7 +8127,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv |> List.fold (fun acc (spBind, isUse, _, pat, rhsExpr, m) -> match isUse, pat with | true, SynPat.Named (SynPat.Wild _, id, false, _, _) - | true, SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) -> + | true, SynPat.LongIdent (longDotId=LongIdentWithDots([id], _)) -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "ApplyUsing" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("ApplyUsing"), m)) @@ -8141,7 +8158,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv Some (translatedCtxt desugared) - | SynExpr.LetOrUseOrAndBang (_, _, _, _, _, _, _, innerComp) -> + | SynExpr.LetOrUseOrAndBang (body=innerComp) -> error(Error(FSComp.SR.tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn(), innerComp.Range)) | SynExpr.Match (spMatch, expr, clauses, m) -> From f0e67c9e3a8e2adc1510fbe9edd3df54d976a662 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 16:12:58 +0100 Subject: [PATCH 239/255] Use more named DU args to make intent clearer --- src/fsharp/TypeChecker.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index d3a8c23e6d..95f094d8f5 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8030,7 +8030,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // 'use! pat = e1 in e2' --> build.Bind(e1, (function _argN -> match _argN with pat -> build.Using(x, (fun _argN -> match _argN with pat -> e2)))) | SynExpr.LetOrUseOrAndBang(spBind, true, isFromSource, (SynPat.Named (SynPat.Wild _, id, false, _, _) as pat) , rhsExpr, _, [], innerComp) - | SynExpr.LetOrUseOrAndBang(spBind, true, isFromSource, (SynPat.LongIdent (LongIdentWithDots([id], _), _, _, _, _, _) as pat), rhsExpr, _, [], innerComp) -> + | SynExpr.LetOrUseOrAndBang(spBind, true, isFromSource, (SynPat.LongIdent (longDotId=LongIdentWithDots([id], _)) as pat), rhsExpr, _, [], innerComp) -> let bindRange = match spBind with SequencePointAtBinding(m) -> m | _ -> rhsExpr.Range if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), bindRange)) From 5d8e0cc37cd6674d7c9889d9e161e5a3f48549cc Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 16:13:23 +0100 Subject: [PATCH 240/255] Use new name for let! ... syntax node --- src/fsharp/pars.fsy | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 885bc1baab..be13fa937e 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3007,20 +3007,20 @@ binders: | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let { let spBind = SequencePointAtBinding(rhs2 parseState 1 5) let m = unionRanges (rhs parseState 1) $8.Range - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } + SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let { $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let m = unionRanges (rhs parseState 1) $8.Range - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } + SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } /* 'let! ... in and! ... in ...' with no return */ | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders %prec expr_let { let m = lhs parseState reportParseErrorAt m (FSComp.SR.parsNoBodyInApplicativeComputationExpression()) let spBind = SequencePointAtBinding(rhs2 parseState 1 5) - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7, SynExpr.ImplicitZero m) } + SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4,m,$7, SynExpr.ImplicitZero m) } /* 'let! ... and! ...' with no return */ | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders %prec expr_let @@ -3028,7 +3028,7 @@ binders: reportParseErrorAt m (FSComp.SR.parsNoBodyInApplicativeComputationExpression()) $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4,m,$7, SynExpr.ImplicitZero m) } + SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4,m,$7, SynExpr.ImplicitZero m) } /* -- TODO: Reintroduce? | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders error %prec expr_let @@ -3036,7 +3036,7 @@ binders: let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) // TODO Check this range let m = $4.Range.EndRange // zero-width range - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, $7, SynExpr.ImplicitZero m) } + SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4, mAll, $7, SynExpr.ImplicitZero m) } */ | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let // TODO Fix CompletionInDifferentEnvs3 @@ -3044,7 +3044,7 @@ binders: let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) // TODO Check this range let m = $4.Range.EndRange // zero-width range - SynExpr.LetOrUseAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } + SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4, mAll, [], SynExpr.ImplicitZero m) } morebinders: | AND_BANG headBindingPattern EQUALS typedSeqExprBlock IN morebinders %prec expr_let /* TODO Check precedence */ From 2ed50a59c8bba38692ae7d979dbeda35a1751d94 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 16:14:40 +0100 Subject: [PATCH 241/255] Fix more let! ... syntax node rename fallout --- src/fsharp/service/ServiceStructure.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/service/ServiceStructure.fs b/src/fsharp/service/ServiceStructure.fs index c8b89b8e16..28dd103ca8 100644 --- a/src/fsharp/service/ServiceStructure.fs +++ b/src/fsharp/service/ServiceStructure.fs @@ -256,7 +256,7 @@ module Structure = // on the same line if there is an `=` the range will be adjusted during the // tooltip creation let r = Range.endToEnd pat.Range e.Range - rcheck Scope.LetOrUseOrAndOrAndUseBang Collapse.Below r r + rcheck Scope.LetOrUseOrAndBang Collapse.Below r r parseExpr e ) parseExpr eBody From d663cf0794a89520092180210410d33427d9153c Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 16:15:27 +0100 Subject: [PATCH 242/255] Fix do! using old let! ... stntax node name --- src/fsharp/pars.fsy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index be13fa937e..0136dcbb99 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3360,7 +3360,7 @@ declExpr: | DO_BANG typedSeqExpr IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let { let spBind = NoSequencePointAtDoBinding - SynExpr.LetOrUseAndBang(spBind,false,true,SynPat.Const(SynConst.Unit,$2.Range),$2, unionRanges (rhs parseState 1) $5.Range, [], $5) } + SynExpr.LetOrUseOrAndBang(spBind,false,true,SynPat.Const(SynConst.Unit,$2.Range),$2, unionRanges (rhs parseState 1) $5.Range, [], $5) } | ODO_BANG typedSeqExprBlock hardwhiteDefnBindingsTerminator %prec expr_let { SynExpr.DoBang($2, unionRanges (rhs parseState 1) $2.Range) } From 95753dfd920d732a2c45c8b1f8eb9bf61231d898 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 16:27:30 +0100 Subject: [PATCH 243/255] Tidy comments in TypeChecker.fs --- src/fsharp/TypeChecker.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 95f094d8f5..1bed3c804f 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8134,7 +8134,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let applyUsingBodyExpr = SynExpr.MatchLambda(false, bindRange, [Clause(pat, None, acc, acc.Range, SequencePointAtTarget)], spBind, bindRange) // TODO Where should we be suppressing sequence points? mkSynCall "ApplyUsing" bindRange [ SynExpr.Ident(id); applyUsingBodyExpr ] | true, _ -> - // TODO Support explicitly typed names on the LHS of a use!/anduse! + // SynPat.Typed unsupported for now... error(Error(FSComp.SR.tcInvalidAndUseBangBinding(), pat.Range)) | false, _ -> acc @@ -8147,13 +8147,13 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let innerRange = returnExpr.Range SynExpr.MatchLambda(false, pat.Range, [Clause(pat, None, acc, innerRange, SequencePointAtTarget)], spBind, innerRange) ) usings - // We take the nested lambdas and put the return back, _outside_ them - // to give an f : F<'a -> 'b -> 'c -> ...> as a series of Apply calls expects + // We take the nested lambdas and rewrap the call to 'Return' _outside_ them + // to give an f : F<'a -> 'b -> 'c -> ... > as a series of Apply calls expects |> (fun f -> if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env returnRange ad "Return" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Return"), returnRange)) mkSynCall "Return" returnRange [f]) - // We wrap the return in a call to Apply for each lambda + // We finally wrap the return in a call to Apply for each lambda |> wrapInCallsToApply Some (translatedCtxt desugared) From ebfb8a40166b5cbe80e2f0d29486bbd240649c1b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 16:34:50 +0100 Subject: [PATCH 244/255] Clean up for comments and code style --- src/fsharp/TypeChecker.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 1bed3c804f..6025ece3fc 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8265,7 +8265,7 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let env = match comp with - | SynExpr.YieldOrReturn ((true, _), _, _) -> { env with eContextInfo = ContextInfo.YieldInComputationExpression } // TODO Do we need our own new context info? + | SynExpr.YieldOrReturn ((true, _), _, _) -> { env with eContextInfo = ContextInfo.YieldInComputationExpression } | SynExpr.YieldOrReturn ((_, true), _, _) -> { env with eContextInfo = ContextInfo.ReturnInComputationExpression } | _ -> env @@ -8405,7 +8405,7 @@ and TcSequenceExpression cenv env tpenv comp overallTy m = //SEQPOINT NEEDED - we must consume spBind on this path Some(mkSeqUsing cenv env wholeExprMark bindPatTy genOuterTy inputExpr consumeExpr, tpenv) - | SynExpr.LetOrUseOrAndBang(_, _, _, _, _, m, _, _) -> + | SynExpr.LetOrUseOrAndBang(range=m) -> error(Error(FSComp.SR.tcUseForInSequenceExpression(), m)) | SynExpr.Match (spMatch, expr, clauses, _) -> @@ -8423,7 +8423,7 @@ and TcSequenceExpression cenv env tpenv comp overallTy m = let matchv, matchExpr = CompilePatternForMatchClauses cenv env inputExprMark inputExprMark true ThrowIncompleteMatchException (Some inputExpr) inputExprTy genOuterTy tclauses Some(mkLet spMatch inputExprMark matchv inputExpr matchExpr, tpenv) - | SynExpr.TryWith (_, mTryToWith, _, _, _, _, _) -> + | SynExpr.TryWith (tryRange=mTryToWith) -> error(Error(FSComp.SR.tcTryIllegalInSequenceExpression(), mTryToWith)) | SynExpr.YieldOrReturnFrom((isYield, _), yieldExpr, m) -> From 7cb2c73d62f397b42dc7d7de5a2cbf397a6c7530 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 17:47:06 +0100 Subject: [PATCH 245/255] Add test showing Delay and Run working for applicative CEs --- src/fsharp/TypeChecker.fs | 8 ++--- .../ApplicativeBuilderLib.fs | 36 +++++++++++++------ ...AndBangWithDelayAndRunDesugarsCorrectly.fs | 22 ++++++++++++ .../ComputationExpressions/env.lst | 7 ++-- 4 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 6025ece3fc..1ded867ab7 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8245,26 +8245,24 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv let delayedExpr = match TryFindIntrinsicOrExtensionMethInfo cenv env mBuilderVal ad "Delay" builderTy with | [] -> basicSynExpr - | _ -> mkSynCall "Delay" mBuilderVal [(mkSynDelay2 basicSynExpr)] // TODO If Delay is defined, it is called. Does this work? + | _ -> mkSynCall "Delay" mBuilderVal [(mkSynDelay2 basicSynExpr)] - let quotedSynExpr = if isAutoQuote then SynExpr.Quote(mkSynIdGet (mBuilderVal.MakeSynthetic()) (CompileOpName "<@ @>"), (*isRaw=*)false, delayedExpr, (*isFromQueryExpression=*)true, mWhole) else delayedExpr - let runExpr = match TryFindIntrinsicOrExtensionMethInfo cenv env mBuilderVal ad "Run" builderTy with | [] -> quotedSynExpr - | _ -> mkSynCall "Run" mBuilderVal [quotedSynExpr] // TODO If Run is defined, it is called. Does this work?call + | _ -> mkSynCall "Run" mBuilderVal [quotedSynExpr] let lambdaExpr = let mBuilderVal = mBuilderVal.MakeSynthetic() SynExpr.Lambda (false, false, SynSimplePats.SimplePats ([mkSynSimplePatVar false (mkSynId mBuilderVal builderValName)], mBuilderVal), runExpr, mBuilderVal) let env = - match comp with + match comp with | SynExpr.YieldOrReturn ((true, _), _, _) -> { env with eContextInfo = ContextInfo.YieldInComputationExpression } | SynExpr.YieldOrReturn ((_, true), _, _) -> { env with eContextInfo = ContextInfo.ReturnInComputationExpression } | _ -> env diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs index 21928bf14a..5175e24693 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs @@ -8,6 +8,8 @@ type TraceOp = | StartUsingBody of resource : obj | EndUsingBody of resource : obj | ExitUsing of resource : obj + | Run + | Delay /// A pseudo identity functor type 'a Trace = @@ -17,18 +19,19 @@ type 'a Trace = sprintf "%+A" this /// A builder which records what operations it is asked to perform -type TraceBuilder() = +type TraceBuilder = - let mutable trace : TraceOp list = [] + val mutable trace : TraceOp list + new () = { trace = [] } - member __.GetTrace () = List.rev trace + member __.GetTrace () = List.rev __.trace - member __.Apply((Trace f) as fTrace, (Trace x) as xTrace) = - trace <- Apply :: trace + member __.Apply(Trace f, Trace x) = + __.trace <- Apply :: __.trace Trace (f x) member __.Return(x) = - trace <- Return :: trace + __.trace <- Return :: __.trace Trace x member __.MapTryFinally(body, compensation) = @@ -40,13 +43,26 @@ type TraceBuilder() = /// Doesn't actually do any disposing here, since we are just interesting /// in checking the order of events in this test member __.ApplyUsing(resource(*:#System.IDisposable*), body) = - trace <- EnterUsing resource :: trace + __.trace <- EnterUsing resource :: __.trace let body' = fun () -> - trace <- StartUsingBody resource :: trace + __.trace <- StartUsingBody resource :: __.trace let res = body resource - trace <- EndUsingBody resource :: trace + __.trace <- EndUsingBody resource :: __.trace res __.MapTryFinally(body', fun () -> - trace <- ExitUsing resource :: trace + __.trace <- ExitUsing resource :: __.trace (*resource.Dispose()*) ()) + +/// A builder which records what operations it is asked to perform +/// and records automatically inserted Run and Delay calls +type TraceWithDelayAndRunBuilder() = + inherit TraceBuilder() + + member __.Run(x) = + __.trace <- Run :: __.trace + x + + member __.Delay(thunk) = + __.trace <- Delay :: __.trace + thunk () diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs new file mode 100644 index 0000000000..0d52c72daa --- /dev/null +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs @@ -0,0 +1,22 @@ +// #Misc #AppCE + +//#Expects: Success +// << OUTPUT +//Trace 3, .Delay; Return; Apply; Apply; Run. +//OUTPUT + +open ApplicativeBuilderLib + +module DelayAndRun = + + let () = + let tracerWithDelayAndRun = TraceWithDelayAndRunBuilder() + + let ceResult : int Trace = + tracerWithDelayAndRun { + let! x = Trace 3 + and! y = Trace true + return if y then x else -1 + } + + printfn "%+A, %+A" ceResult (tracerWithDelayAndRun.GetTrace ()) \ No newline at end of file diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst index 1197881dc0..ebc81e0cba 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst @@ -24,9 +24,10 @@ # Executing Workflows SOURCE=RunAndDelay01.fs # RunAndDelay01.fs - SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangDesugarsCorrectly.fs: let! ... and! ... desugars correctly - SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # UseBangAndUseBangDesugarsCorrectly.fs: use! ... anduse! ... desugars correctly - SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # MixOfUseAndLetDesugarsCorrectly.fs: use! ... and! ... anduse! ... desugars correctly + SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangDesugarsCorrectly.fs: let! ... and! ... desugars correctly + SOURCE=LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs: let! ... and! ... (when the builder defines delay and run) desugars correctly + SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # UseBangAndUseBangDesugarsCorrectly.fs: use! ... anduse! ... desugars correctly + SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # MixOfUseAndLetDesugarsCorrectly.fs: use! ... and! ... anduse! ... desugars correctly # Misc ReqRetail SOURCE=Capacity01.fs # Capacity01.fs From a0f13a9d8b24600b9b10e24a8282048b651a9d3b Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 18:02:27 +0100 Subject: [PATCH 246/255] Test Run and Delay translation separately --- .../ApplicativeBuilderLib.fs | 16 ++++++++++++-- ...etBangAndBangWithDelayDesugarsCorrectly.fs | 22 +++++++++++++++++++ .../LetBangAndBangWithRunDesugarsCorrectly.fs | 22 +++++++++++++++++++ .../ComputationExpressions/env.lst | 2 ++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithDelayDesugarsCorrectly.fs create mode 100644 tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithRunDesugarsCorrectly.fs diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs index 5175e24693..5c721c25a9 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs @@ -54,8 +54,6 @@ type TraceBuilder = (*resource.Dispose()*) ()) -/// A builder which records what operations it is asked to perform -/// and records automatically inserted Run and Delay calls type TraceWithDelayAndRunBuilder() = inherit TraceBuilder() @@ -66,3 +64,17 @@ type TraceWithDelayAndRunBuilder() = member __.Delay(thunk) = __.trace <- Delay :: __.trace thunk () + +type TraceWithDelayBuilder() = + inherit TraceBuilder() + + member __.Delay(thunk) = + __.trace <- Delay :: __.trace + thunk () + +type TraceWithRunBuilder() = + inherit TraceBuilder() + + member __.Run(x) = + __.trace <- Run :: __.trace + x diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithDelayDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithDelayDesugarsCorrectly.fs new file mode 100644 index 0000000000..4483d04acd --- /dev/null +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithDelayDesugarsCorrectly.fs @@ -0,0 +1,22 @@ +// #Misc #AppCE + +//#Expects: Success +// << OUTPUT +//Trace 3, .Delay; Return; Apply; Apply. +//OUTPUT + +open ApplicativeBuilderLib + +module DelayAndRun = + + let () = + let tracerWithDelay = TraceWithDelayBuilder() + + let ceResult : int Trace = + tracerWithDelay { + let! x = Trace 3 + and! y = Trace true + return if y then x else -1 + } + + printfn "%+A, %+A" ceResult (tracerWithDelay.GetTrace ()) \ No newline at end of file diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithRunDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithRunDesugarsCorrectly.fs new file mode 100644 index 0000000000..203f0c86de --- /dev/null +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangWithRunDesugarsCorrectly.fs @@ -0,0 +1,22 @@ +// #Misc #AppCE + +//#Expects: Success +// << OUTPUT +//Trace 3, .Return; Apply; Apply; Run. +//OUTPUT + +open ApplicativeBuilderLib + +module DelayAndRun = + + let () = + let tracerWithRun = TraceWithRunBuilder() + + let ceResult : int Trace = + tracerWithRun { + let! x = Trace 3 + and! y = Trace true + return if y then x else -1 + } + + printfn "%+A, %+A" ceResult (tracerWithRun.GetTrace ()) \ No newline at end of file diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst index ebc81e0cba..1409b2ee47 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst @@ -26,6 +26,8 @@ SOURCE=RunAndDelay01.fs # RunAndDelay01.fs SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangDesugarsCorrectly.fs: let! ... and! ... desugars correctly SOURCE=LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs: let! ... and! ... (when the builder defines delay and run) desugars correctly + SOURCE=LetBangAndBangWithRunDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithRunDesugarsCorrectly.fs: let! ... and! ... (when the builder defines run) desugars correctly + SOURCE=LetBangAndBangWithDelayDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithDelayDesugarsCorrectly.fs: let! ... and! ... (when the builder defines delay) desugars correctly SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # UseBangAndUseBangDesugarsCorrectly.fs: use! ... anduse! ... desugars correctly SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # MixOfUseAndLetDesugarsCorrectly.fs: use! ... and! ... anduse! ... desugars correctly From 1206821d70c2d6d71072b94ad81ef35bdfe336a4 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 18:56:00 +0100 Subject: [PATCH 247/255] Fix missing punctuation in comment --- src/fsharp/service/ServiceStructure.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/service/ServiceStructure.fs b/src/fsharp/service/ServiceStructure.fs index 28dd103ca8..4af1e796b6 100644 --- a/src/fsharp/service/ServiceStructure.fs +++ b/src/fsharp/service/ServiceStructure.fs @@ -253,7 +253,7 @@ module Structure = |> List.iter (fun e -> // for `let!`, `use!`, `and!` or `anduse!` the pattern begins at the end of the // keyword so that this scope can be used without adjustment if there is no `=` - // on the same line if there is an `=` the range will be adjusted during the + // on the same line. If there is an `=` the range will be adjusted during the // tooltip creation let r = Range.endToEnd pat.Range e.Range rcheck Scope.LetOrUseOrAndBang Collapse.Below r r From 03002cf572ce24dc521e45dcb43592559d85df1f Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Wed, 10 Oct 2018 18:58:24 +0100 Subject: [PATCH 248/255] Remove old TODO --- src/fsharp/service/ServiceUntypedParse.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 95c30078cd..19ffbd24e3 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -316,7 +316,7 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: Ast.ParsedInput op yield! walkBindSeqPt spBind yield! walkExpr true e1 for (andBangSpBind,_,_,_,eAndBang,_) in es do - yield! walkBindSeqPt andBangSpBind // TODO Check this is valid & useful + yield! walkBindSeqPt andBangSpBind yield! walkExpr true eAndBang yield! walkExpr true e2 From 062d6ef5f986b14a4ebb2eaec71c22673729f0b0 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 15 Oct 2018 11:18:57 +0100 Subject: [PATCH 249/255] Add err msg for yield!/return! instead of return --- .../FSharp.Compiler.Private/FSComp.fs | 6 +++--- .../FSharp.Compiler.Private/FSComp.resx | 4 ++-- src/fsharp/FSComp.txt | 2 +- src/fsharp/TypeChecker.fs | 8 +++++++- src/fsharp/xlf/FSComp.txt.cs.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.de.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.en.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.es.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.fr.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.it.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.ja.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.ko.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.pl.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.ru.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.tr.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 10 +++++----- src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 10 +++++----- .../E_ReturnBangInsteadOfReturn.fs | 13 +++++++++++++ .../ApplicativeComputationExpressions/env.lst | 1 + 20 files changed, 97 insertions(+), 77 deletions(-) create mode 100644 tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ReturnBangInsteadOfReturn.fs diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs b/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs index 8078f372af..90b441204f 100644 --- a/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs +++ b/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs @@ -4363,9 +4363,9 @@ type internal SR private() = /// Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '. /// (Originally from ..\FSComp.txt:1445) static member tcInvalidAndUseBangBinding() = (3244, GetStringFunc("tcInvalidAndUseBangBinding",",,,") ) - /// 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + /// '%s' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? /// (Originally from ..\FSComp.txt:1446) - static member tcYieldInsteadOfReturnInApplicativeComputationExpression() = (3245, GetStringFunc("tcYieldInsteadOfReturnInApplicativeComputationExpression",",,,") ) + static member tcInvalidKeywordInsteadOfReturnInApplicativeComputationExpression(a0 : System.String) = (3245, GetStringFunc("tcInvalidKeywordInsteadOfReturnInApplicativeComputationExpression",",,,%s,,,") a0) /// No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. /// (Originally from ..\FSComp.txt:1447) static member parsNoBodyInApplicativeComputationExpression() = (3246, GetStringFunc("parsNoBodyInApplicativeComputationExpression",",,,") ) @@ -5791,7 +5791,7 @@ type internal SR private() = ignore(GetString("tcTypeDoesNotInheritAttribute")) ignore(GetString("tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn")) ignore(GetString("tcInvalidAndUseBangBinding")) - ignore(GetString("tcYieldInsteadOfReturnInApplicativeComputationExpression")) + ignore(GetString("tcInvalidKeywordInsteadOfReturnInApplicativeComputationExpression")) ignore(GetString("parsNoBodyInApplicativeComputationExpression")) ignore(GetString("tcMoreAfterReturnInApplicativeComputationExpression")) () diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx b/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx index 84da20aae9..d85a861ca0 100644 --- a/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx +++ b/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx @@ -4366,8 +4366,8 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 306f86d2c0..b055e1759c 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1443,6 +1443,6 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3242,tcTypeDoesNotInheritAttribute,"This type does not inherit Attribute, it will not work correctly with other .NET languages." 3243,tcApplicativeComputationExpressionNotImmediatelyTerminatedWithReturn,"Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! = and! = and! ... and! = return '." 3244,tcInvalidAndUseBangBinding,"Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! = ' or 'anduse! = '." -3245,tcYieldInsteadOfReturnInApplicativeComputationExpression,"'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead?" +3245,tcInvalidKeywordInsteadOfReturnInApplicativeComputationExpression,"'%s' is not valid in this position in an applicative computation expression. Did you mean 'return' instead?" 3246,parsNoBodyInApplicativeComputationExpression,"No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression." 3247,tcMoreAfterReturnInApplicativeComputationExpression,"Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'." \ No newline at end of file diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 1ded867ab7..a9d69592cf 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8048,7 +8048,13 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv // 'let! pat1 = expr1 and! pat2 = expr2 ... and! patN = exprN in yield expr3' --> error | SynExpr.LetOrUseOrAndBang(andBangs=_::_; body=SynExpr.YieldOrReturn((true, _), _, yieldRange)) -> - error(Error(FSComp.SR.tcYieldInsteadOfReturnInApplicativeComputationExpression(), yieldRange)) + error(Error(FSComp.SR.tcInvalidKeywordInsteadOfReturnInApplicativeComputationExpression "yield", yieldRange)) + + // 'let! pat1 = expr1 and! pat2 = expr2 ... and! patN = exprN in yield! expr3' --> error + // 'let! pat1 = expr1 and! pat2 = expr2 ... and! patN = exprN in return! expr3' --> error + | SynExpr.LetOrUseOrAndBang(andBangs=_::_; body=SynExpr.YieldOrReturnFrom((isYieldBang, _), _, range)) -> + let keyword = if isYieldBang then "yield!" else "return!" + error(Error(FSComp.SR.tcInvalidKeywordInsteadOfReturnInApplicativeComputationExpression keyword, range)) // 'let! pat1 = expr1 and! pat2 = expr2 in return expr3 ; moreBody' --> error | SynExpr.LetOrUseOrAndBang(andBangs=_::_; body=SynExpr.Sequential(expr1=SynExpr.YieldOrReturn((_, true), _, _); expr2=moreBodyExpr)) -> diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index e6777220b7..b244c45db7 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index 89ceec0f6a..08c190fb76 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.en.xlf b/src/fsharp/xlf/FSComp.txt.en.xlf index fa32866b51..0fc619b5d8 100644 --- a/src/fsharp/xlf/FSComp.txt.en.xlf +++ b/src/fsharp/xlf/FSComp.txt.en.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index 4aacf93e89..7d6917360b 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index d7a1d5c01a..8df57d09db 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index ac3a8e671f..08bb46f68a 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 46c3c03f65..c46fdcc142 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index 2a9df0d655..9c19482952 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 064c41e521..7c47340898 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index dcb11a1f67..ea0e7691fb 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index c15d700665..3740a4aa46 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index 7639b8399c..b500319209 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 36298e9758..0ee0dd83da 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 97433b2e26..24c7fcfb00 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -7077,11 +7077,6 @@ Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. - - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - 'yield' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? - - No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. @@ -7092,6 +7087,11 @@ Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ReturnBangInsteadOfReturn.fs b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ReturnBangInsteadOfReturn.fs new file mode 100644 index 0000000000..fd18789e1b --- /dev/null +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/E_ReturnBangInsteadOfReturn.fs @@ -0,0 +1,13 @@ +// #ErrorMessages +//'return!' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + +namespace ApplicativeComputationExpressions + +module E_LetBangAndBangNotTerminatedWithReturn = + + let f x y = Eventually.Done (x + y) + eventually { + let! x = Eventually.NotYetDone (fun () -> Eventually.Done 4) + and! y = Eventually.Done 6 + return! f x y + } \ No newline at end of file diff --git a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst index 1b633f1577..7329283d0c 100644 --- a/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/ErrorMessages/ApplicativeComputationExpressions/env.lst @@ -7,4 +7,5 @@ SOURCE=E_MoreAfterReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_MoreAfterReturn.fs: Extra stuff after return SOURCE=E_PatternOnLhsOfUseBangAndUseBangBinding.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_PatternOnLhsOfUseBangAndUseBangBinding.fs: Pattern matching in a use!/anduse! binding SOURCE=E_YieldInsteadOfReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_YieldInsteadOfReturn.fs: yield instead of return + SOURCE=E_ReturnBangInsteadOfReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_ReturnBangInsteadOfReturn.fs: return! instead of return SOURCE=E_NoReturn.fs SCFLAGS="-r:builderlib.dll -g --optimize-" COMPILE_ONLY=1 PRECMD="\$FSC_PIPE --target:library builderlib.fs" # E_NoReturn.fs: No return after let! ... and! ... From 96f189b1ac281ca6cb26942a171e3de36bd068f1 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 15 Oct 2018 12:52:06 +0100 Subject: [PATCH 250/255] Capture Bind preceding Apply being accepted --- .../ApplicativeBuilderLib.fs | 9 +++++++ ...ngAndBangFollowingBindDesugarsCorrectly.fs | 27 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingBindDesugarsCorrectly.fs diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs index 5c721c25a9..e0a31bb02c 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/ApplicativeBuilderLib.fs @@ -8,6 +8,7 @@ type TraceOp = | StartUsingBody of resource : obj | EndUsingBody of resource : obj | ExitUsing of resource : obj + | Bind | Run | Delay @@ -78,3 +79,11 @@ type TraceWithRunBuilder() = member __.Run(x) = __.trace <- Run :: __.trace x + +type MonadicTraceBuilder() = + inherit TraceBuilder() + + member __.Bind(x : 'a Trace, f : 'a -> 'b Trace) : 'b Trace = + __.trace <- Bind :: __.trace + let (Trace x') = x + f x' diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingBindDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingBindDesugarsCorrectly.fs new file mode 100644 index 0000000000..bd10b9f220 --- /dev/null +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingBindDesugarsCorrectly.fs @@ -0,0 +1,27 @@ +// #Misc #AppCE + +//#Expects: Success +// << OUTPUT +//Trace 3, .Bind; Return; Apply; Apply. +//OUTPUT + +open ApplicativeBuilderLib + +module LetBangAndBangAfterLetBang = + + let () = + let tracer = TraceBuilder() + + let ceResult : int Trace = + tracer { + let foo = Trace "foo" + match! foo with + | "bar" -> + return 0 + | _ -> + let! x = Trace 3 + and! y = foo + return if y = "foo" then x else -1 + } + + printfn "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file From c90e5222e95312a5f37b99c158446105196247b6 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 15 Oct 2018 18:56:19 +0100 Subject: [PATCH 251/255] Tidy applicative-after-monad CE test --- .../LetBangAndBangDesugarsCorrectly.fs | 2 +- ...ollowingMonadicConstructsDesugarsCorrectly.fs} | 15 ++++++++------- .../ComputationExpressions/env.lst | 15 ++++++++------- 3 files changed, 17 insertions(+), 15 deletions(-) rename tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/{LetBangAndBangFollowingBindDesugarsCorrectly.fs => LetBangAndBangFollowingMonadicConstructsDesugarsCorrectly.fs} (52%) diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs index 8fa1f22e07..766a1e813e 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangDesugarsCorrectly.fs @@ -7,7 +7,7 @@ open ApplicativeBuilderLib -module LetBang = +module LetBangAndBang = let () = let tracer = TraceBuilder() diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingBindDesugarsCorrectly.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingMonadicConstructsDesugarsCorrectly.fs similarity index 52% rename from tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingBindDesugarsCorrectly.fs rename to tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingMonadicConstructsDesugarsCorrectly.fs index bd10b9f220..9e5635c3f1 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingBindDesugarsCorrectly.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/LetBangAndBangFollowingMonadicConstructsDesugarsCorrectly.fs @@ -7,21 +7,22 @@ open ApplicativeBuilderLib -module LetBangAndBangAfterLetBang = +module LetBangAndBangAfterMonadicOperations = let () = - let tracer = TraceBuilder() + let tracer = MonadicTraceBuilder() let ceResult : int Trace = tracer { - let foo = Trace "foo" - match! foo with + let fb = Trace "foobar" + match! fb with | "bar" -> - return 0 + let! bar = fb + return String.length bar | _ -> let! x = Trace 3 - and! y = foo - return if y = "foo" then x else -1 + and! y = Trace true + return if y then x else -1 } printfn "%+A, %+A" ceResult (tracer.GetTrace ()) \ No newline at end of file diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst index 1409b2ee47..473a66e012 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ComputationExpressions/env.lst @@ -23,13 +23,14 @@ SOURCE=MutateBuilders.fs # MutateBuilders.fs # Executing Workflows - SOURCE=RunAndDelay01.fs # RunAndDelay01.fs - SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangDesugarsCorrectly.fs: let! ... and! ... desugars correctly - SOURCE=LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs: let! ... and! ... (when the builder defines delay and run) desugars correctly - SOURCE=LetBangAndBangWithRunDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithRunDesugarsCorrectly.fs: let! ... and! ... (when the builder defines run) desugars correctly - SOURCE=LetBangAndBangWithDelayDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithDelayDesugarsCorrectly.fs: let! ... and! ... (when the builder defines delay) desugars correctly - SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # UseBangAndUseBangDesugarsCorrectly.fs: use! ... anduse! ... desugars correctly - SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # MixOfUseAndLetDesugarsCorrectly.fs: use! ... and! ... anduse! ... desugars correctly + SOURCE=RunAndDelay01.fs # RunAndDelay01.fs + SOURCE=LetBangAndBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangDesugarsCorrectly.fs: let! ... and! ... desugars correctly + SOURCE=LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithDelayAndRunDesugarsCorrectly.fs: let! ... and! ... (when the builder defines delay and run) desugars correctly + SOURCE=LetBangAndBangWithRunDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithRunDesugarsCorrectly.fs: let! ... and! ... (when the builder defines run) desugars correctly + SOURCE=LetBangAndBangWithDelayDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangWithDelayDesugarsCorrectly.fs: let! ... and! ... (when the builder defines delay) desugars correctly + SOURCE=UseBangAndUseBangDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # UseBangAndUseBangDesugarsCorrectly.fs: use! ... anduse! ... desugars correctly + SOURCE=MixOfUseAndLetDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # MixOfUseAndLetDesugarsCorrectly.fs: use! ... and! ... anduse! ... desugars correctly + SOURCE=LetBangAndBangFollowingMonadicConstructsDesugarsCorrectly.fs SCFLAGS="--nologo -r:ApplicativeBuilderLib.dll" PRECMD="\$FSC_PIPE -a ApplicativeBuilderLib.fs" # LetBangAndBangFollowingMonadicConstructsDesugarsCorrectly.fs: Monadic constructs can precede let! ... and! ... return ... # Misc ReqRetail SOURCE=Capacity01.fs # Capacity01.fs From 0796932434497010c62893c963961c02202bf7d6 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Fri, 19 Oct 2018 14:12:52 +0100 Subject: [PATCH 252/255] Specualtively reintroduce `let! ... and! ... error` case Goal is to make completion offer all valid names --- src/fsharp/pars.fsy | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 0136dcbb99..facbf3d772 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -3015,29 +3015,27 @@ binders: let m = unionRanges (rhs parseState 1) $8.Range SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4,m,$7,$8) } - /* 'let! ... in and! ... in ...' with no return */ + /* TODO Remove? | BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders %prec expr_let { let m = lhs parseState reportParseErrorAt m (FSComp.SR.parsNoBodyInApplicativeComputationExpression()) let spBind = SequencePointAtBinding(rhs2 parseState 1 5) SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4,m,$7, SynExpr.ImplicitZero m) } - /* 'let! ... and! ...' with no return */ | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders %prec expr_let { let m = lhs parseState reportParseErrorAt m (FSComp.SR.parsNoBodyInApplicativeComputationExpression()) $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4,m,$7, SynExpr.ImplicitZero m) } + */ - /* -- TODO: Reintroduce? | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders error %prec expr_let { // error recovery that allows intellisense when writing incomplete computation expressions let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range) let mAll = unionRanges (rhs parseState 1) (rhs parseState 7) // TODO Check this range let m = $4.Range.EndRange // zero-width range SynExpr.LetOrUseOrAndBang(spBind,($1 = "use"),true,$2,$4, mAll, $7, SynExpr.ImplicitZero m) } - */ | OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let // TODO Fix CompletionInDifferentEnvs3 { // error recovery that allows intellisense when writing incomplete computation expressions From 63a898d526e47ff8e1c3e9a23f1c0a0ee52a48c0 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 22 Oct 2018 15:27:21 +0100 Subject: [PATCH 253/255] Add missing autogen'd resources --- src/fsharp/xlf/FSComp.txt.cs.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.de.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.en.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.es.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.fr.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.it.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.ja.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.ko.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.pl.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.ru.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.tr.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 25 +++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 25 +++++++++++++++++++++++++ 14 files changed, 350 insertions(+) diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 1c840d2c66..f273d04633 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index cd09b539e4..d6d7dff1e4 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.en.xlf b/src/fsharp/xlf/FSComp.txt.en.xlf index 69e90b775b..8dfd76de91 100644 --- a/src/fsharp/xlf/FSComp.txt.en.xlf +++ b/src/fsharp/xlf/FSComp.txt.en.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index cab140a050..6112f736b8 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index 0ef363d31e..2b81904b0e 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 5c22444217..2788913a38 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 168cd7d6ef..c9e3a30b60 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index 3dd8fa1641..da1a81ee85 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index a2e4ad8a2b..4eb865fe56 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index b81572e3a7..63dc5ba753 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index e3b2d9f4b5..b451ed41b3 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index 9c9f740dcb..502af9690c 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 180d2f95b6..031d87cf97 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 6e0f7e50c2..35420dd369 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -7082,6 +7082,31 @@ Active patterns do not have fields. This syntax is invalid. + + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + Expecting 'and!', 'anduse!' or 'return' but saw something else. Applicative computation expressions must be of the form 'let! <pat1> = <expr2> and! <pat2> = <expr2> and! ... and! <patN> = <exprN> return <exprBody>'. + + + + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + Pattern matching is not allowed on the left-hand side of the equals. 'use! ... anduse! ...' bindings must be of the form 'use! <var> = <expr>' or 'anduse! <var> = <expr>'. + + + + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + '{0}' is not valid in this position in an applicative computation expression. Did you mean 'return' instead? + + + + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + No body given after the applicative bindings. Expected a 'return' to terminate this applicative computation expression. + + + + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + Saw unexpected expression sequenced after 'return'. Applicative computation expressions must be terminated with a single 'return'. + + \ No newline at end of file From bdfda97ed872df40c563764199cc515a7c34c2e6 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 12 Mar 2019 12:30:25 +0000 Subject: [PATCH 254/255] integrate master --- src/fsharp/service/ServiceLexing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index eda24f6a87..c64a9d83cf 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -259,7 +259,7 @@ module internal TokenClassifications = | MEMBER | STATIC | NAMESPACE | OASSERT | OLAZY | ODECLEND | OBLOCKSEP | OEND | OBLOCKBEGIN | ORIGHT_BLOCK_END | OBLOCKEND | OBLOCKEND_COMING_SOON | OBLOCKEND_IS_HERE | OTHEN | OELSE | OLET(_) - | OBINDER _ | OAND_BANG | BINDER _ | ODO | OWITH | OFUNCTION | OFUN | ORESET | ODUMMY _ | DO_BANG + | OBINDER _ | OAND_BANG _ | BINDER _ | ODO | OWITH | OFUNCTION | OFUN | ORESET | ODUMMY _ | DO_BANG | ODO_BANG | YIELD _ | YIELD_BANG _ | OINTERFACE_MEMBER | ELIF | RARROW | LARROW | SIG | STRUCT | UPCAST | DOWNCAST | NULL | RESERVED | MODULE | AND | AS | ASSERT | ASR From 0a95da57b711e7be4000760997e40e6b9fb674df Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 18 Oct 2019 16:27:23 +1100 Subject: [PATCH 255/255] merge master --- .../FSharp.Compiler.UnitTests.fsproj | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj index 57bd439c83..790fdc6431 100644 --- a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj +++ b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj @@ -10,21 +10,6 @@ nunit - - - True - $(NUnitLibDir)\nunit.framework.dll - - - - - - - - - ..\..\..\packages\System.ValueTuple.$(SystemValueTuplePackageVersion)\lib\netstandard1.0\System.ValueTuple.dll - -