Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wildcard "use" binding works in sequence expression, but not otherwise

Tags:

f#

Using the wildcard pattern with use works within a sequence expression, but not otherwise. Is there a reason for this?

let mkDisposable() = 
  { new IDisposable with 
      member __.Dispose() = () }

let mkSeq() =  
  seq {
    use _ = mkDisposable() //OK
    ()
  }

let f() =  
  use _ = mkDisposable() //ERROR: 'use' bindings must be of the form 'use <var> = <expr>'
  ()
like image 690
Daniel Avatar asked Oct 09 '22 06:10

Daniel


2 Answers

I believe that this is a natural (but unexpected) consequence of the desugaring of computation expressions (a sequence expression in this case, but this behavior applies to all computation expressions). As the spec indicates,

use pat = expr
cexpr

is translated to

Using(expr, fun pat -> cepxr)

Because this is a shallow syntactic translation, you can use any pattern that could be used when writing a function, including just _. However, for normal use bindings, the left hand side of the binding must be an identifier, not a pattern (see section 6.6.3 of the spec).

like image 94
kvb Avatar answered Oct 13 '22 12:10

kvb


I've done a bit of digging and it seems that the special way seq expressions are handled changes the rules of use. The seq expression is in fact transformed into the following with an IDisposable field that is disposed upon completion of the sequence.

internal sealed class mkSeq@11<a> : GeneratedSequenceBase<a>
{
    [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode]
    public IDisposable matchValue = matchValue;
    [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode]
    public int pc = pc;
    [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode]
    public a current = current;
    public mkSeq@11(IDisposable matchValue, int pc, a current)
    {
    }
    public override int GenerateNext(ref IEnumerable<a> next)
    {
        switch (this.pc)
        {
            case 2:
            {
                break;
            }
            case 3:
            {
                goto IL_55;
            }
            default:
            {
                this.matchValue = Program.mkDisposable();
                this.pc = 2;
                break;
            }
        }
        this.pc = 3;
        LanguagePrimitives.IntrinsicFunctions.Dispose<IDisposable>(this.matchValue);
        this.matchValue = null;
        this.pc = 3;
        IL_55:
        this.current = default(a);
        return 0;
    }
    public override void Close()
    {
        switch (this.pc)
        {
            case 1:
            {
                goto IL_41;
            }
            case 3:
            {
                goto IL_41;
            }
        }
        this.pc = 3;
        LanguagePrimitives.IntrinsicFunctions.Dispose<IDisposable>(this.matchValue);
        IL_41:
        this.pc = 3;
        this.current = default(a);
    }
    public override bool get_CheckClose()
    {
        switch (this.pc)
        {
            case 1:
            {
                return false;
            }
            case 3:
            {
                return false;
            }
        }
        return true;
    }
    [CompilerGenerated, DebuggerNonUserCode]
    public override a get_LastGenerated()
    {
        return this.current;
    }
    [CompilerGenerated, DebuggerNonUserCode]
    public override IEnumerator<a> GetFreshEnumerator()
    {
        return new Program<a>.mkSeq@11(null, 0, default(a));
    }
}

Normally use is transformed into this:

IDisposable e = Program.mkDisposable();
try
{
}
finally
{
    IDisposable disposable = e as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

Without a variable name the compiler will ignore the result of the expression and thus it cannot be disposed. To be honest it seems like a special case should be made for use so all the boilerplate is created behind the scenes like we see in seq expressions.

like image 20
ChaosPandion Avatar answered Oct 13 '22 10:10

ChaosPandion