13

I just realised that it is actually legal to write this:

let _ = sum [1..100]
in  "Hello"

The let-binding appears to do absolutely nothing.

But now I'm wondering about the exact semantics here. It is possible to write a program which contains a _ binding, and yet deleting that binding would visibly alter the meaning of said program?

Basically, I'm wondering whether it's safe to automatically delete such bindings. As far as I can tell, the value of this binding cannot possibly affect anything. However, it seems hypothetically possible that it's type might affect something else. Can anybody construct an example?

John Bartholomew
  • 6,428
  • 1
  • 30
  • 39
MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220

3 Answers3

18

Here is one example. With the _ binding, the output is 8.0, but without it, it is 8. (Admittedly, this isn't a very big difference, but I'm sure this could be used as a basis for something more substantial.)

main :: IO ()
main = let x = 5
           _ = asTypeOf x 6.0
       in print $ x + 3
jwodder
  • 54,758
  • 12
  • 108
  • 124
  • Nice find. Here, the defaulting mechanism makes `x` to be Integer "unless there is a reason to do otherwise". From a practical point of view this may be convenient. From the theoretical point of view this looks as a "special case" making the semantics more complex than needed. – chi Jul 27 '14 at 19:53
  • Disabling the monomorphism restriction did not change this behaviour. I thought that would have been enough to make `x` polymorphic, essentially making the `asTypeOf x 6.0` a compile-time no-op, but I was wrong. Adding `x :: Num a => a` _does_ make it a no-op, instead, which puzzled me. – chi Jul 27 '14 at 20:04
  • 2
    @chi That's because local binds are monomorphized unless explicitly made polymorphic. Adding a type signature for `x` makes it polymorphic explicitly, such that it's defaulted independently of the other binding in the `print` line. – Carl Jul 27 '14 at 20:07
  • @Carl Ah! I did not realize local binds are handled differently with respect to monomorphism. I guess that the `NoMonomorphismRestriction` applies only to the top level bindings, then. – chi Jul 27 '14 at 20:13
  • @chi Yep. There's another extension, `NoMonoLocalBinds` that (usually) makes local binds polymorphic by default. However, it's not compatible with some other extensions, so sometimes it doesn't turn on even if you specify it. – Carl Jul 27 '14 at 21:03
  • 2
    This answer could be greatly improved by explaining why this happens. Though it is definitely a good find! –  Jul 28 '14 at 05:51
10

I think in this example x has type Num a => [a], but without the second line it would be (Num a, Monad m) => m a. Don't have GHCi on this computer to double check, though.

let x = return 3
    _ = sum x
 in x
Doug McClean
  • 14,265
  • 6
  • 48
  • 70
1

A pathological example, but I think it qualifies your requirement

It is possible to write a program which contains a _ binding, and yet deleting that binding would visibly alter the meaning of said program?

which the compiler rejects unless you remove that line:

main = 
  let _ = void main() { fprintf STDERR "I'm a confused C programmer" }
  in print "I'm not"
Joachim Breitner
  • 25,395
  • 6
  • 78
  • 139