Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

First impressions apply to languages too. A lot of mathematicians went into AI but didn't start out with any programming experience. They dabbled in some languages and gravitated toward the one(s) they found most useful.

Paraphrasing Guy Steele [1]:

> It's important that when you design a language that does a familiar thing, that you do the familiar thing exactly... it's criminal that you can write 1/2 and get values nowhere near one-half.

Python

>>> 1/2

0.5

Ruby

irb(main):001> 1/2

0

To a novice programmer, ruby's result makes no sense, and introduces an incredible degree of doubt into the mind of the user. Sure, they could explore why it gives that result, but if that's not helping them get toward solving the problem they're using the programming language for in the first place, then it could seem like an indulgent tangent.

[1] https://www.youtube.com/watch?v=agw-wlHGi0E&t=19m27s



> it's criminal that you can write 1/2 and get values nowhere near one-half

This is a foolish argument and Guy Steele should know better. What makes 1/2, which has an accurate floating point representation, any more important than 1/10, which doesn't? Every novice programmer, every single one, trips over floating point, and pretending that floating point numbers match our intuition from mathematics doesn't do anyone any favors.


I don't think his attention was specifically to have 1/2 be a floating point number, like for example Javascript does, but to call to attention the fact that we design programming languages for a specific audience, not realising that if we widened our perspective it could be intuitive for a much wider audience.

For example, Ruby was designed for experienced software developers. Experienced software developers expect that if you divide an integer by another integer, that it performs integer division, and 1/0 would be 0 in integer maths.

If the creator of Ruby had broadened their horizon, and instead realised that outside the world of experienced software developers, the expression "1/2" means something totally different, maybe they could have chosen a different behaviour.

In a different universe perhaps any numeric literal in Ruby would always be a `Number` (like in Javascript), that for division would instead return a `Rational`. So that the end user could at the end simply call `.to_i` or `.to_f` or `.to_s` according to their needs.

I don't know if that would be a better universe though. I agree with your point that every programmer eventually needs to learn about floating point numbers and their sometimes surprising properties. And in the end Ruby was designed for programming computers, and the goal of any programming language is to expose the abilities of a computer, which include at the core integer maths, however surprising that maths might be to a novice.


> If the creator of Ruby had broadened their horizon, and instead realised that outside the world of experienced software developers, the expression "1/2" means something totally different, maybe they could have chosen a different behaviour.

IMO, that would be a tragedy.

In every language that does it implicit type coercion is the cause of a litany of bugs.

It's the cause of many problems in php. It's why people are told not to use == in javascript.

Maybe it is more difficult for beginners. But people are only beginners for a couple of weeks. It makes no sense to design the language to make those couple of weeks a little better and the rest of their programming lives a lot worse.


I agree, but I didn't suggest implicit type coercion. The bugs in Javascript are because of type coercion, but that has nothing to do with the fact that numbers in typescript are all floats. I don't think there are many bugs in Javascript that are due to numbers being floats, though I bet there's some companies who decided to build their backend in Javascript that might eventually run into problems.

There's ways to design a number class that doesn't suffer from any of these problems (obviously it would have to sacrifice performance to some degree).


> In a different universe

"Cuis-Smalltalk computes with rational numbers."

https://cuis-smalltalk.github.io/TheCuisBook/Writing-your-fi...


That's an argument for being more careful with floating point operations, and perhaps not using them as a default numeric value, but it's not what Steele is talking about there. After all `1/10` in Python is still pretty close to one tenth, even if it's not quite there.

The question here is whether or not we should assume integer semantics for numerical values.


I suspect that the real answer to this should be that the representation should remain as a rational until it can't. Forcing it into either float or int at the point of entry is the mistake.


Does any of the programming languages Guy Steele worked with do the former and not the latter?

Java certainly doesn't: https://godbolt.org/z/b847KeaGv

There's a good reason for this. Computer numbers are not the same as numbers in mathematics. Floating point numbers are not real numbers and ints are not natural numbers.


It was not debated that there is no reason some languages behave one way or the other - it is quite the opposite - only that for supporting the pratical use cases - which is the sole purpose of any language, to be useful for practical cases - then it is not a good reason.

When one has to jump hoops of the language - being the user for the whims of the language and not the language for the user - that is wasting efforts and adding complications instead of helping. Being more like puzzle for fun that practical application.

Those want to keep close to the soul of the hardware and computing should use C or other low level languages, existing for long. Not made recently to make programming easier ... on paper only apparently...


Pretty harsh words for an issue that is not really language specific.

Numbers are not as easy as they seem. Computer numbers are not the same as the numbers from mathematics.

Is "0.5" supposed to be a floating point number or a rational data type? What about conversion of types?

Sometimes you just need to know what happens in the particular language you are programming in.


There is no computer numbers and mathematical numbers, there are only mathematical numbers and the limited (failed by circumstances) representation of mathematical numbers existing for low level lenguages since the ancient times of computing.

Which should be improved - failed representation eliminated - wherever and whenever possible. Especially for new languages. And since there are better and worse representations - actually handling - of numbers accross the languages the criticism of doing badly, not making improvements, expecting to know the whims of a modern lannguage in this regard is absolutely founded!


Scheme, Common Lisp.

Neither Java nor CL were intended for the novice; I'm sure Mr. Steele had Scheme in mind.


I think there is also a gotcha there as you get a ratio in Common Lisp and not a floating point number.

> (print (format t "1 divided by 2 is ~a and its type is ~a" (/ 1 2) (type-of (/ 1 2))))

> 1 divided by 2 is 1/2 and its type is RATIO

I don't think there is a simple solution. Numbers are harder than they seem and you just need to know what happens in the particular language you are programming in.


Arguably that's not a gotcha. It's (probably) his point.


Having to work with scientists and mathematicians, as in offering my programming help to them... I see no value in making languages look like math formulas.

Math formulas are an awful language. It served its purpose to discover how to do things better, but it became obsolete many decades ago. People who hold on to these ideas create truly awful languages like Haskell. These languages make comprehension prohibitively difficult. So much so that a lot of people who would otherwise benefit from using the language and would have enough mental capacity to appreciate its higher-level concepts are prevented from doing so by a very superficial thing s.a. syntax.

Also, have had you done any math beyond high-school, you'd not have a problem understanding why the result you see in Ruby makes sense. I think some high schools today also include set theory in math curriculum, which would include concepts like closure, domain / co-domain etc. So... this is really not a good example of the concept you mention.

In general, I think that being correct, minimal and internally consistent is a lot more valuable for the language than to be similar to the prior knowledge newcomers might have. It's nice to be also newcomer-friendly, but that shouldn't come at the expense of sacrificing any of the aforementioned desirable properties.


Notable that this was python behaviour until Py3.


The shift towards // vs / also shows great acumen by the Python designers: they tried (and arguably, largely succeeded) in catering to the greater data science/statistics/numerical analysis communities, where 1/2=0.5 is obvious, rather than sticking to the "compsci-obvious" 1/2=0.


Neither is obvious in either community...

And this isn't the reason Python succeeded. Nor is this the reason for the particular change.

The reason Python succeeded with data-science is NumPy and related group of libraries. They happened to be the first to offer easy access to R-like features of other statistically-flavored languages in an all-purpose language. I.e. it makes it easy to combine general-purpose code with statistics-specific code, and once it accumulated critical mass, the process became self-sustaining and alternatives died off quickly.

The reason for most of the changes that happened to Python in the last fifteen or so years is design driven by fashion. Which means the majority decides what to do with the language. Which also means that Python is made to look more and more like other mainstream languages (eg. Java, JavaScript, C++...) So, a lot of changes, this one included were made out of subconscious fear of non-conformity.


Surely 1/2 = 0.5 is what a statistician would expect? Whereas 1/2 = 0 is what happens in C, C++, Ruby, Java, C#, F#, Rust... Essentially most of the popular programming languages with the exception of Python and JS/Typescript.

Anyway. Maybe my original comment was poorly phrased, but I was not implying that Python succeeded because of this form of catering. Rather, the designers took note of Python becoming popular in that field and made changes (see also the matrix multiplication operator @) that accommodate those users rather than the more "typical" CompSci crowd.


My wife's mom is a statistician (and has been since like 70's). She also used to do a lot of programming in the line of her work (working for a telco, and later as biostatistician), in the latest iteration in R, where things are like you expect them to be. But, before then it was also Matlab, where 1 / 2 = 0.

But, none of that is really relevant. Both operations are useful and common in statistics. Which one is more common will depend on your domain.

> the designers took note of Python [...] made changes

That's putting too much faith in designers of Python. Even calling these people "designers" is giving them too much credit. By their own admission they don't have any sort of vision or strategy for how to deal with the language, they just add random stuff and see if a lot of people complain or thank them.

In other words, matrix multiplication operator is there not because there was some kind of intention or design on the part of the small group of people who are responsible for releasing the language, it was more of a "genetic algorithm" kind of thing: change - iterate - see if change optimizes some metric - repeat.


Back when Python was winning over the AI crowd, they did the same thing as Ruby, if you didn't remember to call some voodoo chants "from future import ..."


I respect Guy Steel a lot but this argument is like complaining that

b = 1

doesn't compare b to 1 and return a boolean.

Lots of novice programmer think that's what equals should do based on familiarity with mathematics.

Using = for assignment doesn't help them solve problems either but it's what those with experience of programming generally expect.


Okay, I'll bite: how much of this is the responsibility of the language designers versus how much of it is the responsibility of the language user?

