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

Remove self binding in member declarations when not used: `member __.P = expr` > `member P = expr` #626

Open
alfonsogarciacaro opened this Issue Nov 24, 2017 · 29 comments

Comments

Projects
None yet
10 participants
@alfonsogarciacaro

alfonsogarciacaro commented Nov 24, 2017

I propose we make "this" binding optional in member declarations, so we get rid of the multiple __. (as in member __.Foo() =) in F# code. For reference see this comment from @vasily-kirichenko and the following four: #506 (comment)

Instead of:

g { new ICodeGen<ILCodeLabel> with
      member __.CodeLabel(m) = m
      member __.GenerateDelayMark() = generateCodeLabel()
      member __.GenLocal(ilty) = failwith "not needed"
      member __.SetMarkToHere(m) = lab2pc.[m] <- instrs.Count
      member __.EmitInstr x = instrs.Add x
      member cg.EmitInstrs xs = for i in xs do cg.EmitInstr i
      member __.MkInvalidCastExnNewobj () = failwith "not needed" }

We'd have:

g { new ICodeGen<ILCodeLabel> with
      member CodeLabel(m) = m
      member GenerateDelayMark() = generateCodeLabel()
      member GenLocal(ilty) = failwith "not needed"
      member SetMarkToHere(m) = lab2pc.[m] <- instrs.Count
      member EmitInstr x = instrs.Add x
      member cg.EmitInstrs xs = for i in xs do cg.EmitInstr i
      member MkInvalidCastExnNewobj () = failwith "not needed" }

NOTE: This is only to remove the need of typing __. when there are no self references in the member body. It's not about implicitly binding this, in order to be able to self reference you would still need to bind this or any other identifier. The reasoning for this is here.

Pros

  • Cleaner syntax, it will likely make member declaration more uniform across code bases
  • Avoid users making a decision in advance whether to bind "this" or not
  • Avoid IDE warning when "this" binding is declared but is not used and is not __
  • Backwards compatible

Cons

Concerns by @dsyme in a comment below and here.

  1. Marginally less regular syntax - some instance members are member x.Method, some are member Method.
  2. As a result it probably marginally disincentives the use of member x.Method bindings in F# methods - people might feel they are doing something wrong, when in fact it's pretty normal and reasonable to use member x.Method in members.
  3. Property can be declared as member x.P = expr. Some programmers - especially those from ML-family languages - would take a while to grok that expr was evaluated once on each property use. We assessed that confusion on this point would be more likely with syntax member P = expr.

Note by me: For 1) if IDEs remove the current exception for __. for the "Not used value" (which afaik is only a convention), hopefully there would be a transition period and then people will just get used to delete __. or x. when not used to remove the warning. About 3) properties in .NET are very convenient but make it easier to forget the expression is evaluated in every use, no matter if it's C# or F#, I think this is a tradeoff programmers need to be aware of.

Extra information

Estimated cost (XS, S, M, L, XL, XXL): I guess it will affect mainly parsing and IDE tooling but I'm not sure how difficult to support the new syntax

Affidavit (please submit!)

Please tick this by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate
  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

Please tick all that apply:

  • This is not a breaking change to the F# language design
  • I or my company would be willing to help implement and/or test this
@vasily-kirichenko

This comment has been minimized.

Show comment
Hide comment
@vasily-kirichenko

vasily-kirichenko Nov 24, 2017

I'm pretty sure it's easy to implement and tooling would work automatically (except the range of implicit this, which could be just pointed to the class name at declaration site).

vasily-kirichenko commented Nov 24, 2017

I'm pretty sure it's easy to implement and tooling would work automatically (except the range of implicit this, which could be just pointed to the class name at declaration site).

@dsyme

This comment has been minimized.

Show comment
Hide comment
@dsyme

dsyme Nov 24, 2017

Collaborator

@alfonsogarciacaro I adjusted your code sample to match the title of the suggestion

That is I believe you are proposing to allow member M args = expr for member __.M args = expr

As far as I can tell, you are not proposing to make the binding of this implicit

See also my comments here #506 (comment), Also that this does come under the "decided in F# 1.x" rule per my comment there. But I'll leave the suggestion open because it does feel worth gathering opinions about this.

Collaborator

dsyme commented Nov 24, 2017

@alfonsogarciacaro I adjusted your code sample to match the title of the suggestion

That is I believe you are proposing to allow member M args = expr for member __.M args = expr

As far as I can tell, you are not proposing to make the binding of this implicit

See also my comments here #506 (comment), Also that this does come under the "decided in F# 1.x" rule per my comment there. But I'll leave the suggestion open because it does feel worth gathering opinions about this.

@dsyme

This comment has been minimized.

Show comment
Hide comment
@dsyme

dsyme Nov 24, 2017

Collaborator

You could also add a con:

  • Marginally less regular syntax - some instance members are member x.Method, some are member Method.
  • As a result it probably marginally disincentives the use of member x.Method bindings in F# methods - people might feel they are doing something wrong, when in fact it's pretty normal and reasonable to use member x.Method in members.
Collaborator

dsyme commented Nov 24, 2017

You could also add a con:

  • Marginally less regular syntax - some instance members are member x.Method, some are member Method.
  • As a result it probably marginally disincentives the use of member x.Method bindings in F# methods - people might feel they are doing something wrong, when in fact it's pretty normal and reasonable to use member x.Method in members.
@realvictorprm

This comment has been minimized.

Show comment
Hide comment
@realvictorprm

realvictorprm Nov 24, 2017

Member

Maybe it's considerable to add a warning for each usage of __.name args style then.

Member

realvictorprm commented Nov 24, 2017

Maybe it's considerable to add a warning for each usage of __.name args style then.

@dsyme

This comment has been minimized.

Show comment
Hide comment
@dsyme

dsyme Nov 24, 2017

Collaborator

Maybe it's considerable to add a warning for each usage of __.name args style then.

@realvictorprm yes, that would be a natural consequence

Collaborator

dsyme commented Nov 24, 2017

Maybe it's considerable to add a warning for each usage of __.name args style then.

@realvictorprm yes, that would be a natural consequence

@eugbaranov

This comment has been minimized.

Show comment
Hide comment
@eugbaranov

eugbaranov Nov 24, 2017

I haven't realized you can use double underscore.
When I first seen warning about unused x in member definition my first instinct was to use single underscore, similar to pattern matching when we don't care about the value itself.
Just curious what is the reason for such inconsistency?

eugbaranov commented Nov 24, 2017

I haven't realized you can use double underscore.
When I first seen warning about unused x in member definition my first instinct was to use single underscore, similar to pattern matching when we don't care about the value itself.
Just curious what is the reason for such inconsistency?

@alfonsogarciacaro alfonsogarciacaro changed the title from Make "this" binding optional in member declarations to Remove self binding in member declarations when not used: `member __.P = expr` Nov 24, 2017

@alfonsogarciacaro alfonsogarciacaro changed the title from Remove self binding in member declarations when not used: `member __.P = expr` to Remove self binding in member declarations when not used: `member __.P = expr` > `member P = expr` Nov 24, 2017

@alfonsogarciacaro

This comment has been minimized.

Show comment
Hide comment
@alfonsogarciacaro

alfonsogarciacaro Nov 24, 2017

@dsyme That's right, thanks for your comments. I edited the title, put a note and added your comments to make it clearer 👍

alfonsogarciacaro commented Nov 24, 2017

@dsyme That's right, thanks for your comments. I edited the title, put a note and added your comments to make it clearer 👍

@cartermp

This comment has been minimized.

Show comment
Hide comment
@cartermp

cartermp Nov 24, 2017

Member

@Regent __ is a convention. A single underscore that prefixes the identifier (e.g., _x.Foo) is also valid syntax, but a single underscore as the identifier itself is not. This is part of the issue with member declaration today. Tt's not entirely clear what code to write when you have no need for the identifier.

Member

cartermp commented Nov 24, 2017

@Regent __ is a convention. A single underscore that prefixes the identifier (e.g., _x.Foo) is also valid syntax, but a single underscore as the identifier itself is not. This is part of the issue with member declaration today. Tt's not entirely clear what code to write when you have no need for the identifier.

@eugbaranov

This comment has been minimized.

Show comment
Hide comment
@eugbaranov

