Hacker News new | past | comments | ask | show | jobs | submit login

I used square root because it is commonly known that negative numbers are a problem for them. What about tangent(x) which periodically blows up? A type system can't fix that.

In my opinion it is far better to throw an exception that can be explicitly handled, rather than cause some math error at some random point later because NaN or some infinity snuck by as a number.




Math.tan will never throw exceptions because, for the exact same reason as above, it won't check its input.

But again for the sake of argument, the return type of tan() could be "Either<Error, double>".


I guess I would get used to that, but it seems broken to me. The way to deal with that is out of band, checking for the type to change to "Error"

In Pascal tan(pi/2) or tan(3pi/2) causes an overflow. The way to deal with that is out of band, to handle an exception of type E_Overflow.


Again no, it isn't. You are well entitled to believe Pascal is the best thing since sliced bread, but the two ideas are fundamentally different.

There is no type change. The function "tan" would return a value of type "Either<Error, double>". An expression such as:

   double x = Math.tan(Math.PI / 8)
would fail the compile-time type check, because the type of the variable "x" is "double" and the type of the expression "Math.tan(Math.PI / 8)" is "Either<Error, double>".

It would be the responsibility of the caller to prove to the type system that the error has been handled, with either a built-in language facility a la ML, or with ad-hoc code such as a Bohm-Berarducci construction.

Let's just agree to disagree.


I know Pascal isn't perfect, and lots has been learned since then... this is genuine confusion on my part. So tan in your system returns a variant type (could be Error, could be Double), right? (I didn't get that)

Compilers can find all sorts of things they couldn't in the past, which is amazing.

What happens at run-time if an overflow happens?

again... I'm sorry if I didn't express myself clearly enough. I'm coming at this from a Pascal programmers perspective... I've missed the past 20 years of compiler improvements.


Like you said, this is a variant type that represents either a value, or an error. Here's the definition of Rust's Result type:

  pub enum Result<T, E> {
      Ok(T),
      Err(E),
  }
I threw together some examples of how you can use a Result value, although I used arcsin instead of tan as the out-of-domain values are much easier to specify.

https://play.rust-lang.org/?version=stable&mode=debug&editio...

I'm not entirely sure what you're asking about regarding runtime behaviour, but here's two guesses.

As a user of a function that returns a Result type, at runtime the function returns a result that has a flag and a payload, where the flag specifies whether the payload is your value or an error. The only ways to get a value out of the result are either to check the flag and handle both possibilities, or to check it and crash the program if it's an error.

As someone writing a function that returns a result type, you write whatever logic you need to determine whether you've successfully produced a value, or which error you've produced, and then you either return(Ok(my_value)); or return(Err(my_error));. If you're doing floating point math, this might be checking an overflow flag, or looking at your inputs, or checking for NaN after operating, or whatever makes sense for your task.

Using a variant/product/discriminated union like this is orthogonal to the details of floating point operations at runtime. What it lets you do is have the compiler enforce that users of your function must check for errors, as it's a type error to assign a Result<f64,E> to a variable of type f64.

I hope that helps! Do you have any more questions about this, or things I didn't address, or topics where you'd like more explanation or examples?


In Rust in particular (and many other languages) tan operates on IEEE 754 double-precision floating points ("double" for Free Pascal, "f64" for Rust). These can contain infinity and NAN values, not just numbers. So if tan blows up, you get NAN back. It's your job as the programmer to check that the result wasn't NAN (or INF) before proceeding. "Double" is already such a variant type. So you have to check your results which can return an error variant for errors, just like you already do with floating-point values.


Rust chooses to panic, I believe, but you could always have applied the same Error strategy — it’d just be really annoying. The stdlib also provides a bunch of methods to pick-your-strategy for overflow behavior, but you need to use them instead of the +/* operation




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

Search: