F# Module/Namespace Error - f#

My first program with F#.
I have one file like so:
namespace LanguageMapper.Data
#if INTERACTIVE
#r "System.Data"
#r "System.Data.Linq"
#r "FSharp.Data.TypeProviders"
#endif
open System.Data
open System.Data.Linq
open Microsoft.FSharp.Data.TypeProviders
module Data =
// You can use Server Explorer to build your ConnectionString.
type SqlConnection = Microsoft.FSharp.Data.TypeProviders.SqlDataConnection<ConnectionString = #"connstring">
let db = SqlConnection.GetDataContext()
Then i have another file like so
namespace LanguageMapper.Program
open Data
module Program =
[<EntryPoint>]
let main argv =
let getLocale x =
match x with
| [|"live"|] -> "live"
| [|"dev"|] -> "dev"
| _ -> "local"
Over top of the open Data i get a red squiggly in VS telling me:
"Error 1 This declaration opens the namespace or module
'Microsoft.FSharp.Data' through a partially qualified path. Adjust
this code to use the full path of the namespace. This change will make
your code more robust as new constructs are added to the F# and CLI
libraries."
What am i doing wrong? I just want to reference one file from the other.

You need to open the module using its fully qualified name, that is including its namespace. So in LanguageMapper.Program you need to open LanguageMapper.Data.Data (only the last bit is the module name).
The Compiler is complaining on your open definition because it only specifies to open a namespace or module named Data - and it finds one in Microsoft.FSharp.Data, probably because there are some 'automatic' opens for the Microsoft.FSharp namespaces.

Related

Can I create a conditional literal?

In order to create a Json provider I need to pass a literal with the path. There are several people working on the project from different locations, and the paths are different in each case. (Actually only the beginning of each path). I tried to create a literal with pattern matching but the compiler does not accept it. Is there another way to do this?
My failed attempt is below:
open FSharp.Data
[<Literal>]
let bitbucketRoot = // Error message: This is not a valid constant expression
let computerName = Environment.MachineName
match computerName with
| "DESKTOP-G3OF32U" -> "C:\\Users\\Fernando"
| "HPW8" -> #"H:\Dropbox\"
| _ -> failwith "Unknown computer"
[<Literal>] // Error message: This is not a valid constant expression
let projDataPath = bitbucketRoot + #"Bitbucket\VSProjects\Fractal10\Fractal10\data\"
[<Literal>] // Error message: This is not a valid constant expression
let jsonPath = projDataPath + "fractal.json"
type PathInfo = JsonProvider<Sample=jsonPath>
I would advise that you store it in source control and make it a path relative to your project root, assuming you are working out of a common source control repository.
Either that, or host the sample on a public URL. (I wouldn't actually recommend this because including it in your source repository allows versioning and doesn't publicly expose your data)
You cannot create a conditional literal as the other comments point it out. However this is a fairly frequent use case and the way to deal with it is as follows:
#r #"..\packages\FSharp.Data\lib\net40\FSharp.Data.dll"
open FSharp.Data
open System
open System.IO
[<Literal>]
let JsonSource = __SOURCE_DIRECTORY__ + #"\test.json"
type JSonType = JsonProvider<JsonSource>
let json1 = JSonType.GetSamples()
let anotherPath = #"C:\tmp"
let anotherJson = anotherPath + #"\test.json"
let json2 = JSonType.Load(anotherJson)
The __SOURCE_DIRECTORY__ directive will point to the project root (just display it in the REPL) and then you can add the filename to it and make that a literal. If you check in this file into a git repo, then everyone who checks it out can have it in a relative path, and you can refer it when generating the type. When actually using the type or referring to the full file you can just use the .Load() method to load any file, and this doesn't have to be a literal.
There is actually a second way, which could work for you depending on the circumstances, compile a sample, and distribute it as a .dll. You can refer to this and use it directly without having access to the actual file. Please see the Using the JSON Provider in a Library section at the end of the documentation.
I have not tried referring to the json in a config file, it might also be possible.

Referencing external assembly fails

I can't get referencing a private assembly working. I've followed the documentation, but it still fails with the error message:
2016-09-29T19:43:08.615 startup(2,1): error FS82: Could not resolve this reference. Could not locate the assembly "Backend.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245)
Here is the run.fsx file:
#r "Backend.dll"
open System
open System.IO
open System.Net
open System.Net.Http.Headers
open System.Collections.Generic
open CoP
let createResponse json =
let responseJson = Request.handleJson json
let response = new HttpResponseMessage()
response.Content <- new StringContent(responseJson)
response.StatusCode <- HttpStatusCode.OK
response.Content.Headers.ContentType <- MediaTypeHeaderValue("application/json")
response
let Run (req: HttpRequestMessage) =
async {
let! json = req.Content.ReadAsStringAsync()
return createResponse json
} |> Async.StartAsTask
I've also placed the Backend.dll in a bin folder inside the same folder as the function.
What am I missing?
Looks like you ran into a bug with private assembly resolution in the Azure Functions F# implementation.
I've opened this issue for tracking and will have a fix included in the next release:
https://github.com/Azure/azure-webjobs-sdk-script/issues/733
In the meantime, you should be able to reference your private assembly by using:
#r "bin/Backend.dll"
Hope this helps!
If that was a question about .fsx scripts alone, I'd say you're missing the part where you tell FSI where to look for the dll to reference:
#I "bin"
#r "BackEnd.dll"
Is there anything Azure does to put the .\bin folder within the context reachable by #r directive?

Use a variable as the path to a file referenced by an F# type provider

How do I pass a variable path to a type provider?
I have a file that I want to load using the SAS type provider that is in different locations on different PCs.
For example, I would like to do something like:
[<Literal>]
let saspath =
match System.Environment.MachineName with
| "a" -> "c:/sas.sas7bdat"
| "b" -> "d:/sas.sas7bdat"
let sasfile = new SasFileTypeProvider<saspath>()
But this is not valid. It's related to this Type provider and static argument in F# and For an F# Type Provider, how do I make a relative path work as a static parameter?, but I do not have the option of using relative paths.
Unfortunately, this is not easily possible. Parameters passed to type providers have to be (syntactically) constants. There are various workarounds.
Use relative path. Can you change the code to use a relative path that will always work? This would be ideal - assuming the type provider supports this.
Improve the type provider. You could send a PR to the SAS type provider so that it can take the file name from an local config file or and environment variable. Many of the SQL type providers do this, because it is a good solution if you need some configuration.
Generate file with a constant. This is a bit of a hack, but you can generate a separate fsx file with the constant and #load it. You'll get red squigglies in the rest of your code (until you run the first part of the script), but if you're happy with that:
open System.IO
let text =
match System.Environment.MachineName with
| "a" -> "module Constants\n[<Literal>] let path = \"c:/sas.sas7bdat\""
| "b" -> "module Constants\n[<Literal>] let path = \"c:/sas.sas7bdat\""
File.WriteAllText("C:/temp/constants.fsx", text)
;;
#load "C:/temp/constants.fsx"
let sasfile = new SasFileTypeProvider<Constants.path>()

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