TL;DR
Why doesn't this work?
"abcdefg".characters.map(String.init) // error: type of expression is ambiguous without more context
Details
One really cool thing I like in Swift is the ability to convert a collection of one thing to another by passing in an init method (assuming an init() for that type exists).
Here's an example converting a list of tuples to instances of ClosedInterval.
[(1,3), (3,4), (4,5)].map(ClosedInterval.init)
That example also takes advantage of the fact that we can pass a tuple of arguments as a single argument as long as the tuple matches the function's argument list.
Here another example, this time converting a list of numbers to string instances.
(1...100).map(String.init)
Unfortunately, the next example does not work. Here I am trying to split up a string into a list of single-character strings.
"abcdefg".characters.map(String.init) // error: type of expression is ambiguous without more context
map() should be operating on a list of Character (and indeed I was able to verify in a playground that Swift infers the correct type of [Character] here being passed into map).
String definitely can be instantiated from a Character.
let a: Character = "a"
String(a) // this works
And interestingly, this works if the characters are each in their own array.
"abcdefg".characters.map { [$0] }.map(String.init)
Or the equivalent:
let cx2: [[Character]] = [["a"], ["b"], ["c"], ["d"]]
cx2.map(String.init)
I know that I could do this:
"abcdefg".characters.map { String($0) }
But I am specifically trying to understand why "abcdefg".characters.map(String.init) does not work (IMO this syntax is also more readable and elegant)
Simplified repro:
String.init as Character -> String
// error: type of expression is ambiguous without more context
This is because String has two initializers that accept one Character:
init(_ c: Character)
init(stringInterpolationSegment expr: Character)
As far as I know, there is no way to disambiguate them when using the initializer as a value.
As for (1...100).map(String.init), String.init is referred as Int -> String. Although there are two initializers that accept one Int:
init(stringInterpolationSegment expr: Int)
init<T : _SignedIntegerType>(_ v: T)
Generic type is weaker than explicit type. So the compiler choose stringInterpolationSegment: one in this case. You can confirm that by command + click on .init.
Related
I was wondering if there's a function (or special syntax) which would behave something like the hypothetical apply below (but for arbitrary f; the one given here is just for the sake of example):
func f(a: String, b: String) -> Bool {
return a == b
}
let argDict = ["a": "foo", "b": "bar"]
apply(f, argDict) // evaluates f(a: "foo", b: "bar") and returns false
It doesn't have to use a dictionary for the arguments (maybe it even can't use a dictionary?); it could use some other datatype or even just some other special syntax, as long as it somehow enables you to package up arguments and then apply a function to them later, as though you had written the arguments in by hand.
If not for all functions, what about for special classes of functions, like functions with variadic arguments? For example, it would be nice to be able to apply a function with signature (Double...) -> Double to an array of type [Double] as though we had inlined the values.
And if it doesn't already exist as a built-in, can it be constructed?
(Also, I'm not looking to redefine f; if you wanted to, I think you could just redefine it via f1(dict: [String: String]) -> Bool { ... } and then use dict["a"] and dict["b"] in the body instead in place of a and b. But I'm asking this question because I'm curious about the language capabilities here, not because I'm trying to solve a specific problem.)
Swift used to have that. You could pass a tuple of the arguments rather than the arguments directly, which is exactly what you're describing (a strongly-typed argument package). It was called "tuple splat." It was removed in Swift 3. See the SE for the background and why it was removed.
This code works fine. It iterates my array of one Int! and prints its magnitude.
import Foundation
let x : Int! = 1
[x].forEach {i in
print(i.magnitude)
}
Output:
1
Presumably, i in the loop body is an Int or an Int!, and indeed if I ask Xcode for "quick help" on forEach it reports:
func forEach(_ body: (Int) throws -> Void) rethrows
However, if perform two statements in my forEach body, instead it fails to compile, complaining that I need to unwrap i which now has the optional type Int?.
import Foundation
let x : Int! = 1
[x].forEach {i in
print(i.magnitude)
print(i.magnitude)
}
Compile error:
Value of optional type 'Int?' must be unwrapped to refer to member 'magnitude' of wrapped base type 'Int'
And if I ask for "quick help" now I get:
func forEach(_ body: (Int?) throws -> Void) rethrows
How on earth does the number of statements I place in my loop body manage to affect the type of the loop variable?
Basically, you've elicited an edge case of an edge case. You've combined two things that are the work of the devil:
Implicitly unwrapped Optionals
Implicit type inference of closures, along with the fact that
Implicit type inference of closures works differently when the closure consists of one line (this is where the "How on earth does the number of statements" comes in)
You should try to avoid both of those; your code will be cleaner and will compile a lot faster. Indeed, implicit type inference of anything other than a single literal, like a string, Int, or Double, is a huge drag on compilation times.
I won't pretend to imitate the compiler's reasoning; I'll just show you an actual solution (other than just not using an IUO in the first place):
[x].forEach {(i:Int) in
print(i.magnitude)
print(i.magnitude)
}
Our Int type is legal because we take advantage of the single "get out of jail free" card saying that an implicitly unwrapped Optional can be used directly where the unwrapped type itself is expected. And by explicitly stating the type, we clear up the compiler's doubts.
(I say "directly" because implicit unwrappedness of an Optional is not propagated thru passing and assignment. That is why in your second example you discovered Int?, not Int, being passed into the closure.)
I am trying to convert an array of enums to a string in Swift. My enum is Printable and has a description property.
I thought this would work:
", ".join(a.map { String($0) })
but the compiler complains
Missing argument label 'stringInterpolationSegment:' in call
So, I follow the suggestion,
", ".join(a.map { String(stringInterpolationSegment: $0) })
But I do not understand:
Why is the argument label needed?
What is the type of stringInterpolationSegment?
You can't call a String initializer with your enum type because there isn't an initializer that takes that type.
There are a number of initializers for String that have the stringInterpolationSegment argument and they each implement it for a different type. The types include Bool, Float, Int, and Character among others. When all else fails, there is a generic fallback:
/// Create an instance containing `expr`\ 's `print` representation
init<T>(stringInterpolationSegment expr: T)
This is the version that is being called for your enum since it isn't one of the supported types.
Note, you can also do the following which is more succinct:
", ".join(a.map { toString($0) })
and you can skip the closure expression (thanks for pointing that out #Airspeed Velocity):
", ".join(a.map(toString))
As #vacawama points out, the error message is a bit of a red herring, and you can use map and toString to convert it.
But what’s nice is, if you’ve already implemented Printable, then the array’s implementation of Printable will also use it, so you can just do toString(a) to get a similar output.
Everyone knows you can use the == Operator to compare things.
if (stringValue1 == stringValue2)
If you do this in Objective-C the program will check if these objects are the same not if both strings do contain the same text. If you want to compare the text values you need to use a compare-Method.
To my understanding the same code in Swift does compare the text values. That is nice. A lot of programing language work like that. But what do I have to do to check if these two values refer to the same object?
For objects of class type you can you the === operator to check whether two objects refer to the same instance. However, you ask specifically about strings. Strings in swift are not of class type - they are values. The === operator will not work for them - the same way as it does not work for integers. So the answer to your question - how to check if two strings are the same instance - in Swift is: it is not possible. With strings in Swift you should use normal operators like == etc. only.
You can't as strings are values types, not object types. The === operator only works with object types (of AnyObject) but String is of type Any.
6> "abc" === "abc"
repl.swift:6:1: error: type 'String' does not conform to protocol 'AnyObject'
"abc" === "abc"
^
Swift.lhs:1:5: note: in initialization of parameter 'lhs'
let lhs: AnyObject?
^
6> var str : String = "abc"
str: String = "abc"
7> str === str
repl.swift:7:1: error: type 'String' does not conform to protocol 'AnyObject'
str === str
^
Swift.lhs:1:5: note: in initialization of parameter 'lhs'
let lhs: AnyObject?
^
Is there any easy way to tell what class an instance variable is in a Swift? In the JVM-based languages that I'm used to, you can do something like println(value.class) to get it's class.
Is there something equivalent in Swift?
The closest thing I can find in the docs is the ability to do "type checking" with the is <Class> keyword but that only helps me guess a little bit.
I've run into a few situations in playing around where I thought I had one type of class, but actually had another and didn't know how to know for sure.
Use type.self to return a type that can be passed into a method that accepts a type-level argument. For example, UILabel.self can be passed to the isKindOfClass method call. The string representation of the class can be found via dynamicType.description():
var label = UILabel()
println(label.dynamicType.description())
println(label.isKindOfClass(UILabel.self))
Swift-3
var label = UILabel()
println(type(of: label).description())
Output UILabel true
Here's a bit more background -- there are two expressions to be aware of: the postfix self expression and the dynamic type expression. From the docs:
Postfix Self
A postfix self expression consists of an expression or the name of a
type, immediately followed by .self. It has the following forms:
expression.self
type.self
The first form evaluates to the value of the expression. For example,
x.self evaluates to x.
The second form evaluates to the value of the type. Use this form to
access a type as a value. For example, because SomeClass.self
evaluates to the SomeClass type itself, you can pass it to a function
or method that accepts a type-level argument
Dyamic Type Expression
A dynamicType expression consists of an expression, immediately
followed by .dynamicType. It has the following form:
expression.dynamicType
The expression can’t be the name of a type. The entire dynamicType
expression evaluates to the value of the runtime type of the
expression.
As of beta 6 _stdlib_getTypeName gets the mangled type name of a variable. Paste this into an empty playground:
import Foundation
class PureSwiftClass {
}
var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"
println( "TypeName0 = \(_stdlib_getTypeName(myvar0))")
println( "TypeName1 = \(_stdlib_getTypeName(myvar1))")
println( "TypeName2 = \(_stdlib_getTypeName(myvar2))")
println( "TypeName3 = \(_stdlib_getTypeName(myvar3))")
The output is:
TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS
Ewan Swick's blog entry helps to decipher these strings:
e.g. _TtSi stands for Swift's internal Int type.
Mike Ash has a great blog entry covering the same topic.