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
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 the following types
type StatusCode =
| OK = 200
| NoContent = 204
| MovedTemp = 301
| MovedPerm = 302
| SeeOther = 303
| NotModified = 304
| NotFound = 404
| ServerError = 500
[<Literal>]
let NoBodyAllowedStatusCodes = [StatusCode.NoContent; StatusCode.NotModified]
And I'm getting a compile-time error that says:
This is not a valid constant expression or custom attribute value
I can't really figure out what's wrong here.
In F#, and .NET in general, lists cannot be literals (constant in C#/VB.NET). Only primitive values can, like string, bool, etc. The F# 3.0 specification has the guidelines on what can or cannot be a literal in section 10.2.2:
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 ThreadStatic or ContextStatic attributes.
The right-hand 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.
Depending on what you are trying to do, you could make your list static if the let binding is being used in a class. If it is in a module, I'd just remove the Literal attribute since let bindings are immutable by default, anyway.
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.
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).
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.