9 ms·
Why does Swift use this weird constraint-based type resolution anyway? To me it seems like it, whenever it manages to found a non-trivial solution, would be one
by im4w1l 10y ago
Why does Swift use this weird constraint-based type resolution anyway? To me it seems like it, whenever it manages to found a non-trivial solution, would be one that is unexpected to the programmer and leads to unintended consequences. Anyone have a motivating example of when it would be useful?
What's wrong with a type resolver that resolves (or not, with an error!) greedily in order of precedence?
Here's how I would solve
let a: Animal = 1 + -(2)
Ok, 2 is an int. If that gives problems down the line, too bad. -(2) is an int, if that gives problems, too bad. 1+-(2) is int+int so result should be an int. a is an Animal, try to convert int to Animal. Fail. Error!
One thing in particular that scares me with the constraint approach is that it seems to me (though I may be wrong) that whether overflow occurs or not is not immediately obvious.
let a: Double = -(INT_MAX) + - (INT_MAX)
whats to say those INT_MAXes wont somehow be interpreted as Doubles and a will be -2 INT_MAX ?
- fooker 10y agoSwift supports overloading functions by return type.
- solidsnack9000 10y agoSwift is not unique in using constraint solving to provide bidirectional type inference. Scala and Haskell provide the same level of flexibility; although not with the same compile-time problems. In the case of Haskell, this is ultimately because the language is intentionally simpler in some ways. Swift brings together three features that can be hard on type inference: * Overloaded literals. When overloaded literals like maps can themselves contain literals, there are actually quite a few possible overloadings to choose from. * Subtyping. We infer not types but type bounds (ranges). * Overloading. Haskell has notably no overloading at all and no subtyping. Somehow Scala manages to combine all these things and a quite astonishing conversions system, too.
- JoshTriplett 10y agoHaskell does have overloaded literals: integer literals have type `Num a => a`, and floating-point literals have type `Fractional a => a`. And GHC has an extension OverloadedStrings to make string literals have type `IsString a => a` (which makes it easier to work with ByteString and other different string-like types).
- cousin_it 10y agoAFAIK, Haskell doesn't really have overloading in the sense that other languages use it. "Num a => a" is just one type. Contrast with Java, where a class can have two methods named "foo" with completely different arguments and return types.
- minitech 10y ago“Overloaded literals” only has one meaning as far as I know, and what Haskell has is it: a literal whose type isn’t solely determined by its contents.
- Sean1708 10y agoEssentially the parent commenters are talking about too different things, one is talking about the first bullet point and the other is talking about the third.
- ufo 10y agoHowever, it seems that the really tricky overloads in Swift's case are the lists, which can contain other overloaded literals inside them.
- capitalsigma 10y agoHaskell is also very happy to throw "ambiguous literal..." errors at you when it gets a little confused.
- solidsnack9000 10y agoSorry, I was not being clear. Since I treated "overloaded literals" and "overloading" as separate bullet points I was using "overloading" to mean only overloaded declarations -- declaring the same function multiple times with different types.
- smikhanov 10y agoInteresting, didn't know that about Swift compiler. What does constraint solving provide that Hindley-Milner doesn't? My understanding of HM was that it's reducible to the system of linear equations, therefore solvable in linear time. What is that that Swift type checker requires that HM doesn't provide?
- Sharlin 10y agoHM has no concept of subtyping or overloading, I think. Scala has both of those and cannot do full type inference, only local inference.
- deleted 10y ago[deleted]
- solidsnack9000 10y agoIt is worth considering that neither Haskell nor Scala are strictly Hindley-Milner.
- trurl 10y agoType inference for HM is actually exponential, due to generalization for let-polymorphism. You're probably thinking of inference for a language with just simple types.
- spullara 10y agoPaul Philips showed me methods in Scala that took minutes to compile as it tried to solve for the return type. It has similar problems and is related to why the compiler is so slow.
- exDM69 10y agoOverloading with type inference is an interesting problem. Haskell has type classes, which is an elegant solution but has its own issues. Swift has more flexible overloading at the expense of compile times and corner cases.
- gilgoomesh 10y agoBoth of your questions have the same answer: Swift doesn't implicitly convert between types. First answer: since Swift has no implicit conversions, greedily selecting types would create a situation where mathematical expressions or even function parameters that use Float instead of Double would be completely impractical. Every floating point literal would be interpreted as Double and compiler error on attempt to use as a Float. You'd need to manually construct a Float around every literal to actually get Float. The same problem would plague and UInt, Int8, etc – any value would require manual construction from a literal of type Int. Second answer: Since Swift has no implicit conversions: let a: Double = -(Int.max) + -(Int.max) would always be a compile error because the right-hand side is an Int and can't be converted to Double. This isn't a type inference or type checking problem, it's a "no implicit conversions allowed" error. You would need to write: let a = Double(-(Int.max) &+ -(Int.max)) for the line to compile (I've used the overflow allowed addition operator '&+' since the regular '+' will trigger an abort if it actually overflows).
- espadrine 10y agoThe idea of having a select number of explicitly defined implicit conversions is interesting, though, but not unheard of. (Is there not something like that in Scala?) However, in order to perform the simplistic algorithm that the parent comment imagines, we have to give up overloading. If you have f(A) and f(B) and an implicit conversion between A and B, and we type f(x) (where x is an A), both f(A) and f(B) could be selected. Since they might have different types, a larger expression it is a part of could be valid by picking one and not the other, but we cannot know which until we go through all possibilities. That's an exponential algorithm too.
- paulddraper 10y agoYes, Scala has that. There's a precedence, and ties are compile errors.It It won't apply more then one level of conversion, though you can perform recursive operations in the type system. Shapeless is a library that uses this for ad-hoc typesafe programming without boilerplate.
- im4w1l 10y ago