12

The first definition below produces the warning in the title when compiled with f# 3.0 and the warning level set to 5. The second definition compiles cleanly. I wondered if someone could please explain just what the compiler worries I might accidentally mutate, or how would splitting the expression with a let clause help avoid that. Many thanks.

let ticks_with_warning () : int64 =
   System.DateTime.Now.Ticks 

let ticks_clean () : int64 =
   let t = System.DateTime.Now
   t.Ticks 
user1878761
  • 175
  • 6
  • That warning is purely informational, feel free to ignore it. – ildjarn Dec 06 '12 at 22:00
  • 3
    @ildjarn But what does it mean? Is F# being inefficient because of a false positive? – Robert Jeppesen Dec 06 '12 at 22:49
  • 1
    @Robert : I'm not sure what it means, but given that `int64` is a value type, it _must_ be copied. I.e., the warning seems redundant to me if anything. – ildjarn Dec 06 '12 at 22:54
  • 2
    @ildjarn I interpret the warning as 'Now' is being copied, basically that 'ticks_with_warning' is being expanded by the compiler to 'ticks_clean'? – Robert Jeppesen Dec 06 '12 at 23:01
  • @Robert : Ah, yes, that seems sensible, but the code/behavior in Tomas' answer concerns me greatly. – ildjarn Dec 07 '12 at 04:31
  • This is probably already resolved by from my experimentation the F# compiler informs you that it has rewritten the code to: ```let TMPNOW = System.DateTime.Now in TMPNOW.Ticks```. This slight semantic change could be problematic in some cases and beneficial in others. – Just another metaprogrammer May 25 '14 at 11:21

1 Answers1

12

I cannot really explain why the compiler emits this warning in your particular case - I agree with @ildjarn that you can safely ignore it, because the compiler is probably just being overly cautious.

However, I can give you an example where the warning might actually give you a useful hint that something might not go as you would expect. If we had a mutable struct like this:

[<Struct>]
type Test =
  val mutable ticks : int64
  member x.Inc() = x.ticks <- x.ticks + 1L
  new (init) = { ticks = init }

Now, the Inc method mutates the struct (and you can also access the mutable field ticks). We can try writing a function that creates a Test value and mutates it:

let foo () =
  let t = Test(1L)
  t.Inc()  // Warning: The value has been copied to ensure the original is not mutated
  t

We did not mark the local value t as mutable, so the compiler tries to make sure the value is not mutated when we call Inc. It does not know whether Inc mutates the value or not, so the only safe thing is to create a copy - and thus foo returns the value Test(1L).

If we mark t as mutable, then the compiler does not have to worry about mutating it as a result of a call and so it does not give the warning (and the function returns Test(2L)):

let foo () =
  let mutable t = Test(1L)
  t.Inc()
  t

I'm not really sure what is causing the warning in your example though. Perhaps the compiler thinks (as a result of some intermediate representation) that Ticks operation could mutate the left-hand-side value (System.DateTime.Now and t respectively) and it wants to prevent that.

The odd thing is that if you write your own DateTime struct in F#, you get a warning in both cases unless you mark the variable t as mutable (which is what I'd expect), but the behaviour with standard DateTime is different. So perhaps the compiler knows something about the standard type that I'm missing...

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • 2
    "*and thus `foo` returns the value `Test(1L)`.*" I find this behavior incredibly surprising – that is a major change in semantics from what one would expect the code to do, so why is this a level 5 warning when the default is level 3? One could sit and debug for hours trying to solve something like this... – ildjarn Dec 07 '12 at 00:13
  • 2
    @ildjarn - I suspect the justification is this: In f# 2.0 there is warning so the default in F#3.0 can't have a warning either or it would break code if people use `warnaserror`. Interestingly C# has the same behaviour - see http://blogs.msdn.com/b/ericlippert/archive/2008/05/14/mutating-readonly-structs.aspx – John Palmer Dec 07 '12 at 02:40
  • 1
    @JohnPalmer : The C# code in that blog is altogether different – it takes a copy of a value type and then mutates the _copy_, and the output is completely unsurprising to anyone used to value semantics (as is the default in many languages e.g. C++). This code, on the other hand, just calls a method on a value type, and it's as though it never happened; this output I find _entirely_ surprising, bordering nonsensical in the absence of compiler diagnostics. – ildjarn Dec 07 '12 at 04:05
  • However, the copy is created very implicitly in the C# example. I think everyone agrees that in .NET (both C# and F#), value types should never be mutable. – wmeyer Dec 07 '12 at 15:41
  • @ildjarn - So you'd like to get a warning on every call to any method on a value type? That's just going to be noise for the vast majority of calls (since the vast majority of value types are actually immutable, though this fact isn't exposed in the metadata). The lesson, as always, is that mutable value types are evil. – kvb Dec 07 '12 at 15:43
  • 1
    @kvb : No, I'd like the value type to not be silently copied every time I call a method. Mutable value types are what they are, but I'd argue that compilers that silently change the semantics of code are evil. ;-] – ildjarn Dec 07 '12 at 17:16
  • @ildjarn - But then an immutable binding can have different values at different times, which violates my expectations even worse... – kvb Dec 07 '12 at 18:15
  • 1
    Found many occurences of this warning in ProvidedTypes.fs. I added #nowarn "52" at the top of the file to ignore. – ded' Nov 02 '14 at 20:50