From 7b170de29b57a50d82476ccac0ccfbc027b03fa6 Mon Sep 17 00:00:00 2001 From: Max Malook Date: Sat, 5 Dec 2015 22:48:37 +0100 Subject: [PATCH 1/2] use ImplicitZero instead of Yield or Return unit --- src/fsharp/TypeChecker.fs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 4b8ad218061..58766ee8d69 100644 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7512,12 +7512,17 @@ and TcComputationExpression cenv env overallTy mWhole interpExpr builderTy tpenv | None -> // This only occurs in final position in a sequence match comp with - // "do! expr;" in final position is treated as { let! () = expr in return () } + // "do! expr;" in final position is treated as { let! () = expr in return () } when Return is provided or as { let! () = expr in zero } otherwise | SynExpr.DoBang(rhsExpr,m) -> let mUnit = rhsExpr.Range let rhsExpr = mkSourceExpr rhsExpr if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(),m)) - trans true q varSpace (SynExpr.LetOrUseBang(NoSequencePointAtDoBinding, false, false, SynPat.Const(SynConst.Unit, mUnit), rhsExpr, SynExpr.YieldOrReturn((false,true), SynExpr.Const(SynConst.Unit,m), m),m)) translatedCtxt + let bodyExpr = + if isNil (TryFindIntrinsicOrExtensionMethInfo cenv env m ad "Return" builderTy) then + SynExpr.ImplicitZero m + else + SynExpr.YieldOrReturn((false,true), SynExpr.Const(SynConst.Unit, m), m) + trans true q varSpace (SynExpr.LetOrUseBang(NoSequencePointAtDoBinding, false, false, SynPat.Const(SynConst.Unit, mUnit), rhsExpr, bodyExpr, m)) translatedCtxt // "expr;" in final position is treated as { expr; zero } // Suppress the sequence point on the "zero" | _ -> From 130c86b1f16f976a8155254985b7c41b5cb5eb01 Mon Sep 17 00:00:00 2001 From: Max Malook Date: Sun, 13 Dec 2015 20:34:50 +0100 Subject: [PATCH 2/2] add tests for old and new behavior for last do! --- tests/fsharp/core/comprehensions/test.fsx | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/fsharp/core/comprehensions/test.fsx b/tests/fsharp/core/comprehensions/test.fsx index 3cea2803a94..e11b74bf688 100644 --- a/tests/fsharp/core/comprehensions/test.fsx +++ b/tests/fsharp/core/comprehensions/test.fsx @@ -1475,6 +1475,49 @@ module EnumPatternWithFunkyTypes_FSharp_1_0_13904 = // This is allowed - 'a is known to be "bool" let s = seq { for i in T true -> i } + +module SideEffectListMonad = + type SideEffectListWithReturnBuilder(onReturn, onZero) = + member b.Bind(x:unit,f) :list<'b> = f() + member b.Combine(x:list<'a>,y:list<'a>) :list<'a> = List.append x y + member b.Delay(f:unit->list<'a>) :list<'a> = f() + member b.Return _ :list<'a> = onReturn(); [] + member b.Zero() :list<'a> = onZero(); [] + member b.Yield(x:'a) :list<'a> = [x] + + let sideEffectListWithReturn onReturn onZero = SideEffectListWithReturnBuilder(onReturn, onZero) + + type SideEffectListWithZeroBuilder(onZero) = + member b.Bind(x:unit,f) :list<'b> = f() + member b.Combine(x:list<'a>,y:list<'a>) :list<'a> = List.append x y + member b.Delay(f:unit->list<'a>) :list<'a> = f() + member b.Zero() :list<'a> = onZero(); [] + member b.Yield(x:'a) :list<'a> = [x] + + let sideEffectListWithZero onZero = SideEffectListWithZeroBuilder(onZero) + + module SideEffectListTests = + #if Portable + let printfn s = printfn "%s" s + #endif + + let x0a : list * int * int = + let calledReturn = ref 0 + let onReturn () = calledReturn := !calledReturn + 1 + let calledZero = ref 0 + let onZero () = calledZero := !calledZero + 1 + sideEffectListWithReturn onReturn onZero { yield 1 + do! printfn "hello" }, !calledReturn, !calledZero + test "x0a" (x0a = ([1], 1, 0)) + + let x0b : list * int = + let calledZero = ref 0 + let onZero () = calledZero := !calledZero + 1 + sideEffectListWithZero onZero { yield 1 + do! printfn "hello" }, !calledZero + test "x0b" (x0b = ([1], 1)) + + let aa = if !failures then (stdout.WriteLine "Test Failed"; exit 1) else (stdout.WriteLine "Test Passed";