Hacker News new | past | comments | ask | show | jobs | submit login
Cast-Free Arithmetic in Swift (realm.io)
54 points by ingve on Oct 12, 2015 | hide | past | favorite | 22 comments



> "In Objective-C, we have syntax like this:"

Not really - implicit casts were always a warning in Obj-C, along with any number of things that other languages consider errors.

My personal opinion is that Obj-C is so loose as a language that, for production uses at least, one should turn on as many warnings as the compiler gives you, and then turn on warnings-as-errors. We've been doing this for a while and it's saved our bacon at least a few times.

Floats, doubles, and ints are not interchangeable. Likewise, int8, int16, int32, and int64 are not interchangeable and IMO should never be converted from one to the other without the explicit knowledge of the programmer.

This seems like a nice convenience, but I'm skeptical if the convenience gained is worth the (IMO substantial) risk of shooting yourself in the foot.


What's dangerous about converting an int to a wider type?


You're bringing just one specific case where probably is acceptable[1] and ignoring all other cases where it's definitively bad (to narrower type, or mixing floats with ints).

[1] and only when you don't care about overhead of converting from one type.


I couldn't think of anything immediately Evil for that case and wanted to check I wasn't missing something :) The others definitely have badness if you aren't expecting it.

I'm quite surprised objective-c doesn't warn by default if you try to a potentially lossy conversion without an explicit cast.


I think it all depends what you meant by wider. I don't think anything will happen if you convert int8 to int16 (don't know swift so I don't know if it distinguishes between those types), but if you for example do casting from integer to floats and vice versa (the author uses double as the universal type) and do some heavy calculations you can get wrong results.

This is because float/double is not really a wider type it is just a number representation that is not exact and just uses approximations so you actually are losing information.


I'd generally limit wider to apply to the same, uh, type classes (there's probably a proper word) int8 to int16, float to double and so on.

Casting between integers and floating point is pretty risky unless you're absolutely sure about your ranges and precision. Anything that does that silently gives me The Fear.


> "I'm quite surprised objective-c doesn't warn by default if you try to a potentially lossy conversion without an explicit cast."

Implicit casts that are precision-lossy (say, int32 to int16) are definitely warnings in Obj-C, the issue is that most(?) Obj-C devs sail right past said warnings, and most projects are not configured to flag these as errors instead.

One of the other issues with implicit casts is that it's often non-obvious if what you're doing is lossy - there are a lot of aliased or conditional types (for example: a CGFloat is a float on 32-bit systems, and double on 64-bit)

Also things like time measurement - a NSTimeInterval is a double, so casting it down to a float is lossy, but not obviously so because for the most part we don't think about what's the actual object backing a lot of system types.


Well, at least theoretically it could lead to catastrophic performance loss.


I understand the spirit of exploration and fun that this was done with, however the author talks of using this in "production". This scares me. These types are not interchangeable.

I would love to see a disclaimer and/or a discussion of what can go wrong

EDIT: If I add the floats 1.5 and 1.5 to get an integer, is the result 2 or 3?


I use the dot property conversion, not the overloading in general use. I also have such reservations about using overloading for casting in production. What I do use, is the series of extensions for single letter dot property conversions, the extensions without the protocol extending, that I also used in Swift 1.2. Sorry if that was unclear...


1.5 has an exact representation in floating-point, so adding 1.5 + 1.5 is always going to give you 3.0 in floating-point, also exactly represented.

Then whatever the type of integer rounding you do, you will get a solid 3.


If you need to convert float to int, use an explicit rounding mode. Then adding 0.1f (or rather the closest fp approximation) ten times will round up to 10.


I've read that as do 1.4 + 1.4 give you 2 or 3 - if you cast 1.4 to int first, you'll get 1 + 1 = 2, but otherwise 1.4+1.4 = 2.8 -> to int = 3


>I understand the spirit of exploration and fun that this was done with, however the author talks of using this in "production". This scares me.

Not all producton is critical.


The Swift patterns Emerging definitely favor explicit conversions over implicit. One of the QA questions begins eluding to some issues with this pattern with under and overflows, but the author also doesn't mention safety around Nan's or Infinity. Last thing I want as a consumer of an API is surprises. There are good reasons why these value types are non interchangeable without being explicit.


Good point bringing up over/underflow, I didn't think much about Nan or infinity, but I did consider potential over/underflows. I originally built an overflow/underflow checker(on the github project), but then re-evaluated it and figured it wasn't really necessary since the default action for number types that overflow/underflow in swift is to throw an error runtime. The person asking the question mentioned another series of operators that exist &+, &-, &*,etc that allow for overflow/underflow to occur on number types in swift.. without the error throwing, which is not the default.


The case that has bugged me is when I have some integer number of items and I need to compute some CGFloat for drawing something based on it. count * SpacingPixels gives an error, need CGFloat(count) * SpacingPixels even though there is no possibility of losing precision by the up-cast from Int to CGFloat.

It is also slightly annoying to have Double and CGFloat not be the same, but I solved that by just always using CGFloat pretty much everywhere even when not referring to pixels or points.

I don't think I would overload + and *, though, due to possible complications with the error messages as the article suggests. But I might play with more terse Ruby-like ".to_f" as an extension or something ...


CGFloat is a different type based on the system, is it not?


Yes. It's double precision on 64-bit platforms and single precision on 32-bit.

  #if defined(__LP64__) && __LP64__
  # define CGFLOAT_TYPE double
  # define CGFLOAT_IS_DOUBLE 1
  # define CGFLOAT_MIN DBL_MIN
  # define CGFLOAT_MAX DBL_MAX
  #else
  # define CGFLOAT_TYPE float
  # define CGFLOAT_IS_DOUBLE 0
  # define CGFLOAT_MIN FLT_MIN
  # define CGFLOAT_MAX FLT_MAX
  #endif


Exactly, this is also pointed out in the struct definition in CoreGraphics for CGFloat.

/// The native type used to store the CGFloat, which is Float on 32-bit architectures and Double on 64-bit architectures.


This is called coercion — implicit conversion between types.

Numeric types coercion is very basic convenience that is present in many statically typed programming languages. Older languages like C used to allow unsafe conversions, but modern ones like C# or Java are doing it right: for example, adding int and float is allowed, but assigning float to int is not. There is no reason Swift shouldn't have the same.

I suspect is has been given up (temporarily or not) because of type inference. Swift's already giving "expression is too complex" errors in some situations, numeric coercions would surely make it worse.


What was wrong with Java type promotion already?




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: