F# FSI, Change working directory - f#

I have the following file located in a "New Folder" of Desktop:
// File location: "C:\Users\my_user_name\Desktop\New folder\AddOne.fs"
//
module internal AddOneModule
let AddOneFunction x = x + 1
I can access this file by using #load on the full path name using FSI F# Interactive.
Microsoft (R) F# Interactive version 4.1
Copyright (c) Microsoft Corporation. All Rights Reserved.
For help type #help;;
> #load "C:\Users\my_user_name\Desktop\New folder\AddOne.fs";;
//[Loading C:\Users\my_user_name\Desktop\New folder\AddOne.fs]
//namespace FSI_0002
// val AddOneFunction : x:int -> int
> open AddOneModule;;
> AddOneFunction 100;;
// val it : int = 101
How do I change the working directory so that I can access the file using relative path?
F# interactive:how to display/change current working directory
I tried something similar to the post above, but FSI still tries to find the file in the Temp folder:
(RESET FSI)
Microsoft (R) F# Interactive version 4.1
Copyright (c) Microsoft Corporation. All Rights Reserved.
For help type #help;;
> open System;;
> Environment.CurrentDirectory <- #"C:\Users\my_user_name\Desktop\New folder";;
//val it : unit = ()
> #load "AddOne.fs";;
// #load "AddOne.fs";;
// ^^^^^^^^^^^^^^^^^
//C:\Users\my_user_name\Desktop\New folder\stdin(3,1): error FS0078: Unable to find
//the file 'AddOne.fs' in any of
// C:\Users\my_user_name\AppData\Local\Temp
Thank you for your help.

Instead of changing the working directory you may achieve the desired using a trick with __SOURCE_DIRECTORY__ built-in identifier.
To begin with, you need a certain anchor point in directory structure. For illustration purposes let's assume you are on Windows and let this anchor point be your user directory, which is defined by %USERPROFILE% environment variable. Place there a script anchorfsi.fsx containing the following single line of code:
#I __SOURCE_DIRECTORY__
That's basically all you need to do. Now, regardless from what location you shoot your fsi using command line fsi --load:%USERPROFILE%\anchorfsi.fsx, you can use relative paths in your scripts and in interactive commands.
Turning to the setup in your question with loading .\desktop\new folder\addone.fs, the following screenshot demonstrates achieving the desired:
Notice how the entered relative path ".\desktop\new folder\addone.fs" was correctly mapped to the absolute one C:\Users\gene\desktop\new folder\addone.fs without any dependency upon the fsi working directory whatsoever.

Related

Importing FSharp.Charting in .fs file fails

Create a new FSharp Console project via VS2015
Add FSharp.Data and FSharp.Charting nuget package.
In Program.fs import both the packages
open FSharp.Charting
open Fsharp.Data
After the import I am able to use functions provided in FSharp.Data package but not in FSharp.Charting.
NOTE: In case of script (.fsx) file, which created in the same project, I am able to use both after adding their reference.
I just wanted to to know if there are any steps i am missing for adding any reference in a .fs file. If yes then why does it work with respect to FSharp.Data package.
I think if you search SO you'll find a few examples of displaying charts with FSharp.Charting. It's not exactly clear what sort of error are you getting. Assuming you are on Windows this should work:
open FSharp.Charting
open System
[<STAThread>]
[<EntryPoint>]
let main argv =
Chart.Line [ for x in 0 .. 10 -> x, x*x ] |> Chart.Show
printfn "%A" argv
0 // return an integer exit code
You will need to add references to System.Windows.Forms, System.Windows.Forms.DataVisualization and System.Drawing.

Should it be possible to put the #load directive inside #if directive in F# fsx file?

I have a fsx file where I try to do this
#if DEV
#load "MyFile.fs"
#endif
// Later in the file
#if DEV
callSomethingFromMyFile()
#endif
The callSomethingFromMyFile() works if I remove the #if DEV ... #endif around the #load directive.
I realize that this might be a weird thing, but it is because I'm using fable to compile to F# to js, and if I want to exclude a file when production "build" to reduce js file size.
In regular F# scripts it is possible, it seems like fable doesn't handle it.
To verify that it works in regular F# I created the following to files:
MyModule.fs:
module MyModule
type A = {b: string}
script.fsx:
#if DEV
#load "./MyModule.fs"
#endif
#if DEV
open MyModule
printfn "Hello A: %A" {b = "yolo"}
#endif
printfn "Done"
running fsharpi --define:DEV --exec script.fsx works as expected. I expect fsi on Windows to work as well.
Ive fixed this in the following PR: https://github.com/fable-compiler/Fable/pull/429
You can currently pass defines to the interactive checker its just that for fsx files this was not currently being done.
"
Some directives are available when you are executing scripts in F# Interactive that are not available when you are executing the compiler. The following table summarizes directives that are available when you are using F# Interactive.
"
https://learn.microsoft.com/en-us/dotnet/articles/fsharp/tutorials/fsharp-interactive/index#differences-between-the-interactive-scripting-and-compiled-environments
The table then lists #load amongst others. Not entirely clear that text (compiler vs. preprocessor), but it also kind of makes sense...

#load a package in F# interactive (FSharpChart.fsx)

Hi i'm a noob and asking this newbie question, please forgive me.
I've installed successfully FSharpChart in my local directory
...
Added package 'MSDN.FSharpChart.dll.0.60.0' to folder 'C:\Users\Fagui\Documents\GitHub\Learning Fsharp\Expert in F\packages'
Added package 'MSDN.FSharpChart.dll.0.60.0' to 'packages.config'
Successfully installed 'MSDN.FSharpChart.dll 0.60.0' to Expert in F
now, if i do
#load "FSharpChart.fsx";;
^^^^^^^^^^^^^^^^^^^^^^^
stdin(4,1): error FS0078: Unable to find the file 'FSharpChart.fsx' in any of
C:\Users\Fagui\AppData\Local\Temp
additional info:
inside this folder, i see a nupkg file, and a lib directory
Inside the lib directory, there is a dll, and a pdf file,
but i don't see any .fsx file.
basically, F# has installed the package in the active folder for the current project, and F#interactive is in another folder ?? bit strange ?
should i install another time the package ? or what is the way around it ?
thanks
UPDATE:
i don't know why, but apparently when i installed the FSharpChart package, I only got the dll, no fsx file
i managed to load it doing
#I #"C:\Users\Fagui\Documents\GitHub\Learning Fsharp\Expert in F"
#r #"packages\MSDN.FSharpChart.dll.0.60\lib\MSDN.FSharpChart.dll";;
unfortunately, typing the script in F# interactive
open MSDN.FSharp.Charting
let rnd = System.Random()
let rand() = rnd.NextDouble()
let randomPoints = [for i in 0 .. 1000 -> 10.0 * rand(), 10.0 * rand()]
randomPoints |> FSharpChart.Point;;
doesn't yield any chart, but just returns a list
val rnd : Random
val rand : unit -> float
val randomPoints : (float * float) list =
[(9.765916457, 2.272289941); (0.8211438594, 1.625466995);
...
(7.783786034, 7.572208311); (6.497914692, 3.66987128); ...]
val it : ChartTypes.PointChart
this may be due to the fact that the library is not supported anymore, and that i should use a newer library like Thomas Petricek indicated.
So, i did manage to install FSharp.Charting instead
let rnd = System.Random()
let rand() = rnd.NextDouble()
let randomPoints = [for i in 0 .. 1000 -> 10.0 * rand(), 10.0 * rand()]
randomPoints |> Chart.Point;;
and it did work
There is a newer version of the FSharpChart.fsx library which is called F# Charting, so first of all, I would recommend using this newer library instead (the API is quite similar, but F# Charting has a number of improvements).
The documentation for F# Charting also has a detailed page on referencing the library.
Typically, when you reference the library using NuGet, you'll need to specify relative reference:
// On Mac OSX use packages/FSharp.Charting.Gtk.0.90.13/FSharp.Charting.Gtk.fsx
#load "packages/FSharp.Charting.0.90.13/FSharp.Charting.fsx"
Where 0.90.13 is the version of the library that you got from NuGet (you may need to check the folder name - the path references in #load are relative to the place where your script lives).
Yes, F# Interactive is independent of the current project.
Use:
#load #"C:\Users\Fagui\Documents\GitHub\Learning Fsharp\Expert in F\packages\FSharpChart.fsx";;
Also you can use the #I directive if you need to reference assemblies of a specific folder, see the reference.

Fsx execution path

I have a c# .net library I am looking to use within FSI/FSX. As part of the initialization of the .net lib, by default it expects and references a custom config file (MyAppConfig.xml) which loads various things before it can be used. When using it in c# it gets copied to the bin folder and the app by default expects it to be there and references it there unless there is a specific entry in the app.config to tell it otherwise. (I should add that it does it all by convention rather than injecting a path + filename, as per NLog, say)
I have an f# source file in a console app which will execute this initialization find, but I can't quite work out how to achieve this with FSI/FSX.
So my program.fs looks simply like
open System
open myApp
module Program =
[<EntryPoint>]
let Main(args) =
myApp.Initialization.Load() // references MyAppConfig.xml
Console.WriteLine("do my stuff!")
Console.ReadLine() |> ignore
0
If I try and do the same in FSI or using FSX, I have
#r #"E:\...path to MyApp...\MyApp.dll"
#I #"E:\...path to MyAppConfig.xml ..."
Environment.CurrentDirectory <- #"E:\...path to MyAppConfig.xml ..."
myApp.Initialization.Load() |> ignore // fails ... can't find MyAppConfig.xml
//do my stuff
I suspect that I've not got the paths quite right.
I'd be grateful of a steer
EDIT:
So I've managed to attach a debugger to the c# lib and see where it is looking for the config file - turns out it is "c:\Program Files\Microsoft F#\v4.0\" ( System.AppDomain.CurrentDomain.BaseDirectory) which again shows I've not quite understood how to tell FSI/FSX to use a particular path. If I copy the config file (MyAppConfig.xml) to that location it works fine.
Many thx
S
I'm not sure of the implications, but one possiblity might be temporarily changing the app base:
let origAppBase = AppDomain.CurrentDomain.BaseDirectory
AppDomain.CurrentDomain.SetData("APPBASE", "path_to_MyAppConfig.xml")
myApp.Initialization.Load() |> ignore
AppDomain.CurrentDomain.SetData("APPBASE", origAppBase) //restore original app base

Using NLog with F# Interactive in Visual Studio - Need documentation

I have a need to capture the input and output of F# functions when using F# Interactive. I am able to get NLog to work just fine when the program is run under Visual Studio using F5 or Ctrl-F5. Also the same methods that contain statements to output to the log work just fine and are called when invoked via F# Interactive; just nothing in the log file.
I also tried the following with F# Interactive to setup references to NLog and still nothing in the log when run from F# Interactive.
#I #"..\packages\NLog.2.0.0.2000\lib\net40"
#r #"NLog.dll"
And I even found this which led me to try each of these
NLog.Config.SimpleConfigurator.ConfigureForConsoleLogging()
NLog.Config.SimpleConfigurator.ConfigureForFileLogging(<full file name>)
and still nothing in the log file.
Anyone know if Nlog can be used with F# Interactive?
If so, how is it done?
EDIT
I was able to get NLog to work with fsi.exe when run as a stand alone. So now the problem appears to be getting NLog to find the config file because NLog cannot find the config file starting from the location of fsi.exe for Visual Studio. Looking at using NLog.dll.nlog in the NLog.dll directory.
The Problem
The problem with using NLog from F# Interactive is that NLog thinks that the Temp directory is where to find NLog.config and never succeeds. The way around this is to programmatically locate NLog.config for NLog.
Things to know to solve this problem:
When you run F# Interactive from within Visual Studio, it sets the current working directory to a temp file.
> System.Environment.CurrentDirectory;;
val it : string = "C:\Users\Eric\AppData\Local\Temp"
NLog logging requires three components:
a. reference to NLog.dll.
b. configuration file.
c. calls to a logger method from code.
NLog can be configured in many ways, both programmatically and using config files.
AppData is a hidden folder. Guess what that means when using Windows Explorer.
To get the location of the application with F# Interactive within Visual Studio you need __SOURCE_DIRECTORY__. See F# Spec 3.11 Identifier Replacements
NLog.conf can use a full file path. Obvious but necessary.
NLog file targets have an autoFlush option.
NLog can be installed into a Visual Studio project using NuGet.
Most of the info here comes from NLog Wiki.
Instead of jumping right into the F# Interactive solution, the following progression will be used because a DLL will need to be created to setup and hold the functions for use with NLog from F# Interactive.
Create a solution with three projects and install NLog.
Solution Name: NLogExample
Project 1 - Library, Name: Log - holds extension functions that call NLog
Project 2 - Library, Name: MyLibrary - used to generate a demo DLL that uses Log functions.
Project 3 - Console Application, Name: Main - used to generate a demo EXE that uses Log functions.
a. Manually create NLog.config
b. Access NLog.config from as a running project
c. Log a message to the file
a. Programmatically create a configuration
b. Create a configuration for a running project and log a message to the file
Create a configuration and log a message to the file using F# Interactive
1. Create a solution with three projects and install NLog
Using Visual Studio create the three projects.
Install NLog for all three projects.
2.a. Manually create NLog.config
Note: For these examples to work when __SOURCE_DIRECTORY__;; is run from F# Interactive it should report a directory that is part of the project and NOT the Temp directory.
Note: All the paths in this answer are relative to the solution directory.
When you see <Solution directory> substitute in your actual solution directory.
Path: <Solution director>\NLog.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
throwExceptions="true">
<targets>
<target xsi:type="File"
name="file"
fileName="<Solution directory>\log.txt"
autoFlush="true"
/>
</targets>
<rules>
<logger name="*"
minlevel="Trace"
writeTo="file"
/>
</rules>
</nlog>
Note: Remember to change <Solution directory> to an actual path and set autoFlush="true"
Note: Adding NLog.config to the solution makes it easier to view/modify the file.
2.b. Access NLog.config from as a running project
In Log.Library1.fs
namespace Log
module MyLog =
let configureNLog () =
let projectPath = __SOURCE_DIRECTORY__
let soulutionPath = projectPath + "\.."
let configPath = soulutionPath + #"\NLog.config"
let xmlConfig = new NLog.Config.XmlLoggingConfiguration(configPath)
NLog.LogManager.Configuration <- xmlConfig
let NLogConfigToString () =
let targets = NLog.LogManager.Configuration.AllTargets
let out = ""
let out = Seq.fold (fun out target -> out + (sprintf "%A\n" target)) out targets
let rules = NLog.LogManager.Configuration.LoggingRules
let out = Seq.fold (fun out rule -> out + (sprintf "%A\n" rule)) out rules
out
let printNLogConfig () =
Printf.printfn "%s" (NLogConfigToString ())
and for the Log project add a reference to System.XML
In Main.Program.fs
open Log
[<EntryPoint>]
let main argv =
MyLog.configureNLog ()
MyLog.printNLogConfig ()
0 // return an integer exit code
and for the Main project add a reference to the Log project and set the Main project as the startup project.
When run this should output to the console:
File Target[file]
logNamePattern: (:All) levels: [ Trace Debug Info Warn Error Fatal ] appendTo: [ file ]
2.c. Log a message to the file
In Log.Library1.fs
namespace Log
open NLog
module MyLog =
let configureNLog () =
let projectPath = __SOURCE_DIRECTORY__
let soulutionPath = projectPath + "\.."
let configPath = soulutionPath + #"\NLog.config"
let xmlConfig = new NLog.Config.XmlLoggingConfiguration(configPath)
NLog.LogManager.Configuration <- xmlConfig
let NLogConfigToString () =
let targets = NLog.LogManager.Configuration.AllTargets
let out = ""
let out = Seq.fold (fun out target -> out + (sprintf "%A\n" target)) out targets
let rules = NLog.LogManager.Configuration.LoggingRules
let out = Seq.fold (fun out rule -> out + (sprintf "%A\n" rule)) out rules
out
let printNLogConfig () =
Printf.printfn "%s" (NLogConfigToString ())
let evalTracer = LogManager.GetLogger("file")
In Main.Program.fs
open Log
open Library1
[<EntryPoint>]
let main argv =
MyLog.configureNLog ()
MyLog.printNLogConfig ()
// Add as many of these as needed
MyLog.evalTracer.Trace("In Main #1.")
MyFunctions.test001 ()
0 // return an integer exit code
and for the Main project add a reference to the MyLibrary project.
In MyLibrary.Library1.fs
namespace Library1
open Log
module MyFunctions =
let test001 () =
MyLog.evalTracer.Trace("In Library #1.")
and for the MyLibrary project add a reference to the Log project.
When run the log file log.txt should contain something similar to:
2016-03-28 11:03:52.4963|TRACE|file|In Main #1.
2016-03-28 11:03:52.5263|TRACE|file|In Library #1
3.a. Programmatically create a configuration
If a NLog.config file exist delete it to verify that the code created a new configuration but did not create a file.
To set the configuration programmatically using F# you need to know:
This FileName string is a layout which may include instances of layout renderers. This lets you use a single target to write to multiple files.
SimpleLayout - Represents a string with embedded placeholders that can render contextual information.
To Log.Library1.fs add
let configureNLogPrgramatically () =
let config = new NLog.Config.LoggingConfiguration()
let fileTarget = new NLog.Targets.FileTarget()
let projectPath = __SOURCE_DIRECTORY__
let soulutionPath = projectPath + "\.."
let filePath = soulutionPath + #"\log.txt"
let layout = new NLog.Layouts.SimpleLayout(filePath)
fileTarget.Name <- "file"
fileTarget.FileName <- layout
fileTarget.AutoFlush <- true
config.AddTarget("file", fileTarget)
let rule1 = new NLog.Config.LoggingRule("*",NLog.LogLevel.Trace,fileTarget)
config.LoggingRules.Add(rule1)
NLog.LogManager.Configuration <- config
3.b. Create a configuration for a running project and log a message to the file
In Main.Program.fs
open Log
open Library1
[<EntryPoint>]
let main argv =
MyLog.configureNLogPrgramatically ()
MyLog.printNLogConfig ()
// Add as many of these as needed
MyLog.evalTracer.Trace("In Main #1.")
MyFunctions.test001 ()
0 // return an integer exit code
When run the log file log.txt should contain something similar to:
2016-03-28 11:16:07.2901|TRACE|file|In Main #1.
2016-03-28 11:16:07.3181|TRACE|file|In Library #1.
and note that a NLog.config file was NOT created.
4. Create a configuration and log a message to the file using F# Interactive
In MyLibrary.Script.fsx
// print out __SOURCE_DIRECTORY__ to make sure we are not using the Temp directory
printfn __SOURCE_DIRECTORY__
#I __SOURCE_DIRECTORY__
// Inform F# Interactive where to find functions in Log module
#I "../Log/bin/Debug/"
#r "Log.dll"
open Log
// Functions in Log module can now be run.
MyLog.configureNLogPrgramatically ()
MyLog.printNLogConfig ()
// Inform F# Interactive where to find functions in MyLibrary module
#I "../MyLibrary/bin/Debug/"
#r "MyLibrary.dll"
open Library1
// Functions in MyLibrary module can now be run.
MyFunctions.test001 ()
When the script is executed with F# Interactive
Microsoft (R) F# Interactive version 14.0.23413.0
Copyright (c) Microsoft Corporation. All Rights Reserved.
For help type #help;;
>
<Solution directory>\MyLibrary
val it : unit = ()
--> Added <Solution directory>\MyLibrary' to library include path
--> Added <Solution directory>\MyLibrary\../Log/bin/Debug/' to library include path
--> Referenced <Solution directory>\MyLibrary\../Log/bin/Debug/Log.dll'
File Target[file]
logNamePattern: (:All) levels: [ Trace Debug Info Warn Error Fatal ] appendTo: [ file ]
--> Added <Solution directory>\MyLibrary\../MyLibrary/bin/Debug/' to library include path
--> Referenced <Solution directory>\MyLibrary\../MyLibrary/bin/Debug/MyLibrary.dll'
val it : unit = ()
>
The log file log.txt should contain something similar to:
2016-03-28 11:42:41.5417|TRACE|file|In Library #1.
Also, this will log while you still have an active F# Interactive session, so you can peek at the log between executing commands.

Resources