If I had a junior programmer complain to me about this case, I'd simply tell them "you didn't RTFM", because RTFM'ing before you use a language is not only the professional thing to do, but vital to your accurate and correct use of the language.

Sure, language designers shouldn't build-in these kinds of foot-bullets, but then again, kids shouldn't play with guns.

>First impressions apply to languages too.

The duty of responsibility applies to all languages, and it is the users responsibility to understand the language they are attempting to use, before using it. No?


If a language clearly states in the FM that something "criminal", in the words of your parent comment, is known to be criminal but they leave it as it is without any clear reason behind it (even recognizing it as a past error they have to carry for backward compatibility), the junior has all the right to think the language is criminal and is against them.

In the extreme case, take something like brainfuck. Everything is in the RTFM, but that doesn't make the language less criminal.

I like to remember that we are not here to program for the sake of programming. We are mainly solving problems. I have a problem that I know I need to solve with "a gun", and maybe I'm a novice to gun usage. After having a look at some guns, I choose the one who seems to be less dangerous and have a friendlier and more intuitive usage. When your very powerful but a deathtrap with 1000 page manual of a gun is used by no one, don't complain with "why you don't use my powerful and clearly better gun!?".

Because the key here is that this is not the 1970's anymore, when all guns were complicated. Learning by doing is the best way to learn, way better than learning by reading (https://thepeakperformancecenter.com/educational-learning/le...). If a language requires me to RTFM is at a big disadvantage with any other language that allows me to easily learn by doing.


>Because the key here is that this is not the 1970's anymore, when all guns were complicated.

I take issue with this - computers are far, far more complex now than they were in the 70's, which is why denying the language-users responsibility for fully understanding the language-designers intentions is such a farce.

>If a language requires me to RTFM is at a big disadvantage with any other language that allows me to easily learn by doing.

There are no languages under the sun which do not require some degree of study, and to claim that it should not be so is simply delusional. All language must be learned before it can be properly used - some people learn by making huge mistakes with the language they use, its true, but the productive, professional use of any and all human language requires its study.


I don’t think it’s fair to say integer division is an error, it’s more so a lack of an abstraction of “number” over floats and ints.


Having a full numeric tower and stuff like having 1/2 evaluate to 1/2 (like in Scheme) definitely is the language designer's responsibility.


"1/2" - are these integers or reals?

Shouldn't the correct results be expected for the correct statement "1.0 / 2.0" instead?

This is a user flaw, not a language flaw. Nothing in the "1/2" statement implies real numbers .. ?


0 is also the behaviour of C and of pretty much any serious programming language.


I've always suspected that Pascal and Haskell were not serious programming languages, thank you for confirming my suspicion.


Notice how I said most; also I would argue that yes, those aren't really serious programming languages.

Haskell is an academic experiment and is mostly esoteric in the industry. It has however impacted a number of other languages that do get some use.

Pascal is way past its prime and no major company is giving it significant support anymore.


> I've always suspected that Pascal and Haskell were not serious programming languages, thank you for confirming my suspicion.

I wouldn't really group Pascal and Haskell together (unless we're playing rhyming games).

One of those had serious market penetration, a whole industry behind it and was one of the dominant choices for applications languages ... for maybe two full decades (including Turbo Pascal all the way through to Delphi).

I mean, right now, Delphi is still orders of magnitudes more popular and in use than Haskell, which is used by .... pandoc, maybe?


I feel like everyone making comments along this line is completely missing his point.

It's not wrong that 0 is the behaviour of C/Ruby, it's just that 1/2=0.5 is the expected result of much of Python's target audience.


But it's not. In second grade I was taught that 1/2 is 0 (with a remainder of 1). Fractions, real numbers, complex... are taught afterwards.


I don't think the target audience of Python are second-graders, but rather people who have graduated elementary school and realise that 1/2 comes out to 0.5.


That's silly, in math those two are just different notations for the same thing, and usually fractions are preferred.

Also anyone who's done any bit of programming should know that numbers as represented by a computer are discrete, while mathematics deal with symbolic relations which might require infinite amount of data to numerically represent.

Binary floating-point can't even represent 0.1.


FWIW that comparison would’ve been identical in Python 2, where to get floating point division in Python would’ve been:

>>> 1//2


1//2 always resulted in an integer, even in Python 2, with or without "from __future__ import division". To get floating point division in Python 2, you had to convert at least on of the operands to float (much like in C or C++).

You can try it online at tio.run, they still have Python 2 among their available languages, as well as Python 3.


> it's criminal that you can write 1/2 and get values nowhere near one-half.

that's sort of ridiculous. Would he be more ok with the output 0.48? what about 0.51? Ironically, in the age of LLMs and non-deterministic output, maybe he would.


Actually it is more like this:

Python 2.x

>>> 1/2

0

Python 3.x

>>> 1/2

0.5




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

Search: