questions about fsi - f#

how to change the working directory of fsi in VS.
how to auto load DLLs (e.g. the powerpack) at the startup of fsi.
how to auto open namespaces at the startup of fsi.
Thanks!

There is no direct way to specify this, but you can use the --use:file.fsx command line parameter, which allows you to specify an FSX file that will be loaded by F# interactive when it starts. You can specify the parameter in Tools -> Options -> F# Tools.
To do the three things you asked for, your file.fsx should look like this:
// 3. Open some namespace
open System
// 1. Change the current directory
Environment.CurrentDirectory <- "C:\\Temp"
// 2. Reference an assembly (e.g. PowerPack)
#r #"FSharp.PowerPack.dll"

See F# Interactive Options for a description of all of the available command line options. As detailed on that page, you can use the -r option to reference a particular DLL. There aren't built-in options for the other two questions you asked, but as Tomas mentioned, you can put them into a file which you #use.

Related

Use environment variables in F# directives

In FSI, is it possible to run something like
#I #"%APPDATA%/npm/node_modules/blabla/bin/"
instead of
#I #"C:/Users/username/AppData/Roaming/npm/node_modules/blabla/bin/"
Any other options to make it not sensitive to username?
Not likely you can use environment variables in #I FSI directives.
However you may make your package installation agnostic of user name by using the trick with __SOURCE_DIRECTORY__ built-in identifier similar to one I've described in this SO answer:
create a file anchorfsi.fsx with the single line of code
#I __SOURCE_DIRECTORY__
and put it into the directory %APPDATA% points to
add to the command line starting your FSI the term
--load:%APPDATA%\anchorfsi.fsx
Now you can use relative paths in your #r directives.
Just for illustration I put into directory associated with my user profile a folder testlib containing FSharp.Data.dll. The snip below shows how it gets referenced from FSI using the outlined above technique:

invoke fsi on memory (not file)

Let's say we have a fsi script like this:
#r "System.Core.dll"
let script = "Console.WriteLine(\"Hello, World!\")"
// fsi script ???
We can use #load to invoke fsi on a file, but is it possible to somehow invoke fsi on an in-memory string without writing that to a file first?
The use-case is an API compatibility tester: given a dll, I would like to create a script that invocations all its public APIs and compile that script against a different version of the same dll.
I could always write the generated script to disk, but it would be much cleaner if I could run it directly.
The tasks of kind you outlined in your question may be performed with tools provided within Microsoft.FSharp.Compiler.Interactive namespace, in particular, with the help of type FsiEvaluationSession of Microsoft.FSharp.Compiler.Interactive.Shell.
This gist authored by Ryan Riley demoes exactly your scenario using a thin wrapper type FSharpEngine over FsiEvaluationSession, making programmatic use of fsi as convenient as:
....
let engine = new FSharpEngine()
engine.Execute("<some F# code>") |> processOutput
....
engine.Dispose()

Cannot open namespace in Fsharp script file

When in a separate Fsharp project in a SomeLib.fs file the following code is compiled:
namespace SomeNameSpace
type SomeType =
member this.SomeMember = "Some member"
and you want to reference and use this type in a script file like:
#I #"c:/pathToDll/"
#r "SomeLib.dll"
This is not possible, although the path to the dll is correct and I checked everything. Also when the SomeLib.fs file is in the same project and referenced by #load, you still cannot open the namespace.
I know you can put the type in a module, but I cannot do this as the type has te be used as a Wcf service type.
After a lot of experimental work and surprisingly little info on the internet or in F# books I found out the following:
// Cannot use a relative path
//#I #"bin\Debug"
// Have to use a absolute path
#I #"C:\Development\FSharpNameSpaceTest\SomeCSharpLib\bin\Debug"
// But I can reference a Csharp dll lib
#r "SomeCSharpLib.dll"
// I cannot add a reference to an external F# library dll
// #I #"C:\Development\FSharpNameSpaceTest\NameSpace\bin\Debug"
// #r "NameSpace.dll"
// If I directly load the external fs file, it works"
#load #"C:\Development\FSharpNameSpaceTest\NameSpace\SomeNameSpace.fs"
#load "Library1.fs"
// Namespaces in both the local and the external fs files can only be openend if every single file is loaded instead of referencing the dll.
// Referencing a C# dll is no problem
open FSharpNameSpaceTest
open SomeCSharpLib
open NameSpace
I do not know if this is the most optimal approach but it works. What I will do is, I will create a fsx file for every project that loads the individual fs files in that project and then I will load that fsx file in the fsx file that references the project.
I still find this all very confusing and counterintuitive. But that might be my limited knowledge of the innerworks of F#
Edit: And the right and complete answer is, I didn't implement a default constructor. Still, if you do not wan't to do this, the above approach is an alternative. Thanks to Marc Sigrit.
If on Windows, the slashes in the import directive should be replaced by backslashes.

