Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pick correct method overload for function composition?

Tags:

f#

Here is a simple composition of functions in F#

let composedFunction = System.Text.Encoding.UTF8.GetBytes >> Array.length
"test" |> composedFunction

Type inference correctly defines the type of composed function string -> int. But compiler cannot pick correct overload of System.Text.Encoding.UTF8.GetBytes method:

Error FS0041: A unique overload for method 'GetBytes' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates:

System.Text.Encoding.GetBytes(chars: char []) : byte [],

System.Text.Encoding.GetBytes(s: string) : byte []Blockquote

Is there any way to compose correct overload of System.Text.Encoding.UTF8.GetBytes which accepts string parameter?

Or course, I can do following

// declare function which calls correct overload and then use it for compostion
let getBytes (s: string) = System.Text.Encoding.UTF8.GetBytes s      
let composedFunction = getBytes >> Array.length

// start composition with ugly lambda
let composedFunction =
  (fun (s: string) -> s) >> System.Text.Encoding.UTF8.GetBytes >> Array.length

But I wonder if there is any way without additional function declarations to make the compiler pick right overload according to the inferred string -> int type of composed function?

like image 925
Sergey Berezovskiy Avatar asked Jan 02 '23 06:01

Sergey Berezovskiy


2 Answers

You can always add annotations:

let composedFunction : string -> _ = System.Text.Encoding.UTF8.GetBytes >> Array.length

or

let composedFunction = (System.Text.Encoding.UTF8.GetBytes : string -> _) >> Array.length
like image 133
AMieres Avatar answered May 06 '23 08:05

AMieres


As your example shows, .NET methods do not always compose well - I think the idiomatic approach in such situations is just to use the .NET style when you're dealing with .NET libraries (and use functional style when you're dealing with functional libraries).

In your specific case, I would just define a normal function with type annotation and get the length using the Length member rather than using the function:

let composedFunction (s:string) = 
  System.Text.Encoding.UTF8.GetBytes(s).Length

The existing answer shows how to get the composition to work with type annotations. Another trick you can do (which I would definitely not use in practice) is that you can add identity function on string to the composition to constrain the types:

let composedFunction = id<string> >> System.Text.Encoding.UTF8.GetBytes >> Array.length

It's fun that this works, but as I said, I would never actually use this, because a normal function as defined above is much easier to understand.

like image 37
Tomas Petricek Avatar answered May 06 '23 08:05

Tomas Petricek