fsharp / fslang-suggestions

The place to make suggestions, discuss and vote on F# language and core library features
344 stars 21 forks source link

Downcast/Upcast operators and keywords as functions similar to infix operators #1069

Closed Swoorup closed 1 year ago

Swoorup commented 3 years ago

I propose we allow both upcast (:>, upcast) and downcast (:?>, downcast) operators as functions similar to infix operators. For example.

let localityEvents = events |> Vector.map (:?> LocalityEvent)
let events = localityEvents |> Vector.map (:> IEvent)
let asIEvent = (:> IEvent)

// keywords
let localityEvents: Vector<LocalityEvent> = events |> Vector.map downcast
let events: Vector<IEvent> = localityEvents |> Vector.map upcast

The existing way of approaching this problem in F# is ...

let localityEvents = events |> Vector.map (fun event -> event :?> LocalityEvent)
let events = localityEvents |> Vector.map (fun localityEvent -> localityEvent :> IEvent)

// keywords
let events: Vector<IEvent> = localityEvents |> Vector.map (fun localityEvent -> upcast localityEvent)
let localityEvents: Vector<LocalityEvent> = events |> Vector.map (fun event -> downcast event)

Pros and Cons

The advantages of making this adjustment to F# are

The disadvantages of making this adjustment to F# are.

Extra information

Estimated cost (XS, S, M, L, XL, XXL): S

Related suggestions: (put links to related suggestions here)

Affidavit (please submit!)

Please tick this by placing a cross in the box:

Please tick all that apply:

For Readers

If you would like to see this issue implemented, please click the :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.

Happypig375 commented 3 years ago

((-) 1) means fun x -> 1 - x, not fun x -> x - 1. Similarly, ((:?>) LocalityEvent) would be fun x -> LocalityEvent :?> x which does not make sense. Instead, we could follow Seq.cast. Maybe we can have (List/Array/Seq/Vector).upCast<_, IEvent> or (List/Array/Seq/Vector).downCast<_, LocalityEvent>.

Swoorup commented 3 years ago

@Happypig375 updated. They aren't parameters in the normal sense, but together with type parameter they would behave as functions

Frassle commented 3 years ago

I think this makes sense for downcast and upcast but as @Happypig375 said above it's inconsistent semantics for :> and :?>.

Happypig375 commented 3 years ago

If the use case is for map, I do not see how introducing more special syntax would be better than upCast<_, ...> or downCast<_, ...>.

Swoorup commented 3 years ago

You'll have to litter your codebase for every single collection types that exists in the wild, since most of the operators act as functions I don't see why we couldn't do the same for this. Use case is wherever casting is involved, that includes custom codebase, not just well known libraries.

@Frassle the inconsistently has been addressed by adjusting the position of the braces.

Frassle commented 3 years ago

@Frassle the inconsistently has been addressed by adjusting the position of the braces.

Yes but that's also kinda inconsistent, in that no other operator supports it.

Happypig375 commented 3 years ago

You'll have to litter your codebase for every single collection types that exists in the wild

It's the responsibility of the collection types to implement collection functions that match List/Array/Seq module signatures. FSharpPlus can also add hypergeneric cast functions on top of map if needed.

since most of the operators act as functions I don't see why we couldn't do the same for this. Use case is wherever casting is involved, not just map.

The syntax of partially applying operators does not make sense with casting. You cannot have the operator without supplying the type.

Happypig375 commented 3 years ago

One way forward for (:?> int) can be adding Haskell's syntax of partial application of infix operators at the same time, e.g. (1 +) -> (fun x -> 1 + x) and (+ 1) -> (fun x -> x + 1) This would provide much greater value. Unfortunately, (+ 1) is already interpreted as use of a prefix operator today.

Happypig375 commented 3 years ago

Another way forward is to merge into #186 and #506 and write (_ :?> int).

dsyme commented 2 years ago

I'm not inclined to this feature

Upcasting and downcasting is just de-emphasised in F# and occurs rarely enough that it seems better to be explicit.

Of the two parts, it's more possible that I could be convined to allow upcast and downcast as first-class functions, sort of resurrecting them back from the edge of deprecation :)

dsyme commented 1 year ago

Closing per comment above