-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using the pipe operator to assign the output of a pipe to a variable #541
Comments
While I like the idea in principle (especially the similarity to Bash where the pipeline gets written to a file at the end), I'm not a fan of any of the proposed syntax suggestions. They all have various drawbacks. For example, this one looks ugly to me: [0..10] |> Seq.min |> let minimum That doesn't read like English, whereas Whereas the proposal of making the let sshow n = sprintf "Result was %i" n // Note the typo
// ... more code here ...
[0..10] |> Seq.min |> show Currently, that would not compile, with the compiler complaining that "The value or constructor 'show' is not defined", and you'd probably catch the typo pretty quickly. But if the let sshow n = sprintf "Result was %i" n // Note the typo
// ... more code here ...
let show = [0..10] |> Seq.min And the compiler wouldn't end up informing you about your Another possibility would be to create a new operator for the "assign to a variable at the end of the pipeline" operation. But the question of "which operator to create?" is a thorny one. Anything you pick will probably overlap with custom operators that have been created by existing libraries; for example, Actually, there is one syntax that I can think of that I wouldn't be opposed to, which would be to use the [0..10] |> Seq.min |> as minimum
printf "Result was %i" minimum // Prints "Result was 0" There's still a disadvantage to this suggestion, which is that now there are two different keywords to define a variable, With the |
Another issue is conflicting names: let minimum x y = if x > y then y else x
...
[0..10] |> Seq.min |> minimum Because of partial application, it's perfectly valid to generate a function via a pipeline. Here's the FSI 4.1 output of doing this:
What should the behavior be here? Emit a warning that you're not getting the |
@cartermp : I also think that For the last example i think that it would be a problem. Since In terms of English language that flows from left to right the following: [0..10] |> Seq.min |> minimum Would be translated to:
let minimum = [0..10] |> Seq.min Would be translated to:
I think that the "pipe in to variable" reads more easily since the flow follows the english language which is Left to Right. It would also be possible to have a separate operator as suggested if that would be needed. Ex: [0..10] |> Seq.min |=> minimum or [0..10] |> Seq.min |= minimum All in all i think the best benefit of a change like this is that the whole language would be focused around the flow of data in pipes from left to right just like the English language. 1.80 |> length
72.0 |> weight
weight/length**2.0 |> printfn "bmi: %A" |
@PerArneng The let inline (|>) x f = f x All it means is apply the value on the left to the function on the right. Changing the meaning of this would definitely be a significant and confusing change. Implementing this suggestion would need new language syntax. Using I don't really see the benefit of this and I don't think it's intuitive. The bash example doesn't actually do the same thing as your suggestion. And I can see downsides: it makes it harder to see where a name was bound, or that an expression is there to bind a name (vs perform a side-effect). Also it adds a second way to do exactly the same thing as an existing construct in a language which is already quite big as it straddles FP and OOP. I think it would be hard for existing F# users to read code making use of this. An interesting idea, though. Got any more? :) |
For the cursory example given, a variable declaration is unnecessary anyway. [0..10] |> Seq.min |> printfn "minimum: %d" But you probably have more sophisticated cases in mind. In those cases, you could use [0..10]
|> Seq.min
|> fun min ->
printfn "minimum: %d" min
min + 1
|> fun minPlusOne ->
printfn "minPlusOne: %d" minPlusOne
minPlusOne I think there are multiple additional ways to do this within the existing language features, depending on the particular problem you're solving. |
Thanks for the suggestion. Realistically we aren't going to do the suggestion as originally stated in F# - the reasons why are covered well in the discussion above The suggestion of using
but I still feel that this just doesn't bring enough to justify adding multiple ways of doing the same thing |
Coming across this ticket when looking for a way to do similar... seeing it marked as "completed"... but reading the context, it's not clear to me anymore, was a syntax added or not for this? |
No, since it was closed before GitHub had two separate reasons when closing an issue, it defaults to "completed". |
The feature proposed in this issue will provide an obvious improvement in readability if a pleasing syntax can be found. Instead of this, which does not read left to right,
this would be much easier to read for the same effect
Regarding the objection to adding another way to do the same thing, [edited in response to the ‘confused’ reaction emojis] |
I propose we allow the output of a pipeline to be stored inside an existing mutable variable or create a new one. The natural flow of data is forward and it is just as natural to store the data inside a variable as it is to pass it in to a function.
The existing way of approaching this problem in F# is to declare the variable first and then let it consume the output of the pipleline by sending it from the end back to the declaration. The data flows forward and then jumps back to the declaration.
An alternative (additional) approach would be to allow the data to be piped in to the variable.
The variable
minimum
in the example above is created and the type is inferred by the type returned by themax
function. Ifminimum
already existed it should have been a mutable variable with the typeint
to compile correctly. When piping in to a variable the value is consumed by the variable and the resulting value of the whole pipeline isunit
.The code above would fail stating that you can not assign
unit
to anint
or something similar.A captured value can always be continued sent forward as show below.
If you want to specify a type on your variable for readability you can do so.
Side Notes
When creating this variable inside the pipeline i have omitted the
let
keyword on purpose since it can be implied by the construct. If you feel that it is needed it could also be added. Then an example would look like this.Other Languages
Similar behavior can be seen in
bash
when pipining the result of a pipeline in to a file.Pros and Cons
The advantages of making this adjustment to F# are that you get a consistent way of allowing data in a pipeline to naturally flow forward regardless of having a function or a variable as the target.
The disadvantages of making this adjustment to F# are that it is an alternative new way of looking at the result from a pipeline and that can cause confusion for people who are used to the old way of looking at the result of a pipeline. People who are not used to seeing this new pipeline might confuse the variable from being a function.
Extra Information
Estimated cost (XS, S, M, L, XL, XXL): (I don't have enough knowledge of the f# tools to give an estimate)
Related suggestions: N/A
Affadavit (must be submitted)
Please tick this by placing a cross in the box:
Please tick all that apply:
The text was updated successfully, but these errors were encountered: