-
Notifications
You must be signed in to change notification settings - Fork 21
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
Allow _.Property shorthand for accessor functions #506
Comments
I really like this suggestion, but it would probably have to be something other than a dot. |
Yea, I just realised the same thing and updated the proposal :) |
Great suggestion. I love the way this works in Elm, but as already mentioned, this is complicated by F# ignoring whitespace between an identifier and the following dot. Maybe these functions could be defined directly on the type and then always accessed with the type name prefix. This would also help with type inference by disambiguating between different record types with the same label (another problem Elm doesn't have due to structurally typed records). E.g.: type Foo = { id : int }
type Bar = { id : int }
[{id = 1}] |> List.map Foo.id The compiler already disallows defining an Another advantage of this approach is that it doesn't require any syntax changes. It's slightly more verbose than what was originally asked for but seems to fit the general F# style. Maybe there are other pitfalls? |
I've just realised that the expression |
@theprash That could work, however I do believe that this should be possible to achieve even without having to declare the type (see the generic constraint function in my original post) which I think should be possible to work with in a nice way with the type inference. However, yes. The dot wont work, and it might be that the code is clearer by writing the typename instead of adding another symbol into the mix. My hope was to be able to stick as close to the Elm implementation/sematics here as possible given that I really like it and it seems to be doable with the generic constraints (at least from my admittedly very limited point of view). But I can certainly see the point of doing it either way. |
duplicate of #159, but this suggestion already has more detail so maybe we expand & extend this one instead? I think an operator with non-standard semantics, similar to how the dynamic operator let inline (?) (src : 'a) (prop : string) : 'b = src.GetType().GetProperty(prop).GetValue(src, null) :?> 'b
let x = "arg"?Length : int
> val x : int = 3 But what we'd want in this case is for the identifier following the accessor operator to be used in a static check against the members of the preceding type, like how it does in a SRTP member call. I don't think this kind of feature should be limited to properties which are a subset of the more general problem of accessing the members of a type passed to a single argument lambda. As such it should support methods as well.
Potential Accessor Operators - ( @. ) @.Data
( .@ ) .@Data
( @| ) @|Data
( |@ ) |@Data
( =| ) =|Data
( |= ) |=Data
( |- ) |-Data
( -| ) -|Data
( ./ ) ./Data
( /. ) /.Data
( |. ) |.Data
( .| ) .|Data
( !. ) !.Data
( *@ ) *@Data
( @* ) @*Data
( -@ ) -@Data
( @- ) @-Data
( |* ) |*Data
( *| ) *|Data |
This appears to be a duplicate of #159 or perhaps of #440 . The original lengthy discussion of #159 on UV is worth taking a look at too. It's an interesting suggestion to make |
Since there is active discussion here, I'll close #159. I'd be grateful if someone could cherry-pick a summary of the original UV discussion into this thread or the suggestion description. |
Crap, I thought it was strange that no one had suggested this before (and of course someone had). :) Just adding some opinions here again, of the options that @cloudRoutine had I'm quite fond of |
@wastaz How about cheers |
@dsyme I like warnings for space-separated long identifiers. However, would this only be for space-separated identifiers in that case? I'm thinking if maybe it's confusing if
is different than
In this case it's probably silly to structure it like that, but if I understand it correctly then the second example would be interpreted as So basically, I think that in the end that special case would be more confusing than helping? |
@cloudRoutine |
I agree with @wastaz, it'll lead to an explosion of warnings across code like let builder =
StringBuilder()
.Append('\t', indent)
.Append(sprintf "%s(%s) = " block.BlockType block.ParenthesizedName)
.AppendLine block.Value which is just one of several fluent examples I could pick out the project I'm working on right now. @rojepp None of the operators I listed conflict with existing operators |
@cloudRoutine The uses of let builder =
System.Console
.WriteLine("abc") Certainly |
@dsyme I agree that |
One concern is the clumsiness of Another concern is whether you could reasonably give autocomplete on |
I think that you might not do (sorry for the contrived example)
...actually..typing that didn't really feel too bad, I'm not sure if it really would be that clumsy. |
I hope this won't be limited to fun (str:string) -> str.Split ... and many other similar lambdas to use instance methods far more often than I do to access a property. |
@dsyme Do you have any thoughts on including the type name? |
The original User Voice thread has a gem of an idea in it that I don't think anyone addressed:
I've always wanted some short syntax for partial application in an arbitrary order but never realised it could help with record access too: type Foo = { id: int }
[{id = 1}] |> List.map _.id
[1; 2; 3] |> List.map (String.replicate _ "x")
[1; 2; 3] |> List.map (1 - _)
// Multiple 'slots' allowed
(String.replicate _ _) 3 "x" I'm not sure about the underscore but the idea is there. It may be difficult to integrate with current syntax so there may need to be a prefix symbol or keyword. |
@theprash I like the suggestion of |
|
Also it's used in Nemerle for both substituting lambda arguments and partial application in exactly the same form as @theprash suggested, see https://github.com/rsdn/nemerle/wiki/Quick-guide#anonymous-functions-and-partial-applications. I like it very much as it makes .NET Frameworks and C#-oriented libraries interoperability much, much nicer. Such form of partial application makes code more readable in some scenarios. For example, in Scala it look like this: def foo(i: Int, s: String, d: Double, a: Any) : Unit = {}
val f = foo(1, _, 2.2, _)
val x = f("bar", null) It'd be fantastic if let getName = _.Name can be automatically generalized to Nemerle cannot generalize such a function and infer type from first (local) usage. Scala does not allow such form at all. |
A couple of examples func.TryGetFullDisplayName()
|> Option.map (fun fullDisplayName -> processIdents func.FullName (fullDisplayName.Split '.'))
|> Option.toList
func.TryGetFullDisplayName()
|> Option.map (processIdents func.FullName (_.Split '.'))
|> Option.toList
func.TryGetFullDisplayName()
|> Option.map (processIdents func.FullName (.Split '.'))
|> Option.toList
func.TryGetFullDisplayName()
|> Option.map (processIdents func.FullName ((.Split) '.')))
|> Option.toList uses
|> Seq.map (fun symbolUse -> (symbolUse.FileName, symbolUse))
|> Seq.groupBy (fst >> Path.GetFullPathSafe)
|> Seq.collect (fun (_, symbolUses) ->
symbolUses
|> Seq.map snd
|> Seq.distinctBy (fun s -> s.RangeAlternate))
|> Seq.toArray
uses
|> Seq.map (_.FileName, symbolUse)
|> Seq.groupBy (fst >> Path.GetFullPathSafe)
|> Seq.collect (fun (_, symbolUses) ->
symbolUses
|> Seq.map snd
|> Seq.distinctBy (_.RangeAlternate))
|> Seq.toArray
uses
|> Seq.map (.FileName, symbolUse)
|> Seq.groupBy (fst >> Path.GetFullPathSafe)
|> Seq.collect (fun (_, symbolUses) ->
symbolUses
|> Seq.map snd
|> Seq.distinctBy (.RangeAlternate)
|> Seq.toArray |
Im not sure Im also unsure whether this language idea is for shorthand access to properties or a way to partially apply functions. I think [1; 2; 3] |> List.map (String.replicate _ "x") Seem a little obtuse, intension is too hidden |
Using this for creating lambdas is definitely quite a big change to the language and would take some getting used to but it's potentially so powerful. It almost obviates the need for currying! The compiler could see if a value expression (as opposed to a pattern expression) contains a records |> List.map _.recordLabel
[1] |> List.map (_ - 1)
[Some 1] |> List.choose _ // `_` equivalent to `id` This would be much more useful if you could refer to multiple parameters. But then there's also a difficulty in knowing which // Instead of...
let flip f = fun a b -> f b a // A helper somewhere
(flip String.replicate) "x" 3
// Simply...
(String.replicate _2 _) "x" 3 And then that also allows to referring to the same parameter twice: // Takes one parameter
let square = _ * _ This could definitely be confusing, especially if there is a let func x =
let a = 1
let b = 2
a + _ + b + x
// func actually takes two parameters! So probably quite dangerous to just throw into the language like this. But what if it required a prefix symbol? Let's try the above examples prefixing with records |> List.map \_.recordLabel
[1] |> List.map (\_ - 1)
[Some 1] |> List.choose \_
(\ String.replicate _2 _) "x" 3
let square = \ _ * _
let func x =
let a = 1
let b = 2
\ a + _ + b + x It's more explicit but less pretty, and probably confusing to someone used to And is this compatible with automatically generic record labels? Maybe not? I've convinced myself that having this new lambda syntax, as opposed to just record label accessors, would take a lot more thought to be viable, if it is at all. |
@dsyme Would be possible to use two dots for the syntax (
|
That's too similar to the range operator, isn't it? |
That's right. This will cause some confusion. My others sugestions:
The motivation to use like this is that remembers a static access. |
agree, this is already useful enough |
Purescript uses the syntax: |
The myriad fields plugin generates these. You can have thus now! 😎
…Sent from my iPhone
On 2 Aug 2022, at 08:57, Oskar Gewalli ***@***.***> wrote:
Purescript uses the syntax:
https://github.com/purescript/documentation/blob/master/language/Syntax.md#property-accessors
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.
|
Merged |
When will this feature be available to use? |
@dlidstrom when .NET 8 is released November. |
I know this is the wrong place to ask this, but is there an easier way to try out preview features in my projects on non-Windows (so no VS preview) platforms, without having to build F# from source on your own machine? |
In FSAC we have a 'nightly' branch where we try to incorporate the prerelease versions of the compiler library. We're currently working on the 8.0.100 previews (and in fact have identified a regression!) but we don't have an automated release system like is present for VS. The current steps would be something like:
|
@baronfel Interesting, got a link to the regression? |
Was there an RFC for this suggestion? |
@Lanayx, I found only |
I think that one should better be finished and merged, otherwise the feature will be undocumented |
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:
The text was updated successfully, but these errors were encountered: