I am writing my first F# library
I am trying to use string.Format and it complains that no such function exists.
Is it not available or am I doing something wrong?
If you want to avoid using the full name, you can use open in F#:
open System
let s = String.Format("Hello {0}", "world")
This should work in both F# interactive (enter the open clause first) and in normal compiled applications. The key thing is that you must write String with upper-case S. This is because string in C# isn't a usual type name - it is a keyword refering to the System.String type.
Alternatively, you could also take a look at the sprintf function. It is an F#-specific alternative to String.Format which has some nice benefits - for example it is type checked:
let s = sprintf "Hello %s! Number is %d" "world" 42
The compiler will check that the parameters (string and int) match the format specifiers (%s for string and %d for integers). The function also works better in scenarios where you want to use partial function application:
let nums = [ 1 .. 10 ]
let formatted = nums |> List.map (sprintf "number %d")
This will produce a list of strings containing "number 1", "number 2" etc... If you wanted to do this using String.Format, you'd have to explicitly write a lambda function.
the full name of it is:
System.String.Format
Related
I love the simplicity of types like
type Code = Code of string
But I would like to put some restrictions on string (in this case - do not allow empty of spaces-only strings). Something like
type nonemptystring = ???
type Code = Code of nonemptystring
How do I define this type in F# idiomatic way? I know I can make it a class with constructor or a restricted module with factory function, but is there an easy way?
A string is essentially a sequence of char values (in Haskell, BTW, String is a type alias for [Char]). A more general question, then, would be if it's possible to statically declare a list as having a given size.
Such a language feature is know as Dependent Types, and F# doesn't have it. The short answer, therefore, is that this is not possible to do in a declarative fashion.
The easiest, and probably also most idiomatic, way, then, would be to define Code as a single-case Discriminated Union:
type Code = Code of string
In the module that defines Code, you'd also define a function that clients can use to create Code values:
let tryCreateCode candidate =
if System.String.IsNullOrWhiteSpace candidate
then None
else Some (Code candidate)
This function contains the run-time logic that prevents clients from creating empty Code values:
> tryCreateCode "foo";;
val it : Code option = Some (Code "foo")
> tryCreateCode "";;
val it : Code option = None
> tryCreateCode " ";;
val it : Code option = None
What prevents a client from creating an invalid Code value, then? For example, wouldn't a client be able to circumvent the tryCreateCode function and simply write Code ""?
This is where signature files come in. You create a signature file (.fsi), and in that declare types and functions like this:
type Code
val tryCreateCode : string -> Code option
Here, the Code type is declared, but its 'constructor' isn't. This means that you can't directly create values of this types. This, for example, doesn't compile:
Code ""
The error given is:
error FS0039: The value, constructor, namespace or type 'Code' is not defined
The only way to create a Code value is to use the tryCreateCode function.
As given here, you can no longer access the underlying string value of Code, unless you also provide a function for that:
let toString (Code x) = x
and declare it in the same .fsi file as above:
val toString : Code -> string
That may look like a lot of work, but is really only six lines of code, and three lines of type declaration (in the .fsi file).
Unfortunately there isn't convenient syntax for declaring a restricted subset of types but I would leverage active patterns to do this. As you rightly say, you can make a type and check it's validity when you construct it:
/// String type which can't be null or whitespace
type FullString (string) =
let string =
match (System.String.IsNullOrWhiteSpace string) with
|true -> invalidArg "string" "string cannot be null or whitespace"
|false -> string
member this.String = string
Now, constructing this type naively may throw runtime exceptions and we don't want that! So let's use active patterns:
let (|FullStr|WhitespaceStr|NullStr|) (str : string) =
match str with
|null -> NullStr
|str when System.String.IsNullOrWhiteSpace str -> WhitespaceStr
|str -> FullStr(FullString(str))
Now we have something that we can use with pattern matching syntax to build our FullStrings. This function is safe at runtime because we only create a FullString if we're in the valid case.
You can use it like this:
let printString str =
match str with
|NullStr -> printfn "The string is null"
|WhitespaceStr -> printfn "The string is whitespace"
|FullStr fstr -> printfn "The string is %s" (fstr.String)
I know I can do this in an imperative manner pretty easily. What I want to figure out is how to do this in a functional manner. I feel like I should be able to accomplish this with either fold or map but I just can't work it out. Its been a long time since I have had education on functional programming.
Say I have tuples like (arbitrary length):
[|("X", 2);("Y", 3)|]
And a string like:
"X + Y"
I want a result of:
"2 + 3"
Thanks in advance.
As you correctly guessed, you can do this using fold:
[|("X", 2);("Y", 3)|]
|> Seq.fold (fun (str:string) (orig, repl) ->
str.Replace(orig, string repl)) "X + Y"
The idea is that we use the string as the state (initial state is "X + Y"). The fold then iterates over all the replacements and, for every replacement, we return a new string. To do that, we replace the orig value with the new value repl, which is done by str.Replace(orig, string repl).
One thing that makes this tricky is that we need type annotation for str in order to call Replace (without this, F# would not know that str is a string).
Why does a carriage return character not appear in a F# string literal bound like this
let hw = #"hello
world";;
whereas in C# the following string does contain a carriage return character?
var s2 = #"hello
world";
Thanks
In fact, it is quite easy to spot that the behavior of F# and C# compilers do not differ: being run thru FSC the following
[<EntryPoint>]
let main argv =
let s = #"Hello
world"
printfn "%i" s.Length
0
outputs 12 similarly to equivalent C# code.
What differs is, in fact, the treatment of multi-line string literals by FSI: sending lines 3-5 of the above snippet from VS to FSI with Alt+Enter produces output 11, one less for absence of carriage return within the parsed string literal.
Came out this is a known problem, although I didn't manage to google the rationale behind this behavior.
I ran your code from the fsi and it entered the newline.
let hw = #"hello
world";;
printf "%s" hw;;
output
hello
world
I am trying to experiment in F# for one of the utility tools we need, wherein we want to trawl through a folder of xml files and look for a particular tag. If found then insert another similar tag alongwith it. Finally, output all the filenames for which such additional tags have been inserted. But am getting a compilation error, of which I am not able to make a lot of sense.
let configFile =
Directory.GetFiles(Path.Combine("rootdir", "relativepath"), #"*.xml")
|> Seq.map(fun configFileName ->
let xmlNavigator = XPathDocument(configFileName).CreateNavigator()
let node = xmlNavigator.SelectSingleNode(#"Product/ABc[#type='xyz']")
match node with
| null -> "not configuration present"
| _ ->
let nodeAppender() = node.InsertAfter("<Risk type=""abc1"" methodology=""xyz1""/>")
let parentNode = node.SelectAncestors(XPathNodeType.Root, false)
parentNode.Current.OuterXml)
|> Seq.iter (printfn "%s")
The compilation error is as below:
This value is not a function and cannot be applied
Your string is escaped improperly. It should be:
node.InsertAfter("<Risk type=\"abc1\" methodology=\"xyz1\"/>")
EDIT: Apparently I was typing this as Brian posted his answer. Either escaping each quote char or prefixing with # as-is will work.
It would help to point out what line/column the error location is at.
At a glance, in nodeAppender, it looks like you left off the # on the string literal, which means it is five strings in a row (rather than one string with escaped quotes), which may be the cause of the error.
How is it possible for F# to examine format strings at compile time to determine that x has type int in the following definition?
let foo x = sprintf "%d" x`?
Is this hard-coded into the language or could somebody write their own "my_print" function that uses format strings with a different syntax? For example:
let foo x = my_print "{integer}" x
You can read a bit about it in 6.4.17 ('printf' formats) here, but briefly
it's built into the language
string literals can effectively be 'coerced' into the weird 'Format' type
printf and friends expect a first argument of the Format type, making the coercion happen
The net result is that you can build your own printf-style functions, but must use the same %s formats, since that stuff is built-in.
Here is an example of how you can build your own printf-style functions in F#. You can't change the format specifiers (e.g. "%d"), but you can leverage the existing specifiers to build additional string formatting functions that the compiler will type check.