conditional include in FSharp

I would like to convert a bunch of fs files to fsx files.
Each of those fs file reference class defined in, say, base.fs
So instead of being compiled in the project and relying on the compiler resolution, all would be file based.
That means if I have all those file to include base.fsx, and that one file references another, base.fsx would be included twice.
Does anyone know how to make a conditional include with fsx files ?
The preprocessor documentation states
There is no #define preprocessor directive in F#. You must use the
compiler option or project settings to define the symbols used by the
#if directive.
If you're loading all the files from a single fsx script, then you can load the individual files from the project in the right order and the individual library files do not need to load base.fs directly - the code will be defined, because it has been loaded before.
For example, if you have base.fs:
module Base
let test() = 10
and you have more.fs which does not load base.fs but uses the functions defined there:
module More
let more () =
Base.test() + 1
then you can load all files in F# interactive (in, say, script.fsx) and it will work fine:
#load "base.fs"
#load "more.fs"
More.more()
The only disadvantage is that you won't get IntelliSense when editting more.fs (because the editor does not know about base.fs). To workaround that, it is probably a good idea to keep the files in a project in Visual Studio. But you can still load them in F# interactive for experimentation & testing.

F# 'modular' scripting

What is the recommended way to load+reload fsx files? Just experimenting... yes yes right language right job ect ect..
I love how the following can be done in FSI:
#load "script.fsx";
open Script
> let p = script.x 1
Error: This expression was expected to have type string but here has int...
(* edit script.fsx x to make it int -> int *)
>
> #load "script.fsx"
> let p = script.x 1
val it : int = 2
But how do we do this for an application that we are running via fsi blah.fsx? Maybe something that is sitting in a while loop. It seems #load and #use must not be inside let or module.. i.e. you cannot use #load like let reload script = #load script, wonder why?
My original method was to have .fs files and recompile + relaunch each time I wanted to add/fix something. This method feels primitive.
Second method was to attempt to use the #load directive inside of a module, which turns out to not work (kind of makes sense in terms of scoping)...
module test1 =
#load #"C:\users\pc\Desktop\test.fsx"
open Test
module test2 =
...
Another way would be to create a new process for every module by loading fsi module.fsx with process diagnostics, but this seems horrible, inefficient and ugh.
I have a feeling deep in my heart that this will not be trivial inside .NET, but I would like to pose the question anyway, FSI does it... I wonder if I can leverage the FSI API or something (or at the least to copy their code)?
TL;DR I read the following about erlang and want it for myself in F#.
Erlang: Is there a way to reload changed modules into an already running node with rebar?
"...any time a module in your program changes on disk, the reloader will replace the running copy automatically."
I don't know if this would work in FS but in ML you can load a master file that loads all your files in your project and then executes any code that you need to use to knit them together and runs your application. To see an example of a massive app run from inside of a REPL look at the Isabelle/HOL site at the Cambridge laboratory of Computational Science http://www.cl.cam.ac.uk/research/hvg/Isabelle/installation.html. After downloading the app look in the src code directory for any file called root.ml. There will be half a dozen of them that control various levels of implementation. This is recursive because a top level file can call a file in several sub-directories that loads that particular sub-feature. This allows targeting your application to various scenarios depending on which top level file is executed.
Typical .NET Framework applications cannot unload/reload assemblies unless they are in an App Domains that are separate from the primary one that starts up with the application. This is essentially how most plugin systems are designed for applications that run on the full .NET Framework. Things may be changing post .NET Standard 2.0 in .NET Core with the Collectible Assemblies feature.
References:
https://github.com/dotnet/coreclr/issues/552
https://github.com/dotnet/corefx/issues/19773

Resources