Why does F# infer int in this function? - f#

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.

Related

Type to represent a string which is not empty or spaces in F#

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)

F# Literal/constant can be composed with strings, but not int?

Why is this OK:
let [<Literal>] hi = "hi"
let [<Literal>] bye = "bye"
let [<Literal>] shortMeeting = hi + bye
...but this is not?
let [<Literal>] me = 1
let [<Literal>] you = 1
let [<Literal>] we = me + you
The third line gives the error:
This is not a valid constant expression
What's up with that?
So the spec / docs are a little unclear, but provide hints.
From the spec (for F# 3.0):
A value that has the Literal attribute is subject to the following
restrictions:
It may not be marked mutable or inline. It may not also have the
ThreadStaticor ContextStatic attributes. The righthand side expression
must be a literal constant expression that is made up of either:
A simple constant expression, with the exception of (), native integer
literals, unsigned native integer literals, byte array literals,
BigInteger literals, and user-defined numeric literals.
OR
A reference to another literal
This seems to suggest that even the combination of strings isn't allowed.
The documentation states that this changed in F# 3.1:
https://msdn.microsoft.com/en-us/library/dd233193.aspx
As of F# 3.1, you can use the + sign to combine string literals. You
can also use the bitwise or (|||) operator to combine enum flags. For
example, the following code is legal in F# 3.1:
Note that integer addition is not on that list

F# operator overloading strange behavoir

Let's say that for some strange reason I want to have this function:
let (~-) (str:string) = 42
So I can do something like this and get 42 as result:
-"test"
val it : int = 42
Which is fine. But now when I do:
let a = 100
-a
I get:
error FS0001: This expression was expected to have type
string
but here has type
int
Any idea why is this happening?
When you define operators using let, the new definition hides all previous definition of the operator. So in your example, you are hiding the default implementation of the unary minus (which works for numbers) and replacing it with a new operator that only works on strings.
It is not easy to re-define overloaded operators on built-in types. If you need that, it is probably better idea to avoid using operators (just use a function). However, if you want to provide an overloaded operator for a custom type, you can do this by adding operator as a static member:
type MinusString(s:string) =
member x.Value = s
/// Provide unary minus for MinusString values
static member (~-) (ms:MinusString) =
MinusString("-" + ms.Value)
-(MinusString "hi") // Returns "-hi"
If you really want to redefine built-in operator like unary minus and make it work on string, then there is actually a way to do this using a trick described in earlier SO answers. However, I would only use this if you have a good reason.
Simply, you overwrote the minus operator with one that takes a string and returns an int, then tried to apply it to an int, which it can't do anymore.

Using System.String.Split from F#

I would like to use the .NET CLR version of String.Split in F#. Specifically I would like to use this code:
let main argv =
let s = "Now is the time for FOO good men to come to the aide of their country"
let sepAry = [|"FOO"; "BAR"|]
let z1 = s.Split sepAry
0 // return an integer exit code
This fails to compile however, due to the fact (I believe) that the version of Split in F# is implemented differently than the one in .Net 4.5.
The version from .NET that I would like is:
Split(String[], StringSplitOptions) Returns a string array that contains the substrings in this string that are delimited by elements of a specified string array. A parameter specifies whether to return empty array elements.
I understand that I am getting the F# version of Split, which formerly resided in the PowerPack and that is why the implementation differs from the CLR version.
What is the best way to get what I want? Is it possible to override the F# version of Split and use the .Net version? Is it possible to extend the F# version and if so, how?
The overload you want to use expects a second argument.
let z1 = s.Split (sepAry, System.StringSplitOptions.None)
It's not an “F# version of Split”, it's exactly that Split you see in C#.
2 things are the problem here:
For .NET BCL you need to specify (), because the parameters are declared in different way using tuples (See http://msdn.microsoft.com/en-us/library/dd483468.aspx)
Only the char[] overload exists without StringSplitOptions. If you want to use a string array, you also need to specify StringSplitOptions.
You could create your own F# overload method, that provides a default for the stringsplitoptions.
Kirelagin is right, the Split method on String your trying to use doesn't exist, it's only available for char arrays with no secondary argument. You have to resort to this version: http://msdn.microsoft.com/en-us/library/tabh47cf.aspx. Also you have to use parentheses around your arguments when calling none-f# .NET apis because the arguments in C# are defined as a tuple.
You could of cause define your own extension-method on String, this way you don't have to specify None all the time if that is your expected default behavior
type System.String with
member x.Split(separator : (string [])) = x.Split(separator, System.StringSplitOptions.None)

Questions about the definition of lazy

On line 5633 in prim-types.fs (v1.9.7.8) there is the following type abbreviation:
type 'T ``lazy`` = Lazy<'T>
I have a few questions about it.
What do the double backticks mean?
Is this definition equivalent to type lazy<'T> = Lazy<'T>? (If not, how is it different?)
The double back ticks are a way of allowing an F# keyword to be used as an identifier. Another example would be
let ``let`` = 42
To answer the second half of your question, generic types in F# can be specified using either the O'Caml-style syntax where the generic parameter precedes the type (e.g 'a list, int array, etc.), or the .NET-style with angle brackets (e.g. list<'a>, array<int>, etc.), so the two definitions are indeed basically equivalent (except that your version as written is syntactically invalid because lazy is a keyword). For multi-parameter generic types, the O'Caml style is deprecated and will generate a warning (e.g. let (m:(int,string) Map) = Map.empty should be rewritten as let (m:Map<int,string>) = Map.empty).

Resources