I am reading XML File in FAKE script using XMLHelper.XMLRead but it is throwing an error i.e.
The type '(string -> string ->seq<string>)' is not a type whose value can be enumerated with this syantax , i.e. is not compatible with either seq<_>,IEnumerable<_> or IEnumerable and does not have a GetEnumerator method
Below Is my code :
let x = XMLHelper.XMLRead true "D:/test/Version.Config" "/version/major/minor"
Target "New" (fun _ ->
for i in x do
printf "%s" i
)
If you look at the API documentation for XMLHelper, you'll see that the function signature for XMLRead looks like this:
failOnError:bool -> xmlFileName:string -> nameSpace:string -> prefix:string -> xPath:string -> seq<string>
It looks like you're specifying the failOnError, xmlFileName and nameSpace parameters*, but you didn't specify the last two string parameters. Since F# uses partial application, that means that what you're getting back from your XMLRead invocation is a function that is waiting for two more string parameters (hence the string -> string -> (result) function signature in the error message you got).
* You probably intended for "/version/major/minor" to fill the xPath parameter, but F# applies parameters in the order given, so it filled the third parameter, which was nameSpace.
To fix this, specify all the parameters that XMLRead expects. I've looked at the XMLRead source, and the nameSpace and prefix parameters should be empty strings if you're not using XML namespaces in your input document. So what you want is:
let x = XMLHelper.XMLRead true "D:/test/Version.Config" "" "" "/version/major/minor"
Target "New" (fun _ ->
for i in x do
printf "%s" i
)
BTW, now that I've looked at your other question, I think you'll want the XMLHelper.XMLRead_Int function:
let minorVersion =
match XMLHelper.XMLRead_Int true "D:/test/Version.Config" "" "" "/version/major/minor" with
| true, v -> v
| false, _ -> failwith "Minor version should have been an int"
Once your code gets past that line, either you have an int in minorVersion, or your build script has thrown an error and exited so that you can fix your Version.Config file.
Related
Right now, when I have a Result type, it seems like I need to do a match to find if it is ok, or not.
So, I am trying to simplify this to be able to use if, like that:
let a : Result<string, string> = ...
if a.IsOk then ....
Here is my attempt
[<AutoOpen>]
module ResultHelper =
type Result with
member this.IsOk =
match this with
| Ok _ -> true
| Error _ -> false
but this will not compile.
Using:
type Result<_, _> with
doesn't help either
How can I achieve this?
The correct syntax is to give the type parameters names. As a bonus, this will allow you to refer to them in the code if you need to.
type Result<'a, 'b> with
member this.IsOk =
match this with
| Ok _ -> true
| Error _ -> false
P.S. For the future, please note that "will not compile" is not a good description of a problem. Please make sure to always include the full error message.
I feel like a total noob for having to ask this but it's got me stumped.
I set a format string like this:
let fs = "This is my format test %s"
Then I attempt to use it like so:
let s = sprintf fs "testing"
When I do so I get this error:
//stdin(26,17): error FS0001: The type 'string' is not compatible with the type 'Printf.StringFormat<('a -> 'b)>'
So I then tried this:
let s = sprintf (Printf.StringFormat fs) "test"
to which the REPL responded:
//stdin(28,18): error FS1124: Multiple types exist called 'StringFormat', taking different numbers of generic parameters. Provide a type instantiation to disambiguate the type resolution, e.g. 'StringFormat<_>'.
So I then tried this:
let s = sprintf (Printf.StringFormat<string> fs) "test"
And I get this:
//stdin(29,18): error FS0001: The type ''a -> 'b' does not match the type 'string'
Am I missing something painfully obvious? This is using F# 3.0 on the Mac from the Xamarin Studio F# Interactive Window.
So you actually need to create a StringFormat which has a function type as follows
> sprintf (Printf.StringFormat<string->string>("Hello %s")) "World";;
val it : string = "Hello World"
In Section 6.3.16 of the spec, an example of this is shown.
Is it best (I'm aware of that there's no silver bullet, but there may be some advantage by using one over the other) - to log in the calling function, or the function calling it?
Examples:
Approach 1
module MongoDb =
let tryGetServer connectionString =
try
let server = new MongoClient(connectionString).GetServer()
server.Ping()
Some server
with _ -> None
Usage:
match MongoDb.tryGetServer Config.connectionString with
| None ->
logger.Information "Unable to connect to the database server."
// ... code ...
| Some srv ->
logger.Information "Successfully connected to the database server."
// ... code ...
Approach 2
module MongoDb =
let tryGetServer connectionString =
try
let server = new MongoClient(connectionString).GetServer()
server.Ping()
Some server
with _ -> None
let tryGetServerLogable connectionString logger =
match tryGetServer connectionString with
| None ->
logger.Information "Unable to connect to the database server."
None
| Some srv ->
logger.Information "Successfully connected to the database server."
Some srv
Usage:
match MongoDb.tryGetServerLogable Config.connectionString logger with
| None ->
// ... code ...
| Some srv ->
// ... code ...
Approach 2 is better. In general, logging is a Cross-Cutting Concern, so it's best decoupled from implementation details. Cross-Cutting Concerns are best addressed via Composition; in OOD, this can be done with Decorators or Interceptors. In FP, we can sometimes learn from OOD, because many of the principles translate from objects to closures.
However, instead of using Approach 2 above verbatim, I'd rather prefer something like this:
module MongoDb =
let tryGetServer connectionString =
try
let server = MongoClient(connectionString).GetServer()
server.Ping()
Some server
with _ -> None
Notice that the MongoDb module has no knowledge of logging. This follows the Single Responsibility Principle, which is also valuable in Functional Programming.
The tryGetServer function has this signature:
string -> MongoServer option
Now you can define a logging function, totally decoupled from the MongoDb module:
module XyzLog =
type Logger() =
member this.Information message = ()
let tryGetServer f (logger : Logger) connectionString =
match f connectionString with
| None ->
logger.Information "Unable to connect to the database server."
None
| Some srv ->
logger.Information "Successfully connected to the database server."
Some srv
Here, you can imagine that XyzLog is a placeholder for a particular logging module, utilising Serilog, Log4Net, NLog, your own custom logging framework, or similar...
The f argument is a function with the generic signature 'a -> 'b option, of which MongoDb.tryGetServer is a specialization.
This means that you can now define a partially applied function like this:
let tgs = XyzLog.tryGetServer MongoDb.tryGetServer (XyzLog.Logger())
The function tgs also has the signature
string -> MongoServer option
So any client that depends on a function with this signature can use MongoDb.tryGetServer or tgs interchangeably, without knowing the difference.
This enables you to change you mind or refactor both MongoDb.tryGetServer and your logging infrastructure independently of each other.
There is a more general way to implement cross-cutting concerns such as logging with a functional language. The example I have is from an async service library (think ASP.NET MVC and ActionFilters) but the same applies here as well. As stated by Mark, the function tryGetServer is of type string -> MongoServer option. Suppose we abstract it to:
type Service<'a, 'b> = 'a -> 'b option
Then suppose we also have a type as follows:
type Filter<'a, 'b> = 'a -> Service<'a, 'b> -> 'b option
A filter is a function which takes a value 'a and a Service<'a, 'b> and then returns a value of the same type as the Service<'a, 'b> function. The simplest filter is a function which simply passes the 'a it receives directly to the service and returns the value it gets from the service. A more interesting filter would be a function which prints a log message after receiving output from the service.
let loggingFilter (connStr:string) (tryGetServer:string -> MongoServer option) : Filter<string, MongoServer option> =
let server = tryGetServer connStr
match tryGetServer connStr with
| Some _ ->
logger.Information "Successfully connected to the database server."
server
| None ->
logger.Information "Unable to connect to the database server."
server
Then if you have the following defined:
type Continuation<'a,'r> = ('a -> 'r) -> 'r
module Continuation =
let bind (m:Continuation<'a, 'r>) k c = m (fun a -> k a c)
module Filter =
/// Composes two filters into one which calls the first one, then the second one.
let andThen (f2:Filter<_,,_>) (f1:Filter<_,_>) : Filter<_,_> = fun input -> Continuation.bind (f1 input) f2
/// Applies a filter to a service returning a filtered service.
let apply (service:Service<_,_>) (filter:Filter<_,_>) : Service<_,_> = fun input -> filter input service
/// The identity filter which passes the input directly to the service and propagates the output.
let identity : Filter<_,_> = fun (input:'Input) (service:Service<_,_>) -> service input
You can apply a filter to a service and get back the original service type but which now does logging:
let tryGetServerLogable = Filter.apply tryGetServer loggingFilter
Why bother? Well, now you can compose filters together. For example you may add a filter which measures the time it takes to create a connection and you can then combine them using Filter.andThen. The gist I originally made is here.
Another approach to consider is the use of a writer monad. With the writer monad, you can defer the actual printing of log messages until some well defined point, but still have similar composition characteristics.
I feel like a total noob for having to ask this but it's got me stumped.
I set a format string like this:
let fs = "This is my format test %s"
Then I attempt to use it like so:
let s = sprintf fs "testing"
When I do so I get this error:
//stdin(26,17): error FS0001: The type 'string' is not compatible with the type 'Printf.StringFormat<('a -> 'b)>'
So I then tried this:
let s = sprintf (Printf.StringFormat fs) "test"
to which the REPL responded:
//stdin(28,18): error FS1124: Multiple types exist called 'StringFormat', taking different numbers of generic parameters. Provide a type instantiation to disambiguate the type resolution, e.g. 'StringFormat<_>'.
So I then tried this:
let s = sprintf (Printf.StringFormat<string> fs) "test"
And I get this:
//stdin(29,18): error FS0001: The type ''a -> 'b' does not match the type 'string'
Am I missing something painfully obvious? This is using F# 3.0 on the Mac from the Xamarin Studio F# Interactive Window.
So you actually need to create a StringFormat which has a function type as follows
> sprintf (Printf.StringFormat<string->string>("Hello %s")) "World";;
val it : string = "Hello World"
In Section 6.3.16 of the spec, an example of this is shown.
Although it seems lie a simple issue, I am unable to write to a configuration file from an F# console application. My last attempt looks like
let config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal)
// config.AppSettings.SectionInformation.AllowExeDefinition <-ConfigurationAllowExeDefinition.MachineToLocalUser
match self.FileName with
| Some name -> config.AppSettings.Settings.["FileName"].Value <- name
| None -> ()
config.Save(ConfigurationSaveMode.Modified)
I got all sorts of errors. The one corresponding to this code is
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Configuration.ConfigurationElement.SetPropertyValue(ConfigurationProperty prop, Object value, Boolean ignoreLocks) ...
There is no good documentation in F# and I find it hard to follow C#/VB documentation.
Any suggestions?
You have to check for null and either update or add accordingly.
Something like this:
let config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal)
let settings = config.AppSettings.Settings
let set (s:KeyValueConfigurationCollection) key value =
match s.[key] with
| null -> s.Add(key,value)
| x -> x.Value <- value
match self.FileName with
| Some name -> set settings "Filename" name
| _ -> ()