Skip to content

Type-directed resolution of [ .. ] syntax #1086

@dsyme

Description

@dsyme

I propose we consider doing type-directed resolution of the [...] syntax if used with a non-list known type.

type SomeApi() =
    static member SomeEntryPoint(xs: ImmutableArray<_>) = ...

SomeApi.SomeEntryPoint([1; 2; 3])

The main thing here is that whenever a method needs a collection (of more or less any kind), as input, you can always use, for example, [1;2;3] and not think about it.

This means F# APIs can reasonable choose to adopt ImmutableArray or other such collection types for inputs without particularly changing user code.

If this is considered reasonable, the question arises about when this is allowed. For example, it's possible that it should just be at method argument position - F# has a tradition of only applying such "magic" (e.g. lambda --> delegate, ParamArray and more recently op_Implicit) only at method applications. However in theory it could also be at any position by type-directed resolution, e.g.

let x: ImmutableArray<_> = [1;2;3]

For some reason people don't seem to mind these transformations at method calls.

From a previous suggestion:

Have the [ ... ] syntax resolved in a type-directed way (e.g. "if the target type is known to be a collection C with a builder pattern or IEnumerable constructor, then treat the syntax as if it is constructing a C, otherwise treat it as a regular F# list"))?

The syntax would work with

  • list types
  • array types by obvious translation
  • Set/Map types (we would add a builder for these)
  • ReadOnlySpan or Span, using stackalloc.
  • Mutable collection types that have a constructor (optionally with a capacity arg), and an 'Add' method to add the elements.
  • System.Collections.Immutable through a builder pattern definable via extensions or naming

This is somewhat related to the ListCollector type in F# 6 which is use as our builder for F# immutable lists and which gave the performance results described here. The only subtlety there is the presence of AddManyAndClose that allows lists to be collected in tail position without copying the list.

The underlying problem here is that we over-emphasise the "list" type in F# - that is, in an API designer wants to get nice, minimal callside syntax, they have no choice but to accept F# lists. However this has problems

  • It can be relatively highly allocating
  • It's not pleasant from C#
  • It's probably not the data representation used inside the API. For example the F# quotations API uses list throughout. but internally converts to arrays.

Pros and Cons

The advantages of making this adjustment to F# are:

  • nice syntax for many more collection types.

The disadvantages of making this adjustment to F# are:

  • performance can vary significantly based on type annotation
  • harder to work out what actual objects are being allocated
  • you need a type annotation or strongly known type
  • there are two ways to do arrays, i.e. [| 1;2 |] and via known type ([ 1; 2 ]: int array)

Extra information

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

Related suggestions: (put links to related suggestions here)

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

For Readers

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions