@@ -1639,4 +1639,390 @@ if result3 <> 3 then failwithf $"Expected 3, but got {result3}"
1639
1639
|> withLangVersionPreview
1640
1640
|> asExe
1641
1641
|> compileAndRun
1642
+ |> shouldSucceed
1643
+
1644
+ [<Fact>]
1645
+ let ``Version 9.0 : and ! with record patterns and type annotations requires parentheses`` () =
1646
+ FSharp """
1647
+ module Test
1648
+
1649
+ type Person = { Name: string; Age: int }
1650
+ type User = { Id: int; Username: string }
1651
+
1652
+ type ParallelBuilder() =
1653
+ member _.Return(x) = async { return x }
1654
+ member _.ReturnFrom(computation: Async<'T>) = computation
1655
+ member _.Bind(computation: Async<'T>, binder: 'T -> Async<'U>) =
1656
+ async {
1657
+ let! x = computation
1658
+ return! binder x
1659
+ }
1660
+ member _.Bind2(comp1: Async<'T1>, comp2: Async<'T2>, binder: 'T1 * 'T2 -> Async<'U>) =
1661
+ async {
1662
+ let! task1 = Async.StartChild comp1
1663
+ let! task2 = Async.StartChild comp2
1664
+ let! result1 = task1
1665
+ let! result2 = task2
1666
+ return! binder (result1, result2)
1667
+ }
1668
+
1669
+ member _.Zero() = async.Zero()
1670
+ member _.Combine(comp1, comp2) = async.Combine(comp1, comp2)
1671
+ member _.Delay(f) = async.Delay(f)
1672
+
1673
+ let parallelCE = ParallelBuilder()
1674
+
1675
+ let asyncPerson() = async { return { Name = "John"; Age = 30 } }
1676
+ let asyncUser() = async { return { Id = 1; Username = "john_doe" } }
1677
+
1678
+ let testParallel1() =
1679
+ parallelCE {
1680
+ let! ({ Name = name; Age = age }: Person) = asyncPerson()
1681
+ and! ({ Id = id; Username = username }: User) = asyncUser()
1682
+ return (name, age, id, username)
1683
+ }
1684
+
1685
+ let testParallel2() =
1686
+ parallelCE {
1687
+ let! { Name = name; Age = age }: Person = asyncPerson()
1688
+ and! { Id = id; Username = username }: User = asyncUser()
1689
+ return (name, age, id, username)
1690
+ }
1691
+ """
1692
+ |> withLangVersion90
1693
+ |> typecheck
1694
+ |> shouldFail
1695
+ |> withDiagnostics [
1696
+ ( Error 3350 , Line 43 , Col 14 , Line 43 , Col 48 , " Feature 'Allow let! and use! type annotations without requiring parentheses' is not available in F# 9.0. Please use language version 'PREVIEW' or greater." );
1697
+ ( Error 3350 , Line 42 , Col 14 , Line 42 , Col 46 , " Feature 'Allow let! and use! type annotations without requiring parentheses' is not available in F# 9.0. Please use language version 'PREVIEW' or greater." )
1698
+ ]
1699
+
1700
+ [<Fact>]
1701
+ let ``Preview : and ! with record patterns and type annotations works without parentheses`` () =
1702
+ FSharp """
1703
+ module Test
1704
+
1705
+ type Person = { Name: string; Age: int }
1706
+ type User = { Id: int; Username: string }
1707
+
1708
+ type ParallelBuilder() =
1709
+ member _.Return(x) = async { return x }
1710
+ member _.ReturnFrom(computation: Async<'T>) = computation
1711
+ member _.Bind(computation: Async<'T>, binder: 'T -> Async<'U>) =
1712
+ async {
1713
+ let! x = computation
1714
+ return! binder x
1715
+ }
1716
+ member _.Bind2(comp1: Async<'T1>, comp2: Async<'T2>, binder: 'T1 * 'T2 -> Async<'U>) =
1717
+ async {
1718
+ let! task1 = Async.StartChild comp1
1719
+ let! task2 = Async.StartChild comp2
1720
+ let! result1 = task1
1721
+ let! result2 = task2
1722
+ return! binder (result1, result2)
1723
+ }
1724
+
1725
+ member _.Zero() = async.Zero()
1726
+ member _.Combine(comp1, comp2) = async.Combine(comp1, comp2)
1727
+ member _.Delay(f) = async.Delay(f)
1728
+
1729
+ let parallelCE = ParallelBuilder()
1730
+
1731
+ let asyncPerson() = async { return { Name = "John"; Age = 30 } }
1732
+ let asyncUser() = async { return { Id = 1; Username = "john_doe" } }
1733
+
1734
+ let testParallel1() =
1735
+ parallelCE {
1736
+ let! ({ Name = name; Age = age }: Person) = asyncPerson()
1737
+ and! ({ Id = id; Username = username }: User) = asyncUser()
1738
+ return (name, age, id, username)
1739
+ }
1740
+
1741
+ let testParallel2() =
1742
+ parallelCE {
1743
+ let! { Name = name; Age = age }: Person = asyncPerson()
1744
+ and! { Id = id; Username = username }: User = asyncUser()
1745
+ return (name, age, id, username)
1746
+ }
1747
+
1748
+ let result1 = testParallel1() |> Async.RunSynchronously
1749
+ let result2 = testParallel2() |> Async.RunSynchronously
1750
+
1751
+ if result1 <> ("John", 30, 1, "john_doe") then
1752
+ failwithf $"Expected ('John', 30, 1, 'john_doe'), but got %A {result1}"
1753
+ if result2 <> ("John", 30, 1, "john_doe") then
1754
+ failwithf $"Expected ('John', 30, 1, 'john_doe'), but got %A {result2}"
1755
+ """
1756
+ |> withLangVersionPreview
1757
+ |> asExe
1758
+ |> compileAndRun
1759
+ |> shouldSucceed
1760
+
1761
+ [<Fact>]
1762
+ let ``Version 9.0 : and ! with union patterns and type annotations requires parentheses`` () =
1763
+ FSharp """
1764
+ module Test
1765
+
1766
+ type MyOption<'T> = Some of 'T | None
1767
+
1768
+ type ParallelBuilder() =
1769
+ member _.Return(x) = async { return x }
1770
+ member _.ReturnFrom(computation: Async<'T>) = computation
1771
+ member _.Bind(computation: Async<'T>, binder: 'T -> Async<'U>) =
1772
+ async {
1773
+ let! x = computation
1774
+ return! binder x
1775
+ }
1776
+ member _.Bind2(comp1: Async<'T1>, comp2: Async<'T2>, binder: 'T1 * 'T2 -> Async<'U>) =
1777
+ async {
1778
+ let! task1 = Async.StartChild comp1
1779
+ let! task2 = Async.StartChild comp2
1780
+ let! result1 = task1
1781
+ let! result2 = task2
1782
+ return! binder (result1, result2)
1783
+ }
1784
+
1785
+ member _.Zero() = async.Zero()
1786
+ member _.Combine(comp1, comp2) = async.Combine(comp1, comp2)
1787
+ member _.Delay(f) = async.Delay(f)
1788
+
1789
+ let parallelCE = ParallelBuilder()
1790
+
1791
+ let asyncOption1() = async { return MyOption.Some 42 }
1792
+ let asyncOption2() = async { return MyOption.Some "hello" }
1793
+
1794
+ let testParallel() =
1795
+ parallelCE {
1796
+ let! (Some value1): MyOption<int> = asyncOption1()
1797
+ and! (Some value2): MyOption<string> = asyncOption2()
1798
+ return (value1, value2)
1799
+ }
1800
+
1801
+ let testParallel2() =
1802
+ parallelCE {
1803
+ let! Some value1: MyOption<int> = asyncOption1()
1804
+ and! Some value2: MyOption<string> = asyncOption2()
1805
+ return (value1, value2)
1806
+ }
1807
+ """
1808
+ |> withLangVersion90
1809
+ |> typecheck
1810
+ |> shouldFail
1811
+ |> withDiagnostics [
1812
+ ( Error 3350 , Line 41 , Col 14 , Line 41 , Col 46 , " Feature 'Allow let! and use! type annotations without requiring parentheses' is not available in F# 9.0. Please use language version 'PREVIEW' or greater." );
1813
+ ( Error 3350 , Line 40 , Col 14 , Line 40 , Col 41 , " Feature 'Allow let! and use! type annotations without requiring parentheses' is not available in F# 9.0. Please use language version 'PREVIEW' or greater." )
1814
+ ]
1815
+
1816
+ [<Fact>]
1817
+ let ``Preview : and ! with union patterns and type annotations works without parentheses`` () =
1818
+ FSharp """
1819
+ module Test
1820
+
1821
+ type MyOption<'T> = Some of 'T | None
1822
+
1823
+ type ParallelBuilder() =
1824
+ member _.Return(x) = async { return x }
1825
+ member _.ReturnFrom(computation: Async<'T>) = computation
1826
+ member _.Bind(computation: Async<'T>, binder: 'T -> Async<'U>) =
1827
+ async {
1828
+ let! x = computation
1829
+ return! binder x
1830
+ }
1831
+ member _.Bind2(comp1: Async<'T1>, comp2: Async<'T2>, binder: 'T1 * 'T2 -> Async<'U>) =
1832
+ async {
1833
+ let! task1 = Async.StartChild comp1
1834
+ let! task2 = Async.StartChild comp2
1835
+ let! result1 = task1
1836
+ let! result2 = task2
1837
+ return! binder (result1, result2)
1838
+ }
1839
+
1840
+ member _.Zero() = async.Zero()
1841
+ member _.Combine(comp1, comp2) = async.Combine(comp1, comp2)
1842
+ member _.Delay(f) = async.Delay(f)
1843
+
1844
+ let parallelCE = ParallelBuilder()
1845
+
1846
+ let asyncOption1() = async { return MyOption.Some 42 }
1847
+ let asyncOption2() = async { return MyOption.Some "hello" }
1848
+
1849
+ let testParallel1() =
1850
+ parallelCE {
1851
+ let! (Some value1): MyOption<int> = asyncOption1()
1852
+ and! (Some value2): MyOption<string> = asyncOption2()
1853
+ return (value1, value2)
1854
+ }
1855
+
1856
+ let testParallel2() =
1857
+ parallelCE {
1858
+ let! Some value1: MyOption<int> = asyncOption1()
1859
+ and! Some value2: MyOption<string> = asyncOption2()
1860
+ return (value1, value2)
1861
+ }
1862
+
1863
+ let result1 = testParallel1() |> Async.RunSynchronously
1864
+ let result2 = testParallel2() |> Async.RunSynchronously
1865
+
1866
+ if result1 <> (42, "hello") then
1867
+ failwithf $"Expected (42, 'hello'), but got %A {result1}"
1868
+ if result2 <> (42, "hello") then
1869
+ failwithf $"Expected (42, 'hello'), but got %A {result2}"
1870
+ """
1871
+ |> withLangVersionPreview
1872
+ |> asExe
1873
+ |> compileAndRun
1874
+ |> shouldSucceed
1875
+
1876
+ [<Fact>]
1877
+ let ``Preview : and ! with mixed patterns and type annotations works`` () =
1878
+ FSharp """
1879
+ module Test
1880
+
1881
+ type Person = { Name: string; Age: int }
1882
+
1883
+ type ParallelBuilder() =
1884
+ member _.Return(x) = async { return x }
1885
+ member _.ReturnFrom(computation: Async<'T>) = computation
1886
+ member _.Bind(computation: Async<'T>, binder: 'T -> Async<'U>) =
1887
+ async {
1888
+ let! x = computation
1889
+ return! binder x
1890
+ }
1891
+ member _.Bind2(comp1: Async<'T1>, comp2: Async<'T2>, binder: 'T1 * 'T2 -> Async<'U>) =
1892
+ async {
1893
+ let! task1 = Async.StartChild comp1
1894
+ let! task2 = Async.StartChild comp2
1895
+ let! result1 = task1
1896
+ let! result2 = task2
1897
+ return! binder (result1, result2)
1898
+ }
1899
+
1900
+ member _.Zero() = async.Zero()
1901
+ member _.Combine(comp1, comp2) = async.Combine(comp1, comp2)
1902
+ member _.Delay(f) = async.Delay(f)
1903
+
1904
+ let parallelCE = ParallelBuilder()
1905
+
1906
+ let asyncInt() = async { return 42 }
1907
+ let asyncPerson() = async { return { Name = "Alice"; Age = 25 } }
1908
+
1909
+ // Test mixing different pattern types with type annotations
1910
+ let testMixed() =
1911
+ parallelCE {
1912
+ let! x: int = asyncInt()
1913
+ and! { Name = name; Age = age }: Person = asyncPerson()
1914
+ return (x, name, age)
1915
+ }
1916
+
1917
+ // Test with parentheses on one and not the other
1918
+ let testMixed2() =
1919
+ parallelCE {
1920
+ let! (y: int) = asyncInt()
1921
+ and! { Name = name2; Age = age2 }: Person = asyncPerson()
1922
+ return (y, name2, age2)
1923
+ }
1924
+
1925
+ let result1 = testMixed() |> Async.RunSynchronously
1926
+ let result2 = testMixed2() |> Async.RunSynchronously
1927
+
1928
+ if result1 <> (42, "Alice", 25) then
1929
+ failwithf $"Expected (42, 'Alice', 25), but got %A {result1}"
1930
+ if result2 <> (42, "Alice", 25) then
1931
+ failwithf $"Expected (42, 'Alice', 25), but got %A {result2}"
1932
+ """
1933
+ |> withLangVersionPreview
1934
+ |> asExe
1935
+ |> compileAndRun
1936
+ |> shouldSucceed
1937
+
1938
+ [<Fact>]
1939
+ let ``Preview : multiple and ! with type annotations works `` () =
1940
+ FSharp """
1941
+ module Test
1942
+
1943
+ type Builder() =
1944
+ member _.Return(x) = async { return x }
1945
+ member _.Bind(computation: Async<'T>, binder: 'T -> Async<'U>) =
1946
+ async {
1947
+ let! x = computation
1948
+ return! binder x
1949
+ }
1950
+ member _.Bind3(comp1: Async<'T1>, comp2: Async<'T2>, comp3: Async<'T3>, binder: 'T1 * 'T2 * 'T3 -> Async<'U>) =
1951
+ async {
1952
+ let! task1 = Async.StartChild comp1
1953
+ let! task2 = Async.StartChild comp2
1954
+ let! task3 = Async.StartChild comp3
1955
+ let! result1 = task1
1956
+ let! result2 = task2
1957
+ let! result3 = task3
1958
+ return! binder (result1, result2, result3)
1959
+ }
1960
+
1961
+ member _.Zero() = async.Zero()
1962
+ member _.Combine(comp1, comp2) = async.Combine(comp1, comp2)
1963
+ member _.Delay(f) = async.Delay(f)
1964
+
1965
+ let builder = Builder()
1966
+
1967
+ let asyncInt() = async { return 42 }
1968
+ let asyncString() = async { return "test" }
1969
+ let asyncFloat() = async { return 3.14 }
1970
+
1971
+ // Test multiple and! with type annotations
1972
+ let testMultiple() =
1973
+ builder {
1974
+ let! x: int = asyncInt()
1975
+ and! y: string = asyncString()
1976
+ and! z: float = asyncFloat()
1977
+ return (x, y, z)
1978
+ }
1979
+
1980
+ let result = testMultiple() |> Async.RunSynchronously
1981
+
1982
+ if result <> (42, "test", 3.14) then
1983
+ failwithf $"Expected (42, 'test', 3.14), but got %A {result}"
1984
+ """
1985
+ |> withLangVersionPreview
1986
+ |> asExe
1987
+ |> compileAndRun
1988
+ |> shouldSucceed
1989
+
1990
+ [<Fact>]
1991
+ let ``Preview : and ! with generic type annotations works `` () =
1992
+ FSharp """
1993
+ module Test
1994
+
1995
+ type Result<'T, 'E> = Ok of 'T | Error of 'E
1996
+
1997
+ type ResultBuilder() =
1998
+ member _.Return(x) = Ok x
1999
+ member _.Bind(m: Result<'T, 'E>, f: 'T -> Result<'U, 'E>) =
2000
+ match m with
2001
+ | Ok x -> f x
2002
+ | Error e -> Error e
2003
+ member _.Bind2(m1: Result<'T1, 'E>, m2: Result<'T2, 'E>, f: 'T1 * 'T2 -> Result<'U, 'E>) =
2004
+ match m1, m2 with
2005
+ | Ok x1, Ok x2 -> f (x1, x2)
2006
+ | Error e, _ -> Error e
2007
+ | _, Error e -> Error e
2008
+
2009
+ let result = ResultBuilder()
2010
+
2011
+ let getValue1() = Ok 42
2012
+ let getValue2() = Ok "success"
2013
+
2014
+ let test() =
2015
+ result {
2016
+ let! x: int = getValue1()
2017
+ and! y: string = getValue2()
2018
+ return (x, y)
2019
+ }
2020
+
2021
+ match test() with
2022
+ | Ok (42, "success") -> printfn "Test passed"
2023
+ | _ -> failwith "Test failed"
2024
+ """
2025
+ |> withLangVersionPreview
2026
+ |> asExe
2027
+ |> compileAndRun
1642
2028
|> shouldSucceed
0 commit comments