Skip to content
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

Stronger type directed conversion from functions to .Net delegates [ RFC FS-1035 ] #248

Closed
baronfel opened this issue Oct 20, 2016 · 10 comments

Comments

@baronfel
Copy link
Contributor

baronfel commented Oct 20, 2016

Submitted by Christoph Rüegg on 4/23/2014 12:00:00 AM
25 votes on UserVoice prior to migration

It seems in some cases type directed conversion from functions to .Net Func<,> delegates only works if the called method has overloads. It would be convenient if it worked the same way for methods without overloads as well.
For example:

open System.Linq

let even n = n % 2 = 0
let seqA = seq { 0..2..10 }

seqA.Where(even) // works
seqA.All(even) // does not work

http://stackoverflow.com/questions/23256355/passing-f-function-to-ienumerable-where-vs-ienumerable-all
http://stackoverflow.com/questions/12933366/f-func-type-inference-difference-between-seq-and-pseq-todictionary

Original UserVoice Submission
Archived Uservoice Comments

@eugbaranov
Copy link

eugbaranov commented Jan 17, 2017

I think it would be great to have this implemented because currently this inconsistency unexpectedly breaks existing code when removing method overrides.

The following works as expected:

type Test() =
    member this.Test(x) = x + 1 |> ignore
    member this.Test(action : System.Func<int, int>) = action.Invoke(1)
let test x = x + 1
Test().Test (test)

Commenting one of the methods breaks use of other method:

type Test() =
    //member this.Test(x) = x + 1 |> ignore
    member this.Test(action : System.Func<int, int>) = action.Invoke(1)
let test x = x + 1
Test().Test (test)
// This expression was expected to have type
//    System.Func<int,int>
//but here has type
//    int -> int

UPDATE
Also, lambdas seems to work fine in the second case:

type Test() =
    //member this.Test(x) = x + 1 |> ignore
    member this.Test(action : System.Func<int, int>) = action.Invoke(1)
let test x = x + 1
Test().Test (fun x -> test x)
// All good

(with Lint wrongly hinting 'If test has no mutable arguments partially applied then the lambda can be removed.')

@dsyme
Copy link
Collaborator

dsyme commented Jan 17, 2017

@Regent Yes, agreed

@smoothdeveloper
Copy link
Contributor

Slightly related to this suggestion, I'm facing a case where having overloads is causing problem with type inference:

dotnet/fsharp#2503

@eugbaranov
Copy link

Created RFC FS-1035.

@dsyme dsyme added needs rfc and removed needs rfc labels Dec 1, 2017
@dsyme dsyme changed the title Stronger type directed conversion from functions to .Net delegates Stronger type directed conversion from functions to .Net delegates [ RFC FS-1035 ] Dec 1, 2017
@baronfel
Copy link
Contributor Author

Would this suggestion help solve the following scenario related to function-composition-to-Func/Action-lifting?

open System

/// string -> unit
let print (s: string) = printfn "%s" s

/// int -> string
let stringify (i: int) = string i

/// int -> unit
let stringifyThenPrint = stringify >> print

/// int -> unit
let doit (i: int) = stringifyThenPrint i

// Action<int> -> int -> unit
let actioner (a: Action<int>) i = a.Invoke i

​(* 
   error FS: This expression was expected to have type
        'Action<int>'    
    but here has type
        'int -> unit' 
*)
let bleh = actioner stringifyThenPrint

@eugbaranov
Copy link

@baronfel, yes - it seems to be related to the same glitch.

@dsyme
Copy link
Collaborator

dsyme commented Sep 25, 2021

Closing this has been completed as part of "additional type directed conversions"

@abelbraaksma
Copy link
Member

@dsyme, although this is closed as completed, I don't think the issue that spawned this RFC (dotnet/fsharp#2907) is actually implemented. At least not in the current version of F#. I.e., the following (which

type Foo =
    static member CallMe (f: Func<string, int, string>) s i =
        f.Invoke(s, i)

module Bar =
    let printInt s i = $"{s}: {i}"
    // this works, but I think that has been so for a while
    let res = Foo.CallMe printInt "The value is" 42
    // lambda DOES NOT work
    let x = Foo.CallMe (fun s i -> $"{s}: {i}") "The value is" 42
    // Curried StringFormat DOES NOT work
    let y = Foo.CallMe (sprintf "%s: %i") "The value is" 42

Though, to be fair, I just tried example of @baronfel, and that does work now. The sprintf I got from the original issue. In which case I should probably just report this as a (new) bug.

@abelbraaksma
Copy link
Member

abelbraaksma commented Jun 28, 2022

FWIW, the linked RFC (dead link, it is now here, RFC FS-1035.md) was archived, and still marked "not implemented". Funnily enough, it says "as a workaround, wrap it in a lambda, works in all cases". I may be missing something, but no, it doesn't work (in all cases), see prev. comment.

Is there another RFC that took its place? Or was it archived so it could be revisited later? (sorry for the confusion)

@abelbraaksma
Copy link
Member

I continued this thread in the discussion thread of the RFC: fsharp/fslang-design#210

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

No branches or pull requests

5 participants