Skip to content

[<System.ParamArray>] parameter in member function causes type check error. #17957

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

Open
muqiuhan opened this issue Nov 5, 2024 · 3 comments
Open

Comments

@muqiuhan
Copy link
Contributor

muqiuhan commented Nov 5, 2024

When I use the parameter of [<ParamArray>] attribute in member function, I cannot pass the parameter using pipeline operator:

> type X () = member _.ID ([<System.ParamArray>] arr) = arr;;
type X =
  new: unit -> X
  member ID: [<System.ParamArray>] arr: 'a -> 'a

> let x = X();;                                              
val x: X

> x.ID([| 1; 2; 3 |]);;                                      
val it: int array = [|1; 2; 3|]

> [| 1; 2; 3 |] |> x.ID;;

  [| 1; 2; 3 |] |> x.ID;;
  -----------------^^^^
/home/muqiu/stdin(10,18): error FS0001: This expression was expected to have type
    'int array'    
but here has type
    'unit'

And no type check error occurs when the [<ParamArray>] attribute is not used or using [<ParamArray>] at the top level:

> type X () = member _.ID (arr) = arr;;
type X =
  new: unit -> X
  member ID: arr: 'a -> 'a

> let x = X();;                        
val x: X

> [| 1; 2; 3 |] |> x.ID;;
val it: int array = [|1; 2; 3|]

> let ID ([<System.ParamArray>] arr) = arr;;     
val ID: [<System.ParamArray>] arr: 'a -> 'a

> [| 1; 2; 3 |] |> ID;;                     
val it: int array = [|1; 2; 3|]

Why does [<ParamArray>] behave differently in member functions? Is this a compiler bug?

@brianrourkeboll
Copy link
Contributor

Probably a duplicate of #11918.

I think it's a type inference + overload resolution thing: a method with a single parameter annotated with [<ParamArray>] can also be treated as a nullary method (or a method taking a single parameter of type unit).

But there is not perfect symmetry in how overload resolution works for directly-invoked methods and methods used in a first-class way (e.g., with piping). See #11918 (comment).

@brianrourkeboll
Copy link
Contributor

brianrourkeboll commented Nov 5, 2024

The error message is confusing, though:

> open System
-
- type T =
-     static member M ([<ParamArray>] xs : int array) = xs
-
- let xs = [|1..10|]
-
- xs |> T.M;;

  xs |> T.M;;
  ------^^^

stdin(8,7): error FS0001: This expression was expected to have type
    'int array'
but here has type
    'unit'

It sounds like the opposite of what the source code looks like.

Especially since this works:

() |> T.M

@ijklam
Copy link
Contributor

ijklam commented Apr 25, 2025

It is same situation for optional parameters. And this only occurs when the method doesn't have overloads.

type X () = 
  member _.ID ([<System.ParamArray>] arr) = arr
  // another overload
  member _.ID (x: int) = x

  member _.OptionalParameter (?x: int) = x

let x = X()
// will not fail
[| 1; 2; 3 |] |> x.ID
// will fail
1 |> x.OptionalParameter 

There is a comment say the optional and out args are resolved to their default values when the method doesn't have other overloads. Can we change this behavior to make it works like a normal function?

// Because there is only one accessible method info available based on the name of the item
// being accessed we know the number of arguments the first class use of this
// method will take. Optional and out args are _not_ included, which means they will be resolved
// to their default values (for optionals) and be part of the return tuple (for out args).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: New
Development

No branches or pull requests

5 participants