I am new to F# and I have been pulling my hair out from this question:
The SqlCommandProvider requires a constant string which I am unable to provide by the below function.
Running it in F# interactive returns "This is not a valid constant expression or custom attribute value. "
let textgen (RIC: string, price: float, volume: int, inst: int, time:string) =
let mutable mystring = "INSERT INTO hsbc(RIC, price, volume, type, time) VALUES (st1, st2, st3, st4, st5)"
mystring <- mystring.Replace ("st1", RIC)
mystring <- mystring.Replace ("st2", price.ToString())
mystring <- mystring.Replace ("st3", volume.ToString())
mystring <- mystring.Replace ("st4", inst.ToString())
mystring <- mystring.Replace ("st5", time)
mystring
let sqlcommand1 = textgen("0005.hk",71.2,5000,1,"2019-07-18 10:34:09.193")
type writedata4=SqlCommandProvider<sqlcommand1,connStr>
do
let cmd6 = new writedata4(connStr)
cmd6.Execute()
0
I have done some research and know that this is a problem with sqlcommand1 not being a constant during compile time. So I tried this:
[<Literal>]
let sqlcommand1 = textgen("0005.hk",71.2,5000,1,"2019-07-18 10:34:09.193")
which again returns: "This is not a valid constant expression or custom attribute value"
Any help would be appreciated, many thanks!
Update:
thanks! I adapted the code from #Mark Pattison and this works:
let mywrite(RIC: string, price: float, volume: int, inst: int, time:string) =
use cmd99 = new SqlCommandProvider<"INSERT INTO hsbc(RIC, price, volume, type, time) VALUES (#st1, #st2, #st3, #st4, #st5)", connStr>(connStr)
cmd99.Execute(st1=RIC,st2=price,st3=volume,st4=inst,st5=System.DateTime.Parse time)
|> ignore
however, the official docs does mention:
let cmd = new SqlCommandProvider<const(SqlFile<"GetDate.sql">.Text), connStr>(connStr)
cmd.Execute() |> ignore
Since the command is loaded from an external text, there should be even less compile time certainty and shouldn't work (but it appears to work).
Can you please tell me why? Thanks
The idea is that the command text is a compile-time constant. Clearly your sqlcommand1 can't be, as it's created at runtime by calling textgen.
Here's an example of how to create a parametrised command using #, from the docs:
open FSharp.Data
[<Literal>]
let connectionString =
#"Data Source=.;Initial Catalog=AdventureWorks2012;Integrated Security=True"
do
use cmd = new SqlCommandProvider<"
SELECT TOP(#topN) FirstName, LastName, SalesYTD
FROM Sales.vSalesPerson
WHERE CountryRegionName = #regionName AND SalesYTD > #salesMoreThan
ORDER BY SalesYTD
" , connectionString>(connectionString)
cmd.Execute(topN = 3L, regionName = "United States", salesMoreThan = 1000000M) |> printfn "%A"
//output
//seq
// [("Pamela", "Ansman-Wolfe", 1352577.1325M);
// ("David", "Campbell", 1573012.9383M);
// ("Tete", "Mensa-Annan", 1576562.1966M)]
You can see that the command string itself (starting SELECT...) is a constant string.
Related
I have read about continuations and partial applications; I am also aware of the kprintf function.
But I still don't know how to write something like:
let myPrintFunction format variable_length_arguments_list =
let a = sprintf format variable_length_ argument_list
do other things
what would be the syntax this this?
so I could use it like:
myPrintFunction "%s : %i" "hello" 3
Edit:
This is different than How do I implement a method with a variable number of arguments? because that question is asking how to make a method with a variable number of arguments, but the issue I am facing is to pass that variable number of argument to the next function (sprintf) that takes a variable number of arguments too.
Or, at least that's where I suppose the problem is.
The test code, based on the solution proposed by Scott can be found here: https://dotnetfiddle.net/oCzcS9
I want to demonstrate the ksprintf function, because that one accepts a continuation that will allow you to pass on the resulting string to e.g. a log system.
For the purpose of demonstration, let's first create something that can take a single string as input and pass it on, in this case to the console.
let writeStringToConsole (s: string) = Console.WriteLine ("OUTPUT : " + s)
So now, if writeStringToConsole is all we have, how to we make it accept F# formatting?
let printToConsole format = Printf.ksprintf writeStringToConsole format
Example that demonstrates that it works.
type DU = A | B
let i = 7
let s = "thirteen"
let du = B
printToConsole """an int %i and a string "%s" here""" i s
printToConsole """an int %i and a string "%s" and DU %A here""" i s du
// OUTPUT : an int 7 and a string "thirteen" here
// OUTPUT : an int 7 and a string "thirteen" and DU B here
// Note that OUTPUT is also part of the actual output,
// and it demonstrates how you can add e.g. a timestamp
// or line number or something to the output string, without
// it being part of the formatting.
edit: Some additional notes
The format string must be a literal. That's because the literal string must be read at compile time in order to compute the function that must be returned in order to gobble up whatever values/types that follow the format string.
For example, if you do printToConsole "%i %s %A %A" 7 "x" myType yourType, then you'll see int -> string -> MyType -> YourType in the signature of printToConsole where it's used.
There is a way to use plain strings as format strings with this system, but I don't remember how it's done, and anyway it spoils the type safety. It comes in handy when doing internationalization of strings, and your format strings must come from a resource and not F# source due to external translator services.
edit 2 : Wrap e.g. log system
I created an interface to use for various logging systems, which pretty much share the same features.
type ILogger =
...
abstract member Debugf: StringFormat<'h, unit> -> 'h
abstract member Verbosef: StringFormat<'h, unit> -> 'h
abstract member Infof: StringFormat<'h, unit> -> 'h
abstract member Warningf: StringFormat<'h, unit> -> 'h
abstract member Errorf: StringFormat<'h, unit> -> 'h
abstract member Fatalf: StringFormat<'h, unit> -> 'h
Then an implementation for my currently used logging system looks like this.
type internal SiLogger(session: Session) =
let slogf = Printf.ksprintf
...
interface ILogger with
...
member _.Debugf format = slogf session.LogDebug format
member _.Verbosef format = slogf session.LogVerbose format
member _.Infof format = slogf session.LogMessage format
member _.Warningf format = slogf session.LogWarning format
member _.Errorf format = slogf session.LogError format
member _.Fatalf format = slogf session.LogFatal format
And there is a null logger.
let slogf = Printf.ksprintf
let dummyLog _ = () // The parameter is the title string.
let dummy format = slogf dummyLog format
let getNullLogger () =
{ new ILogger with
...
member _.Debugf format = dummy format
member _.Verbosef format = dummy format
member _.Infof format = dummy format
member _.Warningf format = dummy format
member _.Errorf format = dummy format
member _.Fatalf format = dummy format
...
}
open System
let myPrintFunction (format: Printf.StringFormat<_>) ([<ParamArray>] args) =
let a = sprintf format args
a
myPrintFunction "%s : %i" "hello" 3
To add the PrintF as a member function, this is the closest I could get. As you see, I had to pass the format string separately (in the constructor, or I could have used a property setter). I could find no way to pass the format string as the first parameter of the PrintF function as I could for a free function (see my other answer at https://stackoverflow.com/a/58822618/5652483).
Also, if I uncomment the line this.RaiseSomeEvent msg, then it breaks. So I could find no way to enable the PrintF function to have a side effect.
Hopefully, someone else can solve these issues.
type Foo (format: Printf.StringFormat<_>) =
member this.RaiseSomeEvent msg = printf "%s" msg
member this.PrintF ([<ParamArray>] args) =
let msg = sprintf format args
//this.RaiseSomeEvent msg
msg
let foo = Foo("%s : %i")
foo.PrintF "hello" 3
I want to write an extended version of F# printfn function that prints current time in addition to text. Something like this:
let printimefn fmt =
let time = DateTime.Now
let strtime = sprintf "%02d:%02d:%02d" time.Hour time.Minute time.Second
printfn "%s %A" strtime fmt
Unfortunately this doesn't work as expected. The "fmt" argument loses its type Printf.TextWriterFormat<'T>.
I can force its type by using type annotation:
let printimefn (fmt : Printf.TextWriterFormat<'T>) =
let time = DateTime.Now
let strtime = sprintf "%02d:%02d:%02d" time.Hour time.Minute time.Second
printfn "%s %A" strtime fmt
But then the result of printimefn becomes unit, and not 'T. So it still doesn't work. I wonder what is the right way of to write a custom printfn function.
This can be done with Printf.ksprintf
let printimefn fmt =
let time = DateTime.Now
Printf.ksprintf (
fun s ->
printfn "%02d:%02d:%02d %s" time.Hour time.Minute time.Second s)
fmt
I want to demonstrate this by moving the reading of the time out of the function, for two reasons. Primarily to demonstrate how to add one or more arguments that are not consumed by ksprintf, but also because we should have a function that does not have side effects. If you want the time to be read inside the function, then instead create a wrapper function that reads the time, then calls the following function.
First there are two explicit arguments not passed on to ksprintf. Then follows implicitly the format string and any implicit arguments the format string requires.
let tsprintf (dateTime: DateTime) (tag: string) =
let dateTimeString =
dateTime.ToString (#"yyyy/MM/dd HH:mm:ss",
System.Globalization.CultureInfo.InvariantCulture)
Printf.ksprintf (fun formatString ->
"[" + dateTimeString + " <" + tag + ">] " + formatString)
let testTime = DateTime(1987, 12, 31, 11, 59, 58)
let message = tsprintf testTime "007" "Hello %s!" "James Bond"
message.Dump() // LINQPad output : [1987/12/31 11:59:58 <007>] Hello James Bond!
The InvariantCulture in combination with the chosen time format string makes sure the result you see is exactly what's in the comment at the end, no matter where or what.
The reason I use DateTime.ToString, is that this formats DateTime whichever way you want. Formatting it in ksprintf doesn't seem like a good idea.
The arguments not consumed by ksprintf are explicitly declared in tsprintf. The format string and the following arguments for ksprintf are not declared explicitly, but they follow all explicitly declared arguments.
How do I create some string from a string?
I'm going through the F# Koans tutorials, and am I stuck on this one:
[<Koan>]
let ProjectingValuesFromOptionTypes() =
let chronoTrigger = { Name = "Chrono Trigger"; Platform = "SNES"; Score = Some 5 }
let halo = { Name = "Halo"; Platform = "Xbox"; Score = None }
let decideOn game =
game.Score
|> Option.map (fun score -> if score > 3 then "play it" else "don't play")
//HINT: look at the return type of the decide on function
AssertEquality (decideOn chronoTrigger) (Some "play it")
AssertEquality (decideOn halo) (Some "don't play")
the exception i get is:
You have not yet reached enlightenment ...
Expected: null
But was: <Some(don't play)>
How do I upcast a string to be of type option string?
How do I upcast a string to be of type option string?
Casting has a very specific meaning. What you want to do is wrap your string with an Option, not cast it. To do this, use the Some constructor function:
let x = Some myString //x: string option
However, I don't think that is going to fix assertion error you're getting (at least, not by itself). I don't want to give you the complete answer here (especially since that's not what you're asking and finding the answer is the entire point of doing a Koan) but I will leave this clue as to why you're seeing a null in the assertion:
None |> printfn "Value: %A" // Value: <null>
See Why is None represented as null? for some more information on that behavior.
I need to assign to a byref parameter, but, using F# 4.0 and .NET 4.5.2 on a Windows 8x64 box, I keep getting complaints that This value is not mutable. I can't change the signature, because I'm implementing a COM interface. Minimal broken example:
module Utils =
let bugFix1([<System.Runtime.InteropServices.Out>] ID : System.String byref) = ID <- "Hi!"
let bugFix1([<System.Runtime.InteropServices.Out>] ID : int byref) = ID <- 0
let bugFix1([<System.Runtime.InteropServices.Out>] ID : System.Guid byref) = ID <- System.Guid.NewGuid()
By this value, it definitely means ID, because it doesn't matter what I assign to ID. Note too, that it doesn't matter whether the type is blittable or not, or whether it is heap- or stack-allocated.
Is there some way do declare ID as mutable?
I think you have discovered another bug (or undocumented feature?). This happens simply because your parameter name is capitalized. Surprise! :-)
These variants will work (omitted [<Out>] for brevity):
let bugFix1(id : string byref) = id <- "Hi!"
let bugFix1(iD : string byref) = iD <- "Hi!"
But these will fail:
let bugFix1(Id : string byref) = Id <- "Hi!"
let bugFix1(ID : string byref) = ID <- "Hi!"
I have absolutely no idea why capitalization would matter. I would guess that this never arose before, because parameters always start with a lower-case letter by convention.
I intend to google some more and then file an issue.
I am new to F# and would like to have an advice.
I would like to use the GetDigitValue function.
open System
open System.Drawing
open System.Globalization
let getSubscript ichar =
match ichar with
|1 -> GetDigitValue(843)
| _ -> GetDigitVale(852)
I have the following error: The value or constructor 'getDigitValue" is not defined.
Without further information I can't really tell what you are trying to do.
GetDigitValue is a static method of the CharUnicodeInfo class.
It is used like this:
let testString = "1234567890"
let digitValue = CharUnicodeInfo.GetDigitValue(testString, 3)
This returns the digit value for the 3rd character in the string. It also works with a single character too.
let test = '5'
let digitvalue = CharUnicodeInfo.GetDigitValue(test)
Update:
To get the superscript of a string I think the Numeric value will return this:
let superscriptTwo ="U+00B2"
let numericvalue = CharUnicodeInfo.GetNumericValue(superscriptTwo)
I would like to get the superscript of numbers.
Then I think you want this function that gives a unicode character that is the superscript of the given digit:
let superscriptOf n =
if 0<=n && n<10 then "⁰¹²³⁴⁵⁶⁷⁸⁹".[n] else
invalidArg "n" "Not a single digit number"
Note that F# supports unicode in F# code. You can even use unicode variable names like λ in F# code!