For
subs
|> Array.map (fun x ->
let t, s, _ = x
getAuditEventsAsync s t (now - TimeSpan.FromDays(7.0)) now)
the compiler says for t when applying getAuditEventsAsync:
The type 'string' does not match the type 'string * string'
getAuditEventsAsync is a string -> string -> DateTime -> DateTime -> Async<string> and t and s are strings.
Any hints on why the compiler think I'm attempting to bind a string to a tuple of strings?
Update:
The root cause was actually in getAuditEventsAsync which does
async {
return! Http.AsyncRequestString
( url, httpMethod="GET",
headers = [ Accept HttpContentTypes.Json;
Authorization "Bearer " + t ])
}
for some url and token t where both are strings.
Operator precedence means that Authorization "Bearer " + t is interpreted as (Authorization "Bearer ") + t. Authorization returns string * string, (a non-overloaded) + is thus applied to a string * string and string which confuses the compiler.
The solution was to change Authorization "Bearer " + t to Authorization ("Bearer " + t).
So the question is now: why is the compiler error thrown at the application of getAuditEventsAsync rather than Authorization? :)
Here is a minimal example giving the same error message:
let Accept a = "Accept", a
let foo x = Accept "hoo" + x
foo "z"
The compiler and editor report two error messages, but (for some reason unclear to me) F# Interactive reports only the one you were getting. One of the errors is for + inside foo and the other is when calling foo.
If you comment out the last line, you get just one error on +:
error FS0071: Type constraint mismatch when applying the default type '(string * string)' for a type inference variable. Expecting a type supporting the operator '+' but given a tuple type Consider adding further type constraints
This makes sense and it says that + cannot be applied to tuples. Now, the compiler uses some defaulting mechanism so that it can continue type-checking and it decides that the argument of foo is also string * string. This then gives you the later error message when calling foo:
error FS0001: The type 'string' does not match the type 'string * string'
The confusing thing is that when you uncomment the last line again, the error message for the body of foo also changes to:
error FS0001: The type 'string' does not match the type 'string * string'
This is happening because the compiler now sees that foo is called with string as an argument and so the default guess x : string * string is replaced with x : string. I think this only affects the first error message though and so the second one still uses the default guess that foo takes string * string.
This is not entirely sensible behavior, but I guess that's the best the compiler can do given code with multiple errors - it has to use some default resolution for the error.
Related
Why does this work, but if I comment out the second line it does not? Is it because F# cannot infer msg is a string?
Line 3 = msg can be indexed msg[], and msg is string String.length msg.
Is this one of those situations where it is better to specify the type? let isQuestion (msg:string) = ...
let isQuestion msg =
let n = String.length msg //if I comment this line out the function no longer works
msg[(String.length msg - 1)] = '?'
Yes, the reason here is that the 2nd line is what tells the compiler that msg is unambiguously a string.
It's more complicated than this, but at a high level you can think of F# type inference as working as such:
top -> down
left -> right
In pre-F# 6, you would need to use msg.[...] to index the string, and with that syntax the error message is more clear:
The operator 'expr.[idx]' has been used on an object of indeterminate type based on information prior to this program point.
Consider adding further type constraints
However, in F# 6 it is a warning because you could technically be trying to write msg [expr] = '?', which would mean that msg is a function that takes a list as input and produces a char. Because of this ambiguity, the warning is emitted and you're asked to specify a type to clarify your intent. And because msg is not inferred to be a string, you see an error in the expr part of the indexing expression.
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 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)
My iPhone crashlog (and not my Simulator) shows me that I have the following issue:
Dyld Error Message:
Symbol not found: __TFE12CoreGraphicsVSC6CGRectCfMS0_FT1xSi1ySi5widthSi6heightSi_S0_
Referenced from: /private/var/mobile/Containers/Bundle/Application/8F97818E-F019-42E8-883C-6FB1994C24B7/Ekalipi.app/PlugIns/EkalipiKeyboard.appex/EkalipiKeyboard
Expected in: /private/var/mobile/Containers/Bundle/Application/8F97818E-F019-42E8-883C-6FB1994C24B7/Ekalipi.app/PlugIns/EkalipiKeyboard.appex/../../Frameworks/libswiftCoreGraphics.dylib
Dyld Version: 353.5
Is this a Unicode symbol that can't be loaded?
Last meaningful stack entry is this:
6 EkalipiKeyboard 0x0010ad88 0xf5000 + 89480
7 UIKit 0x2acbe4f0 -[_UIViewServiceViewControllerOperator __createViewController:withContextToken:fbsDisplays:appearanceSerializedRepresentations:legacyAppearance:hostAccessibilityServerPort:canShowTextServices:replyHandler:] + 1152
What is the pattern for understanding the above resource string?
Many thanks in advance!
Klaus
That is a mangled label that the compiler generated for that function (the CGRect initializer)
You can break down the full label like this (I think)
__TFE12CoreGraphicsVSC6CGRectCfMS0_FT1xSi1ySi5widthSi6heightSi_S0_
_ is a common beginning of a symbol
_T is the marker for a Swift global symbol
F says that it's a function
I don't know what E means (but looking at the detangled symbol it seems to correspond to ext)
12CoreGraphics is the name of the module (prefixes with the length of the name)
V marks the start of a struct
I don't know what S or what C means
6CGRect is the name of the function (I think it's the function)
I don't know what C means (see M below)
f marks this symbol as an "uncurried function"
I don't know what M means (CfM together seem to mean an init function but I don't know what the individual letters mean)
S0_ is a substitution. I think it's a substitution for "self" which is passed to curry the function
F here marks the beginning of the function's parameter list
T marks the beginning of a "tuple" (for the arguments)
1x is the name of the first parameter (prefixed with the length of the name)
Si says that it is of the Swift.Int type
1y is the name of the second parameter (prefixed with the length of the name)
Si says that it is of the Swift.Int type
5width is the name of the third parameter (prefixed with the length of the name)
Si says that it is of the Swift.Int type
6height is the name of the fourth parameter (prefixed with the length of the name)
Si says that it is of the Swift.Int type
_ marks the end of the uncurried function's arguments tuple
S0_ is the same substitution again (which I think means that it returns "self")
Additionally, running it through xcrun swift-demangle gives the official demangling:
ext.CoreGraphics.C.CGRect.init (C.CGRect.Type)(x : Swift.Int, y : Swift.Int, width : Swift.Int, height : Swift.Int) -> C.CGRect
Gwynne Raskind wrote a very detailed article about Swift Name Mangling where you can read more about this name mangling
Using F# in Visual Studio 2012, this code compiles:
let ``foo.bar`` = 5
But this code does not:
type ``foo.bar`` = class end
Invalid namespace, module, type or union case name
According to section 3.4 of the F# language specification:
Any sequence of characters that is enclosed in double-backtick marks (````),
excluding newlines, tabs, and double-backtick pairs themselves, is treated
as an identifier.
token ident =
| ident-text
| `` [^ '\n' '\r' '\t']+ | [^ '\n' '\r' '\t'] ``
Section 5 defines type as:
type :=
( type )
type -> type -- function type
type * ... * type -- tuple type
typar -- variable type
long-ident -- named type, such as int
long-ident<types> -- named type, such as list<int>
long-ident< > -- named type, such as IEnumerable< >
type long-ident -- named type, such as int list
type[ , ... , ] -- array type
type lazy -- lazy type
type typar-defns -- type with constraints
typar :> type -- variable type with subtype constraint
#type -- anonymous type with subtype constraint
... and Section 4.2 defines long-ident as:
long-ident := ident '.' ... '.' ident
As far as I can tell from the spec, types are named with long-idents, and long-idents can be idents. Since idents support double-backtick-quoted punctuation, it therefore seems like types should too.
So am I misreading the spec? Or is this a compiler bug?
It definitely looks like the specification is not synchronized with the actual implementation, so there is a bug on one side or the other.
When you use identifier in double backticks, the compiler treats it as a name and simply generates type (or member) with the name you specified in backticks. It does not do any name mangling to make sure that the identifier is valid type/member name.
This means that it is not too surprising that you cannot use identifiers that would clash with some standard meaning in the compiled code. In your example, it is dot, but here are a few other examples:
type ``Foo.Bar``() = // Dot is not allowed because it represents namespace
member x.Bar = 0
type ``Foo`1``() = // Single backtick is used to compile generic types
member x.Bar = 0
type ``Foo+Bar``() = // + is used in the name of a nested type
member x.Bar = 0
The above examples are not allowed as type names (because they clash with some standard meaning), but you can use them in let-bindings, because there are no such restrictions on variable names:
let ``foo`1`` = 0
let ``foo.bar`` = 2
let ``foo+bar`` = 1
This is definitely something that should be explained in the documentation & the specification, but I hope this helps to clarify what is going on.