In C# you can write string.Join and String.Join. But in F# you cannot write string.Join but only String.Join. Why is that? Is not string a type alias over .NET String class?
In F# string, depending on where it's used, is a type alias or a function:
string 12 // this stringifies '12'
let f (s: string) = ... // here 'string' is a type alias for 'System.String'
And so static members like Join don't sit on string. They are available via String. If you created your own alias, then you'd get access to the static members via ..
In C#, both string and String refer to the same System.String at all times.
Related
In F# I can combine strings with the + operator as follows:
let myString = "one" + "two"
But when I create a function that will accept two arguments and apply the same operator, and I put it in a module declared ahead of its usage, F# infers the types as integers:
module StringFunctions =
let add str1 str2 =
str1 + str2
Calling the below gives me the build error, "The expressions was expected to have type 'int' but here has type 'string'":
let str3 = StringFunctions.add "three" "four"
Why does F# infer this as int? I assume that it can't be a generic method because not all types will implement the + operator, but why does it assume int?
The short version is, "Because that's how F# is designed".
The longer version is that if a function's type can't be generic (and you're correct that this one can't since not all types will necessarily inplement the + operator), then F# has to pick a real type. The most common use case for the + operator is adding integers, so F# defaults to assuming that + is the integer-addition operator unless you tell it otherwise.
One way you could tell it otherwise is to put in a type annotation:
let add (str1 : string) str2 = str1 + str2
Note that you don't have to annotate both parameters: knowing that one side of the + operator is a string is enough to let F# infer that the other side must be a string as well.
The other thing you could do is to make the function inline, which will result in two things:
F# will not compile this into a function call, but will actually insert the function's code into the calling locations, and
Because of this, the F# compiler will be able to use different + operators in different calling locations, if needed.
That is, add 3 5 would become 3 + 5 which uses the integer-addition operator, and add "foo" "bar" would become "foo" + "bar" which uses the string-addition overload of +.
The reasons why this works are gone into in more depth in questions like F# generics / function overloading syntax and Use of `inline` in F# (among others), so I won't get into them here.
I have declared some data types in a F# library that I want to fill from C# code. The problem I encountered is that only DU's get "exported" as a class, consider this example file Test.fs:
module Test
type SimpleType = string
type SimpleList = string list
type SimpleDU =
| A
| B
type SimpleRecord = { Text : string }
I was confused at first when just referencing the F# project wouldn't allow me use the SimpleType and SimpleList types in C#. I looked at the resulting F# library with ILDasm and found only code for the SimpleDU and SimpleRecord type which are perfectly accessible in C# code.
Is there a way to "export" non DU types so they are usable in C# or do I have to declare every non DU type as an explicit record?
The definitions of
type SimpleType = string
type SimpleList = string list
are type abbreiviations which are eliminated during type checking and do not create new types. These are described in the specifition:
8.3 Type Abbreviations
Type abbreviations define new names for other types. For example:
type PairOfInt = int * int
Type abbreviations are expanded and erased during compilation and do
not appear in the elaborated form of F# declarations, nor can they be
referred to or accessed at runtime.
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'm trying to define the following type:
type lToken =
LInt of int
| LString of string
| LList of lToken list
| LFunction of string * LList
but I'm getting an error 'LList' is not defined.
Is there a way to do what I'm trying to do - i.e. use the types I'm defining inside their own type definition?
Thanks
As others pointed out, LList is not a name of a type, but just a name of discriminated union's constructor. In F#, cases of discriminated union happen to be compiled as .NET types, but that's just an implementation detail and you cannot refer to the generated types.
If you want to declare LFunction as a cast that consists of string and a LList then you can either expand the definition (as Brian and Marcelo suggest) or declare a new type (using type .. and to declare recursive types):
type List = Token list
and Token =
| LInt of int
| LString of string
| LList of List
| LFunction of string * List
PS: If you're writing F# then I'd recommend following standard naming guidelines and using PascalCase with a more descriptive name for type names. What does "l" stand for? Could you expand it (thanks to type inference, you won't need to write the type name anyway).
LList is a constructor, not a type. Just use the associated type directly:
...
| LFunction of string * (lToken list)
(My ML is very rusty; I'm not sure whether the parentheses are right.)
LList is not the name of a type; lToken is. Perhaps you want lToken list there instead?
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