{"id":20885,"date":"2019-01-24T07:45:40","date_gmt":"2019-01-24T14:45:40","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/dotnet\/?p=20885"},"modified":"2019-06-19T20:13:30","modified_gmt":"2019-06-20T03:13:30","slug":"announcing-f-4-6-preview","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-f-4-6-preview\/","title":{"rendered":"Announcing F# 4.6 Preview"},"content":{"rendered":"<p><strong>F# 4.6 is now fully released. See the <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-f-4-6\/\" target=\"_blank\" rel=\"noopener noreferrer\">announcement blog post<\/a> for more.<\/strong><\/p>\n<p>We&#8217;re excited to announce that Visual Studio 2019 will ship a new version of F# when it releases: F# 4.6!<\/p>\n<p>F# 4.6 is a smaller update to the F# language, making it a &#8220;true&#8221; point-release. As with previous versions of F#, F# 4.6 was developed entirely via an open RFC (requests for comments) process. The F# community has offered very detailed feedback in discussions for this version of the language. You can view all RFCs that correspond with this release here:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/fsharp\/fslang-design\/tree\/master\/FSharp-4.6\">F# 4.6 RFCs<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/fsharp\/fslang-design\/tree\/master\/FSharp.Core-4.6.0\">FSharp.Core 4.6.0 RFCs<\/a><\/li>\n<\/ul>\n<p>This post will detail the feature set and how to get started.<\/p>\n<h2>Get started<\/h2>\n<p>First, install either:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/core-sdk#installers-and-binaries\">The .NET SDK 2.1.6xx or 2.2.6xx preview of the .NET SDK<\/a><\/li>\n<li><a href=\"https:\/\/visualstudio.microsoft.com\/vs\/preview\/\">Visual Studio 2019 Preview 2<\/a><\/li>\n<\/ul>\n<p>Next, update your FSharp.Core dependency to FSharp.Core 4.6 (or higher). If you&#8217;re using Visual Studio, you can do this with the NuGet Package Management UI. If you are not using Visual Studio, or prefer hand-editing project files, add this to the project file:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/791442ba8fa677f1f838848426e5d714.js\"><\/script><\/p>\n<p>Once you have installed the necessary bits, you can use F# 4.6 with <a href=\"https:\/\/docs.microsoft.com\/dotnet\/fsharp\/get-started\/get-started-visual-studio\">Visual Studio<\/a>, <a href=\"https:\/\/docs.microsoft.com\/dotnet\/fsharp\/get-started\/get-started-with-visual-studio-for-mac\">Visual Studio for Mac<\/a>, or <a href=\"https:\/\/docs.microsoft.com\/dotnet\/fsharp\/get-started\/get-started-vscode\">Visual Studio Code with Ionide<\/a>.<\/p>\n<h2>Anonymous Records<\/h2>\n<p>Aside from various bug fixes, the only language change in F# 4.6 is the introduction of <a href=\"https:\/\/github.com\/fsharp\/fslang-design\/blob\/master\/FSharp-4.6\/FS-1030-anonymous-records.md\">Anonymous Record types<\/a>.<\/p>\n<h3>Basic usage<\/h3>\n<p>From an F#-only perspective, Anonymous Records are F# record types that don&#8217;t have explict names and can be declared in an ad-hoc fasion. Although they are unlikely to fundamentally change how you write F# code, they do fill many smaller gaps F# programmers have encountered over time, and can be used for succinct data manipuation that was not previously possible.<\/p>\n<p>They&#8217;re quite easy to use. For example, here how you can interact with a function that produces an anonymous record:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/82e4d74b32f49e4f1a6c00ad6d22e1d0.js\"><\/script><\/p>\n<p>However, they can be used for more than just basic data containers. The following expands the previous sample to use a more type-safe printing function:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/5e32c63f88e5481fb0428e1235499776.js\"><\/script><\/p>\n<p>If you attempt to call `printCircleStats` with an anonymous record that had the same underlying data types but different labels, it will fail to compile:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/288613f5b50afd86b2bc69fc0ee36c63.js\"><\/script><\/p>\n<p>This is exactly how F# record types work, except everything has been declared ad-hoc rather than up-front. This has benefits and drawbacks depending on your particular situation, so we recommend using anonymous records judiciously rather than replacing all your up-front F# record declarations.<\/p>\n<h3>Struct anonymous records<\/h3>\n<p>Anonymous records can also be structs by using the <em>struct<\/em> keyword:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/5f93a14d160aebd49eaa9b0b26bda8d6.js\"><\/script><\/p>\n<p>You can call a function that takes a struct anonymous record can be done explicitly like this:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/34fb1766da99d020adefcf9113151826.js\"><\/script><\/p>\n<p>Or you can use &#8220;structness inference&#8221; to elide the `struct` at the call site:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/34fb1766da99d020adefcf9113151826.js\"><\/script><\/p>\n<p>This will treat the instance of the anonymous record you created as if it were a struct.<\/p>\n<p>Note that the reverse is not true:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/34fb1766da99d020adefcf9113151826.js\"><\/script><\/p>\n<p>It is not currently possible to define <em>IsByRefLike<\/em> or <em>IsReadOnly<\/em> struct anonymous record types. There is a <a href=\"https:\/\/github.com\/fsharp\/fslang-suggestions\/issues\/712\">language suggestion<\/a> that proposes this enhancement, but due to oddities in syntax it is still under discussion.<\/p>\n<h3>Taking things further<\/h3>\n<p>Anonymous records can be used in a broader set of more advanced contexts.<\/p>\n<h4>Anonymous records are serializable<\/h4>\n<p>You can serialize and deserialize anonymous records:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/b985aa925fb16a59415cbfe00c8ff4c5.js\"><\/script><\/p>\n<p>This outputs what you might expect:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/ac0424d71d512b4482a76389b2963b8c.js\"><\/script><\/p>\n<p>Here&#8217;s a sample library that is also called in another project:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/836202c3ace43d06659be3376698ddca.js\"><\/script><\/p>\n<p>This may make things easier for scenarios like lightweight data going over a network in a system made up of microservices.<\/p>\n<h4>Anonymous records can be combined with other type definitions<\/h4>\n<p>You may have a tree-like data model in your domain, such as the following example:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/cbee6e5779a623d4c12020b5f40c20ad.js\"><\/script><\/p>\n<p>It is typical to see cases modeled as tuples with named union fields, but as data gets more complicated, you may extract each case with records:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/8ce9a9d7da35cb227af5b4873596495e.js\"><\/script><\/p>\n<p>This recursive definition can now be shortened with anonymous records if it suits your codebase:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/ff7206286cfc1ab86e971341be82cc29.js\"><\/script><\/p>\n<p>As with previous examples, this technique should be applied judiciously and when applicable to your scenario.<\/p>\n<h4>Anonymous records ease the use of LINQ in F#<\/h4>\n<p>F# programmers typically prefer using the List, Array, and Sequence combinators when working with data, but it can sometimes be helpful to use <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/programming-guide\/concepts\/linq\/\">LINQ<\/a>. This has traditionally been a bit painful, since LINQ makes use of C# anonymous types.<\/p>\n<p>With anonymous records, you can use LINQ methods just as you would with C# and anonymous types:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/296c3878ab7dfbec88a4131723d4cf2d.js\"><\/script><\/p>\n<p>This prints:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/59fef070202162ee47da55cd65c6723c.js\"><\/script><\/p>\n<h4>Anonymous records ease working with Entity Framework and other ORMs<\/h4>\n<p>F# programmers using <a href=\"https:\/\/docs.microsoft.com\/dotnet\/fsharp\/language-reference\/query-expressions\">F# query expressions<\/a> to interact with a database should see some minor quality of life improvements with anonymous records.<\/p>\n<p>For example, you may be used to using tuples to group data with a `select` clause:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/01533f4142bb514157e2a0ec3eb6d58d.js\"><\/script><\/p>\n<p>But this results in columns with names like <em>Item1<\/em> and <em>Item2<\/em> that are not ideal. Prior to anonymous records, you would need to declare a record type and use that. Now you don&#8217;t need to do that:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/5b71b99d4e695a2d2a58b05741e2726b.js\"><\/script><\/p>\n<p>No need to specify the record type up front! This makes query expressions much more aligned with the actual SQL that they model.<\/p>\n<p>Anonymous records also let you avoid having to create <em>AnonymousObject<\/em> types in more advanced queries just to create an ad-hoc grouping of data for the purposes of the query.<\/p>\n<h4>Anonymous records ease the use of custom routing in ASP.NET Core<\/h4>\n<p>You may be using ASP.NET Core with F# already, but may have run into an awkwardness when defining custom routes. As with previous examples, this could still be done by defining a record type up front, but this has often been seen as unnecessary by F# developers. Now you can do it inline:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/05f6b81111a14637aef819f6e0c6b790.js\"><\/script><\/p>\n<p>It&#8217;s still not ideal due to the fact that F# is strict about return types (unlike C#, where you need not explicitly ignore things that return a value). However, this does let you remove previously-defined record definitions that served no purpose other than to allow you to send data into the ASP.NET middleware pipeline.<\/p>\n<h4>Copy and update expressions with anonymous records<\/h4>\n<p>As with Record types, you can use copy-and-update syntax with anonymous records:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/ff91a9755953718c7143c8f2230671da.js\"><\/script><\/p>\n<p>However, copy-and-update expressions do not restrict the resulting anonymous record to be the same type:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/ebafe362a9e0ef63abb3f804bb1098e9.js\"><\/script><\/p>\n<p>The original expression can also be a record type:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/8bab0f7408ac7c071df79c46755b8a67.js\"><\/script><\/p>\n<p>You can also copy data to and from reference and struct anonymous records:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/4ad75196b972acdc97e47e5a0b673c25.js\"><\/script><\/p>\n<p>The use of copy-and-update expressions gives anonymous records a high degree of flexibility when working with data in F#.<\/p>\n<h4>Equality and pattern matching<\/h4>\n<p>Anonymous records are structurally equatable and comparable:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/e19f98e6e16a0569397a1e7863f0e373.js\"><\/script><\/p>\n<p>However, the types being compared must have the same &#8220;shape&#8221;:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/9cca6c1a3bd83b39eddad7c115f0dca2.js\"><\/script><\/p>\n<p>Although you can equate and compare anonymous records, you cannot pattern match over them. This is for two reasons:<\/p>\n<ul>\n<li>A pattern must account for every field of an anonymous record, unlike record types. This is because anonymous records do not support structural subtyping &#8211; they are nominal types.<\/li>\n<li>There is no ability to have additional patterns in a pattern match expression, as each distinct pattern would imply a different anonymous record type.<\/li>\n<li>The requirement to account for every field in an anonymous record would make a pattern more verbose than the use of &#8220;dot&#8221; notation.<\/li>\n<\/ul>\n<p>Instead, &#8220;dot&#8221;-syntax is used to extract values from an anonymous record. This will always be at most as verbose as if pattern matching were used, and in practice is likely to be less verbose due to not always extracting every value from an anonymous record. Here&#8217;s how to work with a previous example where anonymous records are a part of a discriminated union:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/0a717b482cf6784d3cf9a2585824d079.js\"><\/script><\/p>\n<p>There is currently an <a href=\"https:\/\/github.com\/fsharp\/fslang-suggestions\/issues\/713\">open suggestion<\/a> to allow pattern matching on anonymous records in the limited contexts that they could actually be enabled. If you have a proposed use case, please use that issue to discuss it!<\/p>\n<h2>FSharp.Core additions<\/h2>\n<p>It wouldn&#8217;t be another F# release without additions to the F# Core Library!<\/p>\n<h3>ValueOption expansion<\/h3>\n<p>The <em>ValueOption<\/em> type introduces in F# 4.5 now has a few more goodies attached to the type:<\/p>\n<ul>\n<li>The <em>DebuggerDisplay<\/em> attribute to help with debugging<\/li>\n<li><em>IsNone<\/em>, <em>IsSome<\/em>, <em>None<\/em>, <em>Some<\/em>, <em>op_Implicit<\/em>, and <em>ToString<\/em> members<\/li>\n<\/ul>\n<p>This gives it &#8220;parity&#8221; with the Option type.<\/p>\n<p>Additionally, there is now a <em>ValueOption<\/em> module containing the same functions the the `Option` module has:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/957c4061d38a2691f603fab81058bc0a.js\"><\/script><\/p>\n<p>This should alleviate any concerns that `ValueOption` is the weird sibling of `Option` that doesn&#8217;t get the same set of functionality.<\/p>\n<h3>tryExactlyOne for List, Array, and Seq<\/h3>\n<p>This fine function was contributed by <a href=\"https:\/\/github.com\/gdziadkiewicz\">Grzegorz Dziadkiewicz<\/a>. Here&#8217;s how it works:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/cartermp\/ea6e4debc8d6149876ec36b070506c9c.js\"><\/script><\/p>\n<h2>Wrapping up<\/h2>\n<p>Although the total list of features in F# 4.6 isn&#8217;t huge, they still go quite deep! We encourage you to try out F# 4.6 and leave us feedback so that we can fine-tune things before the full release. As always, thank you to the F# community for their contributions &#8211; both in code and design discussion &#8211; that help us continue to advance the F# language.<\/p>\n<p>Cheers, and happy hacking!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>F# 4.6 is now fully released. See the announcement blog post for more. We&#8217;re excited to announce that Visual Studio 2019 will ship a new version of F# when it releases: F# 4.6! F# 4.6 is a smaller update to the F# language, making it a &#8220;true&#8221; point-release. As with previous versions of F#, F# [&hellip;]<\/p>\n","protected":false},"author":678,"featured_media":21763,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,196,636,646],"tags":[4,9,73,147],"class_list":["post-20885","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-dotnet-core","category-fsharp","category-visual-studio","tag-net","tag-net-core","tag-f","tag-visual-studio"],"acf":[],"blog_post_summary":"<p>F# 4.6 is now fully released. See the announcement blog post for more. We&#8217;re excited to announce that Visual Studio 2019 will ship a new version of F# when it releases: F# 4.6! F# 4.6 is a smaller update to the F# language, making it a &#8220;true&#8221; point-release. As with previous versions of F#, F# [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/20885","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/678"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=20885"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/20885\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/21763"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=20885"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=20885"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=20885"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}