{"id":52902,"date":"2024-07-31T10:05:00","date_gmt":"2024-07-31T17:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=52902"},"modified":"2024-07-31T10:05:00","modified_gmt":"2024-07-31T17:05:00","slug":"enhancing-help-in-fsi","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/enhancing-help-in-fsi\/","title":{"rendered":"Enhancing #help in F# Interactive"},"content":{"rendered":"<blockquote>\n<p>This is a guest blog post by David Schaefer. David is a freelance software developer with a focus on functional programming. He&#8217;s a member of <a href=\"https:\/\/amplifyingfsharp.io\/\">Amplifying F#<\/a>, a community initiative to improve the F# ecosystem. There he works mainly on developer tooling and helps maintain various open-source projects.<\/p>\n<\/blockquote>\n<p>F# interactive, or <a href=\"https:\/\/learn.microsoft.com\/dotnet\/fsharp\/tools\/fsharp-interactive\/\">fsi<\/a>, is a favorite among F# programmers. This component executes F# scripts and provides a REPL (Read, Evaluate, Print Loop) for F#. The feature is well-established and documented, with extensive configuration opportunities (see <code>dotnet fsi --help<\/code>).<\/p>\n<p>This post describes the most recent addition to <code>fsi<\/code> &#8211; the <code>#help \"idn\"<\/code> directive, which allows users to quickly obtain documentation for things like library functions.<\/p>\n<h2>Background<\/h2>\n<p>Some of you might remember our <a href=\"https:\/\/amplifyingfsharp.io\/sessions\/2023\/11\/24\/\">Unlocking F# Potential<\/a> session from last November when I showed the first prototype of <a href=\"https:\/\/github.com\/dawedawe\/fsih\">fsih<\/a>. This small package, modeled after the <code>h<\/code> function of the Elixir <a href=\"https:\/\/hexdocs.pm\/iex\/1.16.0\/IEx.html\">IEx<\/a> REPL, provides documentation available at your fingertips without the need to context switch to a browser. The feedback I got was very encouraging, I released the package and blogged about it in the <a href=\"https:\/\/amplifyingfsharp.io\/blog\/2023\/12\/25\/\">F# Advent<\/a>.<\/p>\n<p>The next logical step was to integrate it into <code>fsi<\/code> itself, removing the hassle of referencing the package in every <code>fsi<\/code> session. The first try was made during an Amplifying F# <a href=\"https:\/\/amplifyingfsharp.io\/sessions\/2024\/01\/26\/\">session<\/a> back in January. For various reasons I wasn&#8217;t able to finish it at that time. A second attempt was started in May, this time with financial backing from the <a href=\"https:\/\/opencollective.com\/amplifying-fsharp\">Amplifying F# Open Collective<\/a>.<\/p>\n<h2>Implementation and discussion<\/h2>\n<p>The <a href=\"https:\/\/github.com\/dotnet\/fsharp\/pull\/17140\">PR<\/a> sparked a good conversation about how to make the functionality available to <code>fsi<\/code> users. My initial port used a new hash directive called <code>#h<\/code>. However, as hash directives in <code>fsi<\/code> are parsed with the regular F# parser, that meant we had to wrap the expression in quotation marks, like <code>#h \"List.map\"<\/code>.<\/p>\n<p><a href=\"https:\/\/github.com\/brianrourkeboll\">Brian<\/a> proposed the idea of using a method in the <code>fsi<\/code> object that is available in every <code>fsi<\/code> session. This would allow us to use the method without the need for quotation marks, e.g., <code>fsi.h List.map<\/code>. I liked the idea and made the necessary changes to the PR. <\/p>\n<p>In parallel, <a href=\"https:\/\/github.com\/KevinRansom\">Kevin<\/a> started to <a href=\"https:\/\/github.com\/dotnet\/fsharp\/pull\/17206\">work on the parser<\/a> to remove the need for quotation marks, as he favored reusing the existing <code>#help<\/code> hash directive for the new functionality. As the primary maintainer of <code>fsi<\/code>, his opinion was the most important one. So, I adapted the PR again to use the <code>#help<\/code> directive. To my great joy, the PR was merged and is already available in the latest .NET 9 preview. With Kevin&#8217;s PR also merged, quotation marks are now optional.<\/p>\n<h2>The usage<\/h2>\n<p>So where does this leave us? Now, when you invoke the <code>#help<\/code> directive in <code>fsi<\/code>, you will see a new entry: <code>#help \"idn\"<\/code> (where <code>idn<\/code> stands for identifier):<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/07\/fsi_hashhelplist_screenshot.png\" alt=\"fsi #help output\" \/><\/p>\n<p>You can use it like this:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/07\/fsi_hashhelplistmap_screenshot.png\" alt=\"fsi #help List.map output\" \/><\/p>\n<p>Et voil\u00e0, you get the functionality from the <code>fsih<\/code> package in <code>fsi<\/code> without any additional dependencies.<\/p>\n<p>To keep things simple and to avoid bloating F#, the dependency of <code>fsih<\/code> on <code>Spectre.Console<\/code> was not ported over. The output is just uncolored plain text. A follow-up PR might add some coloring with the built-in coloring capabilities.<\/p>\n<h2>A few caveats<\/h2>\n<p>It&#8217;s possible to run into type constraint issues when using the functionality with functions like <code>List.sum<\/code>:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/07\/fsi_hashhelplistsum_screenshot1.png\" alt=\"fsi #help List.sum output with an error\" \/><\/p>\n<p>You will see something similar when you just type <code>List.sum<\/code> to get the signature of the function:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/07\/fsi_listsum_screenshot.png\" alt=\"fsi List.sum output\" \/><\/p>\n<p>You can still get the documentation by helping F# infer the type:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/07\/fsi_hashhelplistsum_screenshot2.png\" alt=\"fsi #help List.sum output without the error\" \/><\/p>\n<h2>Acknowledgements<\/h2>\n<p>Overall, I&#8217;m quite happy with the result of the porting effort. It would not have been possible without the generous supporters of the <a href=\"https:\/\/opencollective.com\/amplifying-fsharp\">Amplifying F# Open Collective<\/a>. So once again, I want to express my gratitude to all of you and to all reviewers of the PR. If you are interested in getting involved and helping out with continuous improvements in the F# ecosystem please checkout <a href=\"https:\/\/amplifyingfsharp.io\/\">Amplifying F#<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The &#8216;#help&#8217; directive in F# Interactive can now quickly access documentation instantly within the REPL.<\/p>\n","protected":false},"author":139675,"featured_media":52903,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,636],"tags":[73,7858,85],"class_list":["post-52902","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-fsharp","tag-f","tag-fsi","tag-interactive"],"acf":[],"blog_post_summary":"<p>The &#8216;#help&#8217; directive in F# Interactive can now quickly access documentation instantly within the REPL.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/52902","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\/139675"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=52902"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/52902\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/52903"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=52902"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=52902"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=52902"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}