Closed wastaz closed 1 year ago
Having tooling that works with currying would be nice rather than only tupled syntax.
The issue I see with the auto-convert module thing is what do you do about overloads? Even in that example, String.Split has overloads.
I'm really fond of the _.Property almost as a way to make property access nearly first class. It seems like a halfway solution though as this will only help with access and not updating records.
Maybe something like?
{ _ with x = newVal }
How might this interact with pattern matching? It would be ideal to do something like:
match x with
| _.Foo.Bar.Baz as baz -> ...
Also, what about passing them in as active pattern parameters?
Just spit-balling here so don't take my syntax too seriously. I think though that if we're going to add something lens-like to the language we should consider these other use cases as well.
@Rickasaurus I was just thinking that it could add a numeric let split s inst = ; let split2
or let split ...; let splitCharArray
if you like it more verbose. The obstacle for me when I program F# is that I have to rewrap all instance methods in modules for every project so I can use point-free programming – but more importantly; use currying. My suggestion is not more verbose if you assign _
as the module name either, so VeryLongTypeName
would not come into play.
@haf If this passing types into type provider thing works out then you could solve the problem that way. This would allow a lot of different experimentation for such a feature. I think this is a really hard problem to solve without a bunch of iteration.
We should try to get completely away from having to 'dot into' objects at any point.
@varon Yes, @vasily-kirichenko is correct in this comment - this is a misunderstanding of the relationship between F# and various aspects of notation associated with object programming. F# embraces appropriate use of dot notation (though that's by no means the same thing as embracing or emphasizing all aspects of object-orientation). That embrace brings some dissonance, and this suggestion is really about resolving one remaining part of that dissonance.
@dsyme Thanks for the clarification.
I'm genuinely surprised to hear that dot notation is still considered something to be embraced. While I'll totally defer to your expertise here, I'm really curious as to what the reasoning was behind this, and specifically why In general, in F#, the use of object-oriented programming is preferred as a software engineering device.
Where do you see F# as a language in a few years? Where does it aim to position itself? C# with ML syntax? Haskell.net? Something else?
I'm genuinely surprised to hear that dot notation is still considered something to be embraced.
@varon Pretty much every F# book and F# sample introduces the use of the dot notation and type-directed name resolution, and always has done since F# 0.1. For one view, see Expert F# 4.0 ch 6.
It would clearly be odd to force the use of DateTime.getYear date
for date.Year
- 21 characters instead of 9 - and likewise for thousands and thousands of other properties in decently designed .NET types. DateTime has problems, but notation is not particularly one of them. And it is extremely hard to argue that a DateTime API that doesn't use properties is a better design according to any specific utilitarian metric. The question in F# coding is not whether dot notation should be used, but how much. The component guidelines give one perspective on where to draw the line on that.
@dsyme Thanks for the detailed answer.
I really like the suggestion made here #506.
.bar or (.bar) (or whatever syntax) be precisely shorthand for (fun (x: ^T) -> (^T : (member bar : 'U) x), so let inline f xs = List.map (.bar) xs would get a generalized generic type.
Once I read that, I'm beginning to be more in favor of the original suggestion as long as we can have the mentioned generalization. :)
@TIHan Yes, it's a very simple addition
Simple is good, one step at a time.
On 25 Nov 2016 12:04, "Don Syme" notifications@github.com wrote:
@TIHan https://github.com/TIHan Yes, it's a very simple addition
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/fsharp/fslang-suggestions/issues/506#issuecomment-262942822, or mute the thread https://github.com/notifications/unsubscribe-auth/AAj7yr3tqyKIpFY-do9VeYdu50F3NtS_ks5rBs7kgaJpZM4KoICL .
The List.map (.bar) xs
example is nice looking but I worry that it might not work very well with the type inference. Currently even explicit record access often gets mucked up. A good example of this ambiguity would be something like List.map (.Length)
. Where would you put the type annotation?
List.map (.Length: 'a ->int)
same as
let inline length rekt = (^T : (member Length : int) rekt)
let inline fn ls = List.map (length:'a->int) ls
or just
let inline fn ls : int list = List.map length ls
let inline fn ls = List.map length ls : int list
make it generic for fun
let inline length rekt = (^T : (member Length : 'r) rekt)
let inline fn ls = List.map length ls : 'b list
I don't think there's much ambiguity, the type with either be inferred from the value you pass to it, or it's in an inline function so it'll be determined at site, or you explicitly label the type in one of the several places that are available.
List.map (.Length: 'a ->int)
Hrm, yeah. That's not bad at all.
Adjusted the title to match what feels like the most likely versions of this proposal
@dsyme could this suggestions also include other member (than property) access?
@smoothdeveloper No, this suggestion doesn't cover that.
I really like this exprs |> List.map _.PropertyName
or we could use exprs |> List.map #PropertyName
which aligns with Standard ML and reduces the context overload of .
and _
No, this suggestion doesn't cover that.
It seems like it could be pretty sweet using this for member access, especially with collections of so called fluent api classes which are so popular in C# these days.
The new keyword is the easiest method to implement this functional. overload some structure (_.name
, (.name)
and so on ) will bring some problems that we may do not know. I have some new keywords to candidate:
books |> Array.map which.Author // longer, new keyword *which*
books |> Array.map __.Author // double _ (double underline)
books |> Array.map '.Author // after '. inteliSence will popup
books |> Array.map fun'.Author // prompt this is a fun
books |> Array.map lambda.Author
all the above candidates is context-free. and avoid the problem about white space. for example:
let singleAuthor = getSingleBook() |> which.Author
anywhere or/and everywhere:
which.Author // === `(fun x -> x.Author)`
which<Book>.Author // === `(fun (x:Book) -> x.Author)`
it is easier for compiler or human reader to read or understand.
by the way, inteliSence prompt list is very important. when you typed __.
, VS will popup a select list box to select a member property.
@xp44mm "which" is a long word. Kotlin uses "it" - short and readable.
Interesting. "it" already has a top level meaning in FSI as the previous value. I don't think it sticks out as much though which is a downside.
it
is a perfectly valid identifier though, like most of @xp44mm's proposals, so either we turn it
into a keyword (which is a breaking change) or we add to F# the concept of identifiers that have special meaning in certain contexts (the context being "there is no value called it
in scope", which is extremely error-prone). Either way, I'm not a fan.
I just realized that fun.Prop
is also a possibility, and a non-breaking one (since it is currently always invalid syntax). I think it expresses the intention fairly well.
If we translate _.property
to an inline function with the property as an member constraint of a statically resolved type parameter as suggested by @dsyme in https://github.com/fsharp/fslang-suggestions/issues/506#issuecomment-258126097, then some one who has not seen the syntax before could hover of the ._property
part and see the inline function definition in a tooltip and therefore immediately understand what this syntax does (assuming the reader is familiar with member constraints on statically resolved type parameters).
Note that the OP (@wastaz) was asking for first-class record fields like in Elm (http://elm-lang.org/docs/records), but Elm does not have objects, only records and unions and even in Elm .field
is just a shorthand for \record -> record.field
where \
maps to fun
in F#. Elm introduced the .field
syntax because it supports polymorphic records via row polymorphism, which F# does not, but it is equivalent to structural subtyping, which F# supports via member constraints on statically resolved type parameters.
I just wanted to give a bit of context why I think that translating _.property
to an inline function with the property as an member constraint of a statically resolved type parameter as suggested by @dsyme in #506 (comment) solves the problem exactly the same way as Elm does.
The proposal here seems to resolve towards adding
(.Prop)
or
_.Prop
to the language as a shorthand for
(fun (obj: ^T) -> (^T : (member Prop : 'U) obj)
I would still be interested in a straight-up vote between _.Prop
and (.Prop)
though. Please 👍 for (.Prop)
and ❤️ for _.Prop
, e.g.
👍for this:
xs |> List.map (.Prop) |> List.map (.AnotherProp) |> (.FinalProp)
❤️ for this:
xs |> List.map _.Prop |> List.map _.AnotherProp |> _.FinalProp
I'm for __.Prop
.
.Prop
looks too "unfinished" (i.e. my brain is expecting something before the dot)
@gusty why? It'd remove a lot of noise from code interacting with OO APIs.
@vasily-kirichenko I think .Prop
is not on the menu. Did you mean to write _.Prop
or (.Prop)
?
@vasily-kirichenko I think that was a vote for _.Prop
. Maybe I should change 👎 to ❤️
Oh, than I'm 100% for __.Prop
.
@gusty Changed to ❤️ , please adjust your vote :)
@vasily-kirichenko note that the proposed underscore is a single one, not double. Which I think makes sense.
Ah! Even better :) It's ok to steal good syntax from other languages :)
Would _.Prop.ChildProp
(or (.Prop.ChildProp)
) also work?
@susl I don't think _.Prop.ChildProp
would works, but (_.Prop >> _.ChildProp)
Nemerle also uses _.Prop. I am all in for that.
I think _.Prop.ChildProp.GrandchildMethod().ExtensionProp.ExtensionMethod()
should work.
I normally don't like the use of _
but in this case it works quite nicely:
_
for the "nameless" binding.( )
.Both are an improvement over what we have now, but I prefer the _.
syntax.
how about ..Prop
or even ...Prop
?
❤️
Isn't method
already a reserved keyword? Eg. method.Prop
❤️ wins the day
I vote for _.Prop
as always having to wrap in parens makes the syntax different than most other stuff in F#.
Would that also handle indexers, ex: xs |> _.[1..]
or xs |> (.[0])
?
Would that also handle indexers, ex: xs |> _.[1..] or xs |> (.[0]) ?
Very good question. I suppose not since as described in this suggestion it even doesn't handle method calls, and indexers are sugar for method calls.
I'm for _.Prop
:)
There are a lot hidden scala lovers here ;)
Would that also handle indexers, ex: xs |> _.[1..] or xs |> (.[0]) ?
this is a really cryptic to read. please don allow that. seems code golf.
But this allow other method chaining?
kotlin has the nice it
inside a block, so you can shorthand the whole (fun x -> x > 0 )
val positives = list.filter { it > 0 }
val names = list.filter { uppercait.Name > 0 }
so this proposal will work also for
xs |> List.map _.Prop.ToUpper()
or
xs |> List.filter (_.Age > 0)
or
xs |> List.map (uppercase _.Name)
?
I agree on the cryptic access...
annnd I like the it
stuff too although without the brackets. Please no brackets 😄
EDIT:
What was the disadvantage of using xs |> List.map fun.Name
? I prefer this one even more.
This is a feature that I would love to steal from Elm :) In Elm, if I define a record as such
I then automatically get functions such as .bar and .baz that I can use as getters for this record. So instead of writing code like this
I can write code like this
This does make things a lot nicer in larger chains, for example if I have a list that I want to map over
fooList |> List.map _.Bar |> List.max
Questions
Are indexers allowed
Are method calls allowed
Is this 1-place placeholder syntax?
Is this multi-place placeholder syntax?
Pros and Cons
The advantages of making this adjustment to F# are
The disadvantages of making this adjustment to F# are
Affadavit (must be submitted)
Please tick this by placing a cross in the box:
Please tick all that apply: