2

In F# interactive shell dotnet fsi, I'm trying to test flip function as in Haskell,

flip :: (a -> b -> c) -> b -> a -> c

> let flip = fun f -> fun a -> fun b -> f(b)(a);;
val flip: f: ('a -> 'b -> 'c) -> a: 'b -> b: 'a -> 'c

then, investigating the built-in pipe-operator,

> (|>);;
val it: ('a -> ('a -> 'b) -> 'b)

so far so good.

Now,

> flip (|>);;

  flip (|>);;
  ^^^^^^^^^

/..... : error FS0030: Value restriction. The value 'it' has been inferred to have generic type
    val it: (('_a -> '_b) -> '_a -> '_b)    
Either make the arguments to 'it' explicit or, if you do not intend for it to be generic, add a type annotation.

Can someone explain what's going on with this error in the F# type system?

To me,

val it: (('_a -> '_b) -> '_a -> '_b) should be actually the expected result, and how can I sort this out? Thanks.

SmoothTraderKen
  • 602
  • 4
  • 16
  • 1
    Does this answer your question? https://stackoverflow.com/a/61243576/180286 – Fyodor Soikin Aug 17 '22 at 13:30
  • @FyodorSoikin Thank you, your answer there is very informative, and I sort of figured out this is about mutability in F#. What I still don't understand is how to avoid this in a concise(elegant) way. Could you advise in the answer with some specific code? I will appreciate it! – SmoothTraderKen Aug 17 '22 at 13:38
  • @FyodorSoikin I mean, the problem here is the type of the both `flip` and `(|>)` functions are generic in principle, so I don't know what to do. – SmoothTraderKen Aug 17 '22 at 13:46
  • 1
    Done. Left an answer. – Fyodor Soikin Aug 17 '22 at 14:35

1 Answers1

6

My previous answer explains what Value Restriction is and why it happens. But now your next question is: ok, so what do I do about it?

As the error message itself suggests, there are two possible ways: (1) give it explicit arguments or (2) if you don't want it to be generic, add a type annotation.

1. Explicit arguments

let flippedPipe x = flip (|>) x

Even though logically this is the same as let flippedPipe = flip (|>), syntactically this is now a function, not a value. And syntax is what matters for the Value Restriction.

2. A type annotation

let flippedPipe : (int -> int) -> int -> int = flip (|>)

This works because the function is no longer generic, so the Value Restriction does not apply. This is a desirable option in many circumstances, but judging by the kinds of functions you're working with here, I would assume this is not what you wanted in this case.

3. Explicit type parameters

The error message doesn't mention this, and in fairness, it can be considered a variation of option (2). The idea is that you can give your function explicit type parameters like this:

let flippedPipe<'a, 'b> = flip (|>)

This makes the Value Restriction go away, because, even though it still technically applies, the fact that you added the type parameters presumably shows that you know what you're doing, so the compiler shuts up.

However, though this appears to work at first glance, it does so in the wrong way. If you look at the inferred type of this function, you'll see:

val flippedPipe : (obj -> obj) -> obj  -> obj

This happens because, even though you added type parameters, you didn't specify exactly where they go. They may not be used at all (aka "phantom types") for all the compiler knows.

So to make it work properly, this should be the definition:

let flippedPipe<'a, 'b> : ('a -> 'b) -> 'a -> 'b = flip (|>)
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • Thanks a Lot! I tried it to confirm by myself, and as you mentioned #2 is out of the scope, and #1 is logically very weird because the function should be the first-class value, so to me it looks like Bug. – SmoothTraderKen Aug 17 '22 at 19:50
  • Finally, #3 in my observation is the closest solution of the error message that is "The value 'it' has been inferred to have generic type `val it: (('_a -> '_b) -> '_a -> '_b)` Either make the arguments to 'it' explicit" – SmoothTraderKen Aug 17 '22 at 19:52
  • So, the great thing is this error fact is the F# has a high inference ability so that it could infer the type that I've intended; however, it also warned me the intended type can cause incosistent result so let me make decide either it's actually genric type of non-generic. – SmoothTraderKen Aug 17 '22 at 19:57
  • Thanks to your previous great answer and concrete answer here, I understand what's going on. I really appreciated it! – SmoothTraderKen Aug 17 '22 at 19:58