eugbaranov Nov 24, 2017

Can we allow single underscore in instance member definition and treat it same way we would in e.g. fun _ -> 1?

eugbaranov commented Nov 24, 2017

Can we allow single underscore in instance member definition and treat it same way we would in e.g. fun _ -> 1?

@rmunn

This comment has been minimized.

Show comment
Hide comment
@rmunn

rmunn Nov 28, 2017

Yes, allowing the single underscore in member declarations (e.g., member _.Foo = 1) is the best option here by far. It keeps the existing meaning of the single underscore: it says "I'm not going to use the identifier that normally appears in this position." And, in fact, I actually find it surprising that the single underscore is not legal in member declarations: I would have expected it to be legal there, since it's legal in pattern matching and function declarations (which are, of course, special cases of pattern matching themselves).

@alfonsogarciacaro - Would you mind editing the suggestion to include allowing the single-underscore in member declarations? Or do you feel like that's a separate idea that should be tracked in a different issue?

rmunn commented Nov 28, 2017

Yes, allowing the single underscore in member declarations (e.g., member _.Foo = 1) is the best option here by far. It keeps the existing meaning of the single underscore: it says "I'm not going to use the identifier that normally appears in this position." And, in fact, I actually find it surprising that the single underscore is not legal in member declarations: I would have expected it to be legal there, since it's legal in pattern matching and function declarations (which are, of course, special cases of pattern matching themselves).

@alfonsogarciacaro - Would you mind editing the suggestion to include allowing the single-underscore in member declarations? Or do you feel like that's a separate idea that should be tracked in a different issue?

@vasily-kirichenko

This comment has been minimized.

Show comment
Hide comment
@vasily-kirichenko

vasily-kirichenko Nov 28, 2017

OMG, we don't need either explicit this nor underscores. member Foo and static member Foo is completely unambiguous and clear. I'd like to know who the hell suggested this awful idea at the first place.

vasily-kirichenko commented Nov 28, 2017

OMG, we don't need either explicit this nor underscores. member Foo and static member Foo is completely unambiguous and clear. I'd like to know who the hell suggested this awful idea at the first place.

@cartermp

This comment has been minimized.

Show comment
Hide comment
@cartermp

cartermp Nov 28, 2017

Member

I don't think the work to enable a single underscore is really all that worth it. It's no less confusing than any other identifier to me. I remember being horribly confused about this when I first learned F#. It's embarrassing, but I still sometimes forget to type the <ident>.<member-name> construct properly when I'm defining new members. It just doesn't feel natural at all.

@dsyme made this point in another issue:

Whether having visual clues for which members of an object make recursive calls or not is important is best a matter of separate discussion

I think that's worth discussing here. I don't think it's important. Consider the following:

type Foo =
    let DoIt x = printfn "I think %A should do it." x

    member DoIt x = printfn "%A did it!" x
    member DoItToAll xs = for x in xs do DoIt x

This would be have to be a compiler error in the second member saying that DoIt is ambiguous in this use. Assuming no implicit this, you wouldn't be allowed to disambiguate by qualifying the call. You'd have to rename either the DoIt function or the DoIt member. I submit to the court that this restriction is perfectly fine.

Member

cartermp commented Nov 28, 2017

I don't think the work to enable a single underscore is really all that worth it. It's no less confusing than any other identifier to me. I remember being horribly confused about this when I first learned F#. It's embarrassing, but I still sometimes forget to type the <ident>.<member-name> construct properly when I'm defining new members. It just doesn't feel natural at all.

@dsyme made this point in another issue:

Whether having visual clues for which members of an object make recursive calls or not is important is best a matter of separate discussion

I think that's worth discussing here. I don't think it's important. Consider the following:

type Foo =
    let DoIt x = printfn "I think %A should do it." x

    member DoIt x = printfn "%A did it!" x
    member DoItToAll xs = for x in xs do DoIt x

This would be have to be a compiler error in the second member saying that DoIt is ambiguous in this use. Assuming no implicit this, you wouldn't be allowed to disambiguate by qualifying the call. You'd have to rename either the DoIt function or the DoIt member. I submit to the court that this restriction is perfectly fine.

@vasily-kirichenko

This comment has been minimized.

Show comment
Hide comment
@vasily-kirichenko

vasily-kirichenko Nov 28, 2017

this is implicit, not hidden, so

member DoItToAll xs = for x in xs do this.DoIt x

vasily-kirichenko commented Nov 28, 2017

this is implicit, not hidden, so

member DoItToAll xs = for x in xs do this.DoIt x
@cartermp

This comment has been minimized.

Show comment
Hide comment
@cartermp

cartermp Nov 28, 2017

Member

@vasily-kirichenko My bit at the bottom was written under the assumption of no implicit this, as per Don's comment and the body of the suggestion. Though I do agree that an implicit this is fine for object programming, I'm ruling it out as an option given the reasoning in the other thread.

Member

cartermp commented Nov 28, 2017

@vasily-kirichenko My bit at the bottom was written under the assumption of no implicit this, as per Don's comment and the body of the suggestion. Though I do agree that an implicit this is fine for object programming, I'm ruling it out as an option given the reasoning in the other thread.

@alfonsogarciacaro

This comment has been minimized.

Show comment
Hide comment
@alfonsogarciacaro

alfonsogarciacaro Nov 28, 2017

@rmunn Although on one hand I agree that a single underscore would be an improvement over the current situation, on the other I'm with @cartermp in that it's just not enough to justify the spec change. Actually, I'm more in the line of @vasily-kirichenko in that I would like to see this implicitly bound so you don't have to write nothing at all between member keyword and the name. The only reason is I didn't add that to the suggestion is I thought that would make it more likely to go through.

After several years of writing F# the problem has faded away a bit, but I must say the class declaration syntax was by far the most difficult part to learn and I always had to look it up many times. And that feeling seems to be shared by many other programmers: @vasily-kirichenko, @cartermp, @enricosada... and I'm gonna ping also people starting F# recently with Fable: @MangelMaxime, @whitetigle. Talking about Fable, object expressions for example would be really useful when interacting with JS, but I don't use them much in the examples because the syntax is very cluttered and it feels like a huge step back from JS object literals.

One of the main goals of most modern languages is to bring the benefits of functional programming in a way that is more appealing to programmers of the C-family of languages and it makes sense we follow suit. In conclusion, I would personally be more inclined to edit the suggestion to ask for implicit binding than reducing it to allowing a single underscore.

alfonsogarciacaro commented Nov 28, 2017

@rmunn Although on one hand I agree that a single underscore would be an improvement over the current situation, on the other I'm with @cartermp in that it's just not enough to justify the spec change. Actually, I'm more in the line of @vasily-kirichenko in that I would like to see this implicitly bound so you don't have to write nothing at all between member keyword and the name. The only reason is I didn't add that to the suggestion is I thought that would make it more likely to go through.

After several years of writing F# the problem has faded away a bit, but I must say the class declaration syntax was by far the most difficult part to learn and I always had to look it up many times. And that feeling seems to be shared by many other programmers: @vasily-kirichenko, @cartermp, @enricosada... and I'm gonna ping also people starting F# recently with Fable: @MangelMaxime, @whitetigle. Talking about Fable, object expressions for example would be really useful when interacting with JS, but I don't use them much in the examples because the syntax is very cluttered and it feels like a huge step back from JS object literals.

One of the main goals of most modern languages is to bring the benefits of functional programming in a way that is more appealing to programmers of the C-family of languages and it makes sense we follow suit. In conclusion, I would personally be more inclined to edit the suggestion to ask for implicit binding than reducing it to allowing a single underscore.

@dsyme

This comment has been minimized.

Show comment
Hide comment
@dsyme

dsyme Nov 28, 2017

Collaborator

@Regent There is an open approved ticket to allow a single underscore, #333. It should not be hard to implement.

Collaborator

dsyme commented Nov 28, 2017

@Regent There is an open approved ticket to allow a single underscore, #333. It should not be hard to implement.

@dsyme

This comment has been minimized.

Show comment
Hide comment
@dsyme

dsyme Nov 28, 2017

Collaborator

For reference, OCaml always binds the self identifier explicitly (though not on member declarations), e.g. see https://caml.inria.fr/pub/docs/manual-ocaml/objectexamples.html#sec26.

Collaborator

dsyme commented Nov 28, 2017

For reference, OCaml always binds the self identifier explicitly (though not on member declarations), e.g. see https://caml.inria.fr/pub/docs/manual-ocaml/objectexamples.html#sec26.

@vasily-kirichenko

This comment has been minimized.

Show comment
Hide comment
@vasily-kirichenko

vasily-kirichenko Nov 28, 2017

Yes, but in OCaml self is a single declaration, it's not repeated in every method.

vasily-kirichenko commented Nov 28, 2017

Yes, but in OCaml self is a single declaration, it's not repeated in every method.

@gsomix

This comment has been minimized.

Show comment
Hide comment
@gsomix

gsomix Nov 28, 2017

In ReasonML this is implicitly binded.

As you can see, a Reason object can also access this. Just like a JavaScript object's this, our this has very erratic behavior depending on the context. Just kidding. Our this always points to the object itself correctly. Gotta learn from history.

https://reasonml.github.io/guide/language/object

gsomix commented Nov 28, 2017

In ReasonML this is implicitly binded.

As you can see, a Reason object can also access this. Just like a JavaScript object's this, our this has very erratic behavior depending on the context. Just kidding. Our this always points to the object itself correctly. Gotta learn from history.

https://reasonml.github.io/guide/language/object

@vasily-kirichenko

This comment has been minimized.

Show comment
Hide comment
@vasily-kirichenko

vasily-kirichenko Nov 28, 2017

@gsomix yes, this is the best approach: bind this implicitly, but require to use it to access members.

vasily-kirichenko commented Nov 28, 2017

@gsomix yes, this is the best approach: bind this implicitly, but require to use it to access members.

@dsyme

This comment has been minimized.

Show comment
Hide comment
@dsyme

dsyme Nov 29, 2017

Collaborator

yes, this is the best approach: bind this implicitly, but require to use it to access members.

You can make a separate fslang-suggestion on this if you wish. The reasons for explicitly binding this are explained here and you'd have to explicitly address those issues from a soundness/performance/readability/succinctness/design-principle point of view. Please also note the don't revisit decisions unless there has been a change of circumstance policy :)

Collaborator

dsyme commented Nov 29, 2017

yes, this is the best approach: bind this implicitly, but require to use it to access members.

You can make a separate fslang-suggestion on this if you wish. The reasons for explicitly binding this are explained here and you'd have to explicitly address those issues from a soundness/performance/readability/succinctness/design-principle point of view. Please also note the don't revisit decisions unless there has been a change of circumstance policy :)

@vasily-kirichenko

This comment has been minimized.

Show comment
Hide comment
@vasily-kirichenko

vasily-kirichenko commented Nov 29, 2017

:)

@dsyme

This comment has been minimized.

Show comment
Hide comment
@dsyme

dsyme Nov 29, 2017

Collaborator

Whether having visual clues for which members of an object make recursive calls or not is important is best a matter of separate discussion

I think that's worth discussing here. I don't think it's important. Consider the following...

@cartermp I don't really follow the example. In F#, you spot mutually-referential code by looking for

  • let rec
  • member this.Method() = ... this ...

and much less commonly:

  • module rec and namespace rec (for large recursive scopes in F# 4.x)
  • type C() as this (for mutually referential access in the implicit constructor, with a potential runtime check)
  • type C() + mutually referential uses of static and extension members

If this is implicitly bound then you also have to look in all method expression bodies for this.Method(). There will be a lot of such expressions. While this may not appear a dramatic change, it will actually have a significant effect on the feel of doing object-programming with F# - basically you will get more spaghetti objects (and you certainly won't get fewer).

Now, I'll admit I do have a strong bias against spaghetti objects. For example consider this code:

type Service() = 
    member private __.HelperCode(args) = expr
    member service.M() = ..service.HelperCode(args)...

In F# 4.x there is a strong incentive to change this code as follows

type Service() = 
    let helperCode(args) = expr
    member __.M() = ...helperCode(args)...

Only OO-diehards would use member private unless there is some other reason. Note that the second is less "object-oriented" and more "simple" - in particular

  • there is manifestly no use of self-referentiality
  • the overall constructs used are simpler
  • it is easy to apply refactorings, e.g. factor out helperCode to a separate set of helper functions if appropriate

You can tell that without even looking at the implementations of the methods. The second is "better" in the transcendental moral hierarchy of F# code betterness.

With an implicit this, there is very little incentive to change code in the direction of betterness. The programmer begins with

type Service() = 
    ...
    member private HelperCode(args) = expr
    member M() = ....this.HelperCode(args)...

and the programmer says "the language has encouraged me to write this, why would I ever change it?" There is no incentive to improve the code in the direction indicated. The code is needlessly using self-referentiality, but there is absolutely no penalty for it, and there is no disincentive to increase the amount of self-referentiality without end (e.g. there is no incentive to avoid using this in HelperCode).

This is fundamental to why OCaml and F# both bind this explicitly (and also why we require this.Foo not Foo): pervasive, implicit, zero-cost recursively referential object programming puts you on a trajectory towards spaghetti objects. There are plenty of cases of spaghetti-ish F# code, but like let mutable the aim is to put you on a gentle trajectory towards improvement by making you pay small costs for problematic complexificiation.

There may be other ways to disincentivize both mutation and spagghetti objects (e.g. put a "complexity metric" in a tooltip, colorize mutating code differently etc.). But in the F# language design these small disincentivizations are built into the language design.

Collaborator

dsyme commented Nov 29, 2017

Whether having visual clues for which members of an object make recursive calls or not is important is best a matter of separate discussion

I think that's worth discussing here. I don't think it's important. Consider the following...

@cartermp I don't really follow the example. In F#, you spot mutually-referential code by looking for

  • let rec
  • member this.Method() = ... this ...

and much less commonly:

  • module rec and namespace rec (for large recursive scopes in F# 4.x)
  • type C() as this (for mutually referential access in the implicit constructor, with a potential runtime check)
  • type C() + mutually referential uses of static and extension members

If this is implicitly bound then you also have to look in all method expression bodies for this.Method(). There will be a lot of such expressions. While this may not appear a dramatic change, it will actually have a significant effect on the feel of doing object-programming with F# - basically you will get more spaghetti objects (and you certainly won't get fewer).

Now, I'll admit I do have a strong bias against spaghetti objects. For example consider this code:

type Service() = 
    member private __.HelperCode(args) = expr
    member service.M() = ..service.HelperCode(args)...

In F# 4.x there is a strong incentive to change this code as follows

type Service() = 
    let helperCode(args) = expr
    member __.M() = ...helperCode(args)...

Only OO-diehards would use member private unless there is some other reason. Note that the second is less "object-oriented" and more "simple" - in particular

  • there is manifestly no use of self-referentiality
  • the overall constructs used are simpler
  • it is easy to apply refactorings, e.g. factor out helperCode to a separate set of helper functions if appropriate

You can tell that without even looking at the implementations of the methods. The second is "better" in the transcendental moral hierarchy of F# code betterness.

With an implicit this, there is very little incentive to change code in the direction of betterness. The programmer begins with

type Service() = 
    ...
    member private HelperCode(args) = expr
    member M() = ....this.HelperCode(args)...

and the programmer says "the language has encouraged me to write this, why would I ever change it?" There is no incentive to improve the code in the direction indicated. The code is needlessly using self-referentiality, but there is absolutely no penalty for it, and there is no disincentive to increase the amount of self-referentiality without end (e.g. there is no incentive to avoid using this in HelperCode).

This is fundamental to why OCaml and F# both bind this explicitly (and also why we require this.Foo not Foo): pervasive, implicit, zero-cost recursively referential object programming puts you on a trajectory towards spaghetti objects. There are plenty of cases of spaghetti-ish F# code, but like let mutable the aim is to put you on a gentle trajectory towards improvement by making you pay small costs for problematic complexificiation.

There may be other ways to disincentivize both mutation and spagghetti objects (e.g. put a "complexity metric" in a tooltip, colorize mutating code differently etc.). But in the F# language design these small disincentivizations are built into the language design.

@MangelMaxime

This comment has been minimized.

Show comment
Hide comment
@MangelMaxime

MangelMaxime Nov 29, 2017

As @alfonsogarciacaro asked my opinion here it is.

I used several times the OO possibilities of F# via Fable project and I do prefer the explicit version.

I am also a huge fan of this kind of code show by @dsyme because is easy to understand the different scope and what variable is declared where.

type Service() = 
    let helperCode(args) = expr
    member __.M() = ...helperCode(args)...

And for me having this implicitly bound is much harder to read and reason about.

MangelMaxime commented Nov 29, 2017

As @alfonsogarciacaro asked my opinion here it is.

I used several times the OO possibilities of F# via Fable project and I do prefer the explicit version.

I am also a huge fan of this kind of code show by @dsyme because is easy to understand the different scope and what variable is declared where.

type Service() = 
    let helperCode(args) = expr
    member __.M() = ...helperCode(args)...

And for me having this implicitly bound is much harder to read and reason about.

@alfonsogarciacaro

This comment has been minimized.

Show comment
Hide comment
@alfonsogarciacaro

alfonsogarciacaro Nov 29, 2017

Thanks a lot for the explanations @dsyme and @MangelMaxime. I'm still inclined to implicit this binding (or at least to some way to make the identifier consistent across code bases) but I understand the reasoning and I do agree it's good to keep the F# guideline that recursiveness must be explicit.

I'm also happy to see your arguments favor this proposal: if we can remove __. when not necessary the programmer has an incentive to not use self-references. In particular, as commented above, I think this would improve object expression declarations when used to pass a function dictionary.

That said, given there's already an open ticket for allowing the single underscore, if most of people think that's enough, I can close this suggestion in favor of that one.

alfonsogarciacaro commented Nov 29, 2017

Thanks a lot for the explanations @dsyme and @MangelMaxime. I'm still inclined to implicit this binding (or at least to some way to make the identifier consistent across code bases) but I understand the reasoning and I do agree it's good to keep the F# guideline that recursiveness must be explicit.

I'm also happy to see your arguments favor this proposal: if we can remove __. when not necessary the programmer has an incentive to not use self-references. In particular, as commented above, I think this would improve object expression declarations when used to pass a function dictionary.

That said, given there's already an open ticket for allowing the single underscore, if most of people think that's enough, I can close this suggestion in favor of that one.

@ReedCopsey

This comment has been minimized.

Show comment
Hide comment
@ReedCopsey

ReedCopsey Nov 29, 2017

@alfonsogarciacaro I wouldn't remove this issue. Not having an implicit this doesn't make your suggestion invalid - I would still love to be able to leave of __. when there is no self references.

I would prefer this vs changing to single underscore. I use the OO capabilities in F# frequently, and there is definitely extra "cruft" - easing the noise, especially if we can do it in a way that doesn't introduce disadvantages, would be very nice.

ReedCopsey commented Nov 29, 2017

@alfonsogarciacaro I wouldn't remove this issue. Not having an implicit this doesn't make your suggestion invalid - I would still love to be able to leave of __. when there is no self references.

I would prefer this vs changing to single underscore. I use the OO capabilities in F# frequently, and there is definitely extra "cruft" - easing the noise, especially if we can do it in a way that doesn't introduce disadvantages, would be very nice.

@dsyme

This comment has been minimized.

Show comment
Hide comment
@dsyme

dsyme Nov 29, 2017

Collaborator

I would prefer this vs changing to single underscore.

@ReedCopsey I consider these orthogonal, though allowing single underscore is already approved (if/when it is implemented...)

Collaborator

dsyme commented Nov 29, 2017

I would prefer this vs changing to single underscore.

@ReedCopsey I consider these orthogonal, though allowing single underscore is already approved (if/when it is implemented...)

@vasily-kirichenko

This comment has been minimized.

Show comment
Hide comment
@vasily-kirichenko

vasily-kirichenko Nov 29, 2017

I love the explicit way Microsoft avoid improving the language based on the users feedback 👍 "If you really need a feature (Microsoft itself do not, never), go implement it"

vasily-kirichenko commented Nov 29, 2017

I love the explicit way Microsoft avoid improving the language based on the users feedback 👍 "If you really need a feature (Microsoft itself do not, never), go implement it"

@dsyme

This comment has been minimized.

Show comment
Hide comment
@dsyme

dsyme Nov 29, 2017

Collaborator

@vasily-kirichenko It is wrong of you to interpret any of my comments in that way.

The comment was made in an FSSF repository in my role as F# language designer, and not from a Microsoft-business-priority angle. I usually mention Microsoft when explicitly calling out a Microsoft-specific perspective, e.g. I've tried to be more careful to highlight Microsoft-specific perspectives in the tooling RFCs

When I started the F# design process (as both a Microsoft employee and FSSF-approved F# language designer) we honestly had no idea how much co-contribution we would get on the implementation side. It would have been silly to say "ok, the community does it all", and equally foolish to say "We only approve what Microsoft is willing to implement" when so many people are obviously keen to contribute.

I believe that splitting the design process to be a neutral, community-oriented activity is correct and by far the best approach for F#. Every approved item is something I believe should go into F# and is something I personally would implement given infinite time. However I have no shame in saying that, in some areas, the F# language design process runs runs ahead of any specific ability to prioritize implementations. I also have no shame with leaving some low-hanging fruit items for people to start with. As a result, I will of course regularly point out opportunities for people to contribute to the implementation process. Or would you prefer I didn't regularly open the door for contributing?

Given that, the only way you can find out what people will/won't contribute is by trying. For example, it turns out that the we together successfully implemented the majority of the additions to FSharp.Core in F# 4.x - contributors from Microsoft did maybe only 10% of the work. That really surprised me - and I learned a lesson: well-specified library additions to FSharp.Core could be rapidly developed, especially for collection functions. The limiting factor for that kind of work is working out new things to add , not how fast we can implement them.

For language additions it has been different, and I've been learning a lot.

  • There have been many additions from the community, but they are much more sporadic and hard to land
  • People seem to appreciate that language design and implementation is separated out as a community-oriented activity, but that leads to a mismatch

"If you really need a feature (Microsoft itself do not, never), go implement it"

This is an incorrect summary of what I wrote (and definitely not a quote, so I'm not sure why it is in quotation marks :) )

Collaborator

dsyme commented Nov 29, 2017

@vasily-kirichenko It is wrong of you to interpret any of my comments in that way.

The comment was made in an FSSF repository in my role as F# language designer, and not from a Microsoft-business-priority angle. I usually mention Microsoft when explicitly calling out a Microsoft-specific perspective, e.g. I've tried to be more careful to highlight Microsoft-specific perspectives in the tooling RFCs

When I started the F# design process (as both a Microsoft employee and FSSF-approved F# language designer) we honestly had no idea how much co-contribution we would get on the implementation side. It would have been silly to say "ok, the community does it all", and equally foolish to say "We only approve what Microsoft is willing to implement" when so many people are obviously keen to contribute.

I believe that splitting the design process to be a neutral, community-oriented activity is correct and by far the best approach for F#. Every approved item is something I believe should go into F# and is something I personally would implement given infinite time. However I have no shame in saying that, in some areas, the F# language design process runs runs ahead of any specific ability to prioritize implementations. I also have no shame with leaving some low-hanging fruit items for people to start with. As a result, I will of course regularly point out opportunities for people to contribute to the implementation process. Or would you prefer I didn't regularly open the door for contributing?

Given that, the only way you can find out what people will/won't contribute is by trying. For example, it turns out that the we together successfully implemented the majority of the additions to FSharp.Core in F# 4.x - contributors from Microsoft did maybe only 10% of the work. That really surprised me - and I learned a lesson: well-specified library additions to FSharp.Core could be rapidly developed, especially for collection functions. The limiting factor for that kind of work is working out new things to add , not how fast we can implement them.

For language additions it has been different, and I've been learning a lot.

  • There have been many additions from the community, but they are much more sporadic and hard to land
  • People seem to appreciate that language design and implementation is separated out as a community-oriented activity, but that leads to a mismatch

"If you really need a feature (Microsoft itself do not, never), go implement it"

This is an incorrect summary of what I wrote (and definitely not a quote, so I'm not sure why it is in quotation marks :) )

@ErikSchierboom ErikSchierboom referenced this issue Dec 11, 2017

Merged

Cleanup #461

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment