The Crystal Programming Language Forum

Crystal 0.31.1 has been released!

Thanks to all the quick adopters of 0.31.0 and the feedback this past week. We hope this patch release allows everybody to move to 0.31 (and keep all your cores busy).

Back to our regularly scheduled programming …


We’ve written a blog post summarizing the changes in this release: https://crystal-lang.org/2019/09/30/crystal-0.31.1-released.html

The full changelog is here: https://github.com/crystal-lang/crystal/releases/tag/0.31.1

All the usual installation methods still apply: https://crystal-lang.org/docs/installation/

Docker images and 64 bits Linux packages are available as usual.

The brew formula PR is in https://github.com/Homebrew/homebrew-core/pull/44735

2 Likes

Nice!

I’m curious if any improvements to the compilation process are coming in the imminent future?

I was reading this thread on reddit, and decided to extract possible implementations:

From asterite

I believe some sort of caching to reuse previous compilation is possible. The only problem is that it’s really, really hard to do it and it’s not the current priority. Ruby is more than 20 years old and they still optimize it. I believe we can worry about these optimizations in the future.

From throwawaybrimsenboy

Would it be feasible to implement optional concrete type annotations to speed up the compiler ( compiling ) ?

The ideas so far:

  • Compilation Caching
  • Optional Concrete Type Annoations

What does the core team believe about these possible solutions?

Is it just me or is the compiler way slower in 0.31.1 than 0.31.0?

There are no plans to improve compilation times before 1.0

I haven’t notice that and the changes are minimal between those versions. Is there any reproducible experiment?

You’re right, reverted back to 0.31.0 and there’s barely any difference, should’ve measured before i reported.

On another note, homebrew doesn’t seem to have been updated with 0.31.1, is that not part of the roll out?

I used to wait until homebrew merged the PR to announce the release. That way there were no surprises. But it was later decided that we can’t wait for everyone to update (for example arch Linux). Basically, we now don’t wait for things we don’t control.

I personally don’t agree because we do take care of homebrew (except the PR merge time) but that’s just me.

I also waited because I use Mac, but that’s not fair for people who use Linux and can use the release before homebrew merged the PR. There are always tradeoffs :-)

We should also go back to using a tap, at least until we reach 1.0

1 Like

I personally don’t understand why the / division operator was changed to result in a Float64 as default. @ysbaddaden noted there is a new operator ( // ), IMO this is sufficient because developers can just opt-in to use //, if they want their resulting division value to be Float64.

My entire project is broken. Not broken in a way where I can just replace Time.now to Time.local, but broken in a way that I cannot write in words. I don’t think wrapping all math expressions with parenthesis and casting .to_i is a good idea, that feels very hacky to me. I have over 60 or so Int32 ivars, and about 30 of them are modified by simple integer division. This also adds pressure to the developer to utilize unions (which IMO is not a good idea). And it tries to modify the type that was explicitly set for that ivar!! I tried c, golang, and they all return an int type.

With that said, Brian’s PR went over my head. Probably because of my lack of mathematics and comprehension skills. And I really don’t want to post on GitHub and complain because I’m not smart enough to rebuke the PR. Mathematics is just out of my league, especially compared with compiler internals. I believe the highest math I have completed is pre-algebra, and that was in high school (I was in special math classes).

However! What I can try to do is express how this has negatively affected me :(. Not only my confidence level as a programmer, but my code. I’ve also spent all day yesterday trying to find a new language because I was so distraught. It would be really stupid of me to do that, because the time it takes to fix all my code would be far less than learning a new language and converting everything. Plus, when I think of ideas or solutions to in-game bugs, I can transcend those thoughts into Crystal code, subconsciously. I’m not an expert in Crystal by any means, however I feel confident it’s the right language for me. I don’t want to not use Crystal, it’s a very special thing in my life.

Finally, since Windows has a new editing server for WSL, editing unix files and compiling crystal have been quite fun. I spent all day yesterday fiddling around. I finally added a Total Compilation Time line to the progress_tracker, which is awesome!! This gives me confidence I can fork Crystal and change the / back to returning an Int. Or, I was thinking of just compiling an older version.

Right, of course! Forgot that homebrew can take their sweet time merging PRs. A tap sounds like a good idea when iterating fast :)

I understand your frustration. You dealt with two of the most annoying breaking changes at once: Time.now and //. Note however that they were introduced in very different versions and their discussion happened time ago.

All the changes are always with the intention of improving. But that is not always a smooth road. To facilitate that the compiler warnings were introduced.

If your code is built with --warnings=all to opt-in the warnings, all the Int#/ calls will show as deprecation, so it will facilitate the lookup and check if that should or should not be replaced by Int#//. With Time.now since the method was will be eventually removed the migration is easier. In 0.31 you are able to use --warnings=none to opt-out and keep using the deprecated method (yet, the // change is still needed).

Regarding / and //, the changes should allow you to use less to_i/to_f unless you do care about specific precision. Now the operators are able to reflect the intended operation despite the numeric values. As an example, before you were not able to code an avg method that will work across all types.

Maybe this is just an abstract thing testing my perseverance with Crystal. I trust your judgement based on your actions and knowledge in computer science.

Thank you for your understanding.

And the homebrew formula has been merged. :tada: :apple: :tada:

3 Likes

I have to agree - It broke my code, too. Using // works, technically, but it looks broken :grimacing:

Magically casting integer division to a float is unexpected behavior; developers coming over from JVM, C#, C, or other LLVM languages would expect int32 / int32 to return an int32.

One quick example - in web based financial apps and fintech style apps a common rule is to never use floating point code for basic math or accounting. The (admittedly aging) standard practice is to multiply currency figures out by 1000 (to deal with fractions of a cent). Often the same is done for working win cryptocurrency in satoshis - we just multiply it out to eliminate potential bugs across platforms and languages in fp calculations.

cryptography and networking code both expect a result rounded down to zero like C, so those folks are very likely to be coming back to complain.

// in math expressions, looks like a regex remnant that got lost in the wild and infiltrated its way into your code :laughing:

I like the way Ruby deals with using / for both integers and floats, so a user’s code looks like normal (expected) math expressions.

For integer results: 7 / 2 => 3 or a.to_i / b.to_i

For float results, make at least one value a float: 7 / 2.0 = 3.5 or a / b.to_f

Should be easy for compiler because source code explicitly sets numeric types.

If I remember right, the math symbols are implemented as a method call on the type of the left argument in crystal.

Being a lazy bad programmer that hates unit tests, I often cast var_int / var2.to_f in Ruby to return a float, rather than call result.to_f. But Crystal is using the left-side type. var.to_f / var_int would dynamically return a float, but the other way around would round down to zero, confusing users.

With the rest of the types to dynamically manage I think the core devs decided that consistent output was a better compromise than a massive switch statement and weeks of QA. So they’ve erred on the side of flexibility to soften the edges of the type system. I see the logic in the trade-off considering where most of the user audience will be drawn from - one of the top questions to type system newbies is why division doesn’t return a float - they expect 3/4 = 0.75, not 0.

I don’t know what this phenomenon is called, but I believe when a repo reaches a certain point in time, there is a disconnect that can happen every once in a while. This disconnect is always between the core developers (working on the internals of the language), and the average user. I’ve seen it happen with Godot’s repository (been there for over half a decade), and starting to slightly see it in Crystal’s repo.

An example in Crystal: (Time.now -> Time.local)? Reason? “Because it makes more sense”

Maybe to the contributor / core developers who implemented the PR, but to me, Time.now is far more explicit and less ambiguous than “local” or “utc”. Time.local? Local to what? We already know it’s a local time because we looked at the API! We want the TIME! (.now). This is equivalent to the English language changing how a word is spelled, after 50 years, just because someone thought it sounded funny.

Similarly, in Godot, 5/2 = int, and 5/2.0 = float. Same in the C language. Same for Ruby!
Crystal utilizes several C functions. And after reading this, a developer will inherently assume… this operation would work the same.

In Godot, the author (reduz), created GDScript to be easy to use, have high readability, dynamically typed, dev friendly syntax, lightweight, etc. Over time, a new “warning system” was added which bombarded the developer with any kind of error. Then, static typing was introduced, which now it’s not even simple GDScript anymore. Luckily, it’s optionable, and reduz is minimalist and very conservative about what PRs to merge, etc. That is the only thing protecting Godot from becoming a heap. Which is why I still use Godot, because I have faith in reduz and know Godot is going to be okay. I mean, they even had to create a separate repo (godot-proposals) because of the massive influx of ideas/PRs.

My point is it’s not about a feature creep of new additions to the language (Crystal removing markdown was a good thing). It’s about the use case a core developer might think is okay, compared to what an average user might do. I understand contributors are super important because of their knowledge of the inner-workings of the language. This is vital, however, this doesn’t necessarily mean PRs are objectively correct. PRs needs to make MORE SENSE to the community, not just several contributors.

For example, my issue reporting an arithmetic overflow. The arithmetic overflow error is a GREAT new exception because it makes the compiler stronger and more versatile detecting bugs. These kinds of changes will make Crystal prosper, not / -> // or Time.now -> Time.local.

“But but, girng is a newb, has not contributed at all to the language, why listen to him?”. Because I’m your average user!!