Static Typing

Crystal for Rubyists

Crystal for Rubyists

by Kirk Haines

The video titled "Crystal for Rubyists," presented by Kirk Haines at RubyConf Mini 2022, introduces the Crystal programming language, emphasizing its similarities and differences with Ruby. The session encourages Ruby developers to leverage their existing skills to explore Crystal effectively.

Key Points:

  • Overview of Crystal:

    • Crystal is a compiled language with syntax nearly identical to Ruby, allowing much Ruby code to compile without changes.
    • Developed with Ruby principles in mind, Crystal aims to provide performance through a static typing system while maintaining Ruby's programmer-friendly syntax.
  • Benefits of Ruby:

    • Ruby is known for its user-friendly syntax, dynamic typing, and a robust set of tools that facilitate project management and testing.
  • Ruby's Limitations:

    • Challenges in distributing Ruby applications due to dependencies.
    • Performance issues and concurrency complexities in Ruby.
    • Although Ruby has adopted some static type-checking features, it remains largely dynamic in nature.
  • Crystal's Advantages:

    • Being a statically typed compiled language, Crystal addresses Ruby’s performance and distribution concerns.
    • Offers static type-checking, type inference, and compiled binaries for faster execution.
    • Facilitates concurrency modeling similar to Go through processes and channels, simplifying complex interactions and eliminating low-level management.
  • Syntax Comparisons:

    • The presentation illustrates similarities and differences in handling arrays, method naming (e.g., inject vs reduce), and type requirements (explicit type specification in Crystal).
  • Testing and Libraries:

    • Crystal includes an inbuilt testing library called Spec, which resembles RSpec, enhancing familiarity for Ruby developers.
    • Differences in package management, where Ruby uses gems and Crystal uses shards.
  • Practical Examples:

    • The Fibonacci calculator example showcases performance comparisons among Ruby, Crystal, and Rust.
    • The session invites attendees to rewrite Ruby logic in Crystal to facilitate hands-on learning.

Conclusion:

Kirk Haines emphasizes that Crystal is distinct from Ruby, designed to ease the transition for Rubyists while providing enhanced performance and capabilities. Developers are encouraged to explore Crystal for various projects, especially in web development and integration with C libraries. The video concludes with engaging discussions and collaborative coding opportunities, inviting further exploration of the language and its community.

00:00:16.440 My name is Kirk Haines. My day job is as a developer relations engineer for.
00:00:22.320 Parity Technologies, but I've been doing Ruby for a long time—more than 20 years. Today, we're going to talk a little bit about Crystal, and specifically, Crystal for Rubyists.
00:00:27.599 What that means is: what does Crystal look like? What is it? And how can you leverage your existing Ruby knowledge to be productive more quickly with Crystal? The first part of this will be somewhat like a talk, as I would like to give an overview of various topics.
00:00:39.420 However, it won't be a typical talk in the sense that I will hold questions till the end. If you have anything to ask, or you want to discuss something that appears on the screen or anything I say, feel free to interrupt me. Let's engage in conversation and see where this goes.
00:00:50.399 The entire slide deck, along with some code used in some of the slides, examples, and a few exercises, are all available on GitHub. You can access this through the QR code. If the QR code is too small for your camera to read, you can grab the URL here to go to it. If you want to download it to your laptop to follow along directly, you're welcome to do so.
00:01:01.680 I'll give you a moment to grab that, and then we'll move on.
00:01:08.000 While we're waiting, let me ask: who here knows what Crystal is? Quite a few of you; cool! Now, who here has actually written a line of Crystal before? Okay, cool!
00:01:18.659 It's sometimes kind of surprising to me how much Ruby code will simply compile with the Crystal compiler because the syntax is so similar. There are a lot of similarities.
00:01:31.379 If you need me to go back to that QR code, let me know. But otherwise, let's talk a little about why Crystal. We're all here because we like Ruby, and many of us have been using Ruby for a long time.
00:01:39.420 There are many benefits to Ruby, and you can make a pretty exhaustive list of them. This is just a few quick ones that many people tend to agree on: the blocks, the user-friendly syntax, and the fact that Ruby is a very versatile language. You can use it for a lot of different things.
00:01:51.480 Ruby has some interesting writing mixed with functional programming, which enhances its usability and versatility. One thing that honestly drew me to Ruby initially is dynamic typing. Most of the time, you don't really have to worry about what type the variable is; you let Ruby figure it out and tell you if you made a mistake.
00:02:07.260 Ruby also has a robust set of tools built around it, along with a strong testing ecosystem that supports developers in structuring their projects and makes them easier to work with.
00:02:25.980 However, there are some drawbacks to Ruby, and I know we can debate some of these points, but generally, if you’re trying to distribute a Ruby program or binary, it’s really difficult to do.
00:02:35.400 There have been some projects aimed at addressing this issue for quite a long time, but none have ever really taken off or dominated the space, as it’s a really tough problem to solve due to the many dependencies involved in Ruby code.
00:02:45.840 You can write applications in M Ruby, which is a language that resembles Ruby. However, it’s not directly Ruby due to some significant differences, and it will compile into an executable. But you can’t just take your Rails app and turn it into an M Ruby executable.
00:03:01.020 Ruby's performance has always been a topic of discussion. With Ruby 3, performance has significantly improved, but nobody writes Ruby to be a lightning-fast 3D rendering engine; that’s not why we use it. Its performance is relatively mediocre.
00:03:12.000 Additionally, concurrency in Ruby is interesting due to the different approaches available. You've got fibers, threads, reactors, and evented systems, which can make managing concurrency a bit of an adventure at times.
00:03:23.160 Static type checking was introduced in Ruby 3, and arguably a little before that, due to the Sorbet project. Ruby 3 includes RBS for static type checking; you can use it to determine type-related errors, but it’s more of a bolt-on solution and has its limitations.
00:03:30.960 It’s not entirely fluid to use RBS while writing code.
00:03:35.880 These are some drawbacks that Crystal addresses. Crystal is syntactically extremely similar to Ruby. If you look at this code snippet, the first example is Ruby, and the next is Crystal; the only difference is that in Crystal, when declaring an array, you need to specify the type of data it will store.
00:03:47.630 The typing information adds just a bit of extra syntax, which you can see in the last line where it specifies 'Array(Int32)'. That's the only significant difference.
00:03:58.300 Crystal is a compiled language that compiles your code into an executable built on top of LLVM. Anyone here use Rust? Well, Rust compiles down to LLVM, too, creating a shared foundation between the two.
00:04:08.400 This is just a snippet that shows that building something with Crystal results in an ELF executable on my system. The compiled binaries are also fast to execute.
00:04:19.680 In the GitHub repository available through that QR code, there's a folder called 'fib bench' that contains four implementations of a simple Fibonacci calculator. It calculates Fibonacci numbers recursively.
00:04:31.800 On my laptop, these are the timings for those implementations. You can see that Rust is a bit faster, but Crystal outperforms everything else by a substantial margin. In this case, I calculated the 42nd Fibonacci number.
00:04:40.440 If I go beyond that on my laptop, Ruby becomes exceedingly slow. Conversely, if I choose a smaller number, everything happens too quickly.
00:04:51.300 Crystal provides solutions to some challenges while retaining versatility and productivity, all with a syntax similar to Ruby.
00:05:02.160 If you don't have Crystal installed on your system and you're interested in doing so, it's pretty easy. During our session, if you'd like to play around and ask me questions, go to crystal-lang.org/install for guided installation instructions for your operating system.
00:05:12.360 If you need a moment to grab that URL, I can wait.
00:05:17.460 But remember, if you want to experiment without installation, you can use play.crystal-lang.org. It's a browser console where you can write and run code.
00:05:27.060 Next, I'll quickly summarize the differences between Ruby and Crystal at a high level, but feel free to interrupt with questions as we proceed.
00:05:36.060 While Crystal looks a lot like Ruby—which is true, given that a lot of Ruby code compiles with the Crystal compiler—it also has significant differences.
00:05:46.500 Crystal is a statically typed compiled language, lacking many of the dynamic features that Ruby possesses. For instance, there’s no eval or send in Crystal; while it’s possible to achieve similar functionalities through other mechanisms, those methods do not exist in Crystal.
00:05:57.779 Consider that it's not merely a variant of Ruby; it's a distinct language that leverages Ruby concepts extensively.
00:06:06.899 Importantly, Crystal's similarity to Ruby arises from its history: the original Crystal compilers were built with Ruby, and the goal was to create a compiled language that feels and looks like Ruby, but is compiled and fast.
00:06:21.300 To clarify the first distinction: Ruby is interpreted, while Crystal is compiled. However, an interpreter for Crystal is currently experimental.
00:06:30.060 You can actually run a large subset of your Crystal code as interpreted code.
00:06:39.180 While Crystal is dynamically typed, Ruby is dynamically type-checked, meaning the Ruby interpreter checks the types of your data at runtime.
00:06:50.820 Any mismatches become apparent only when you run your code, whereas Crystal is statically type-checked, ensuring type correctness before compiling.
00:07:00.060 Ruby does include static type-checking through RBS or Sorbet, but it is not integrated directly into the language.
00:07:09.600 Here's a quick example: in Ruby, if you try to add a string to a number, you get a type error.
00:07:14.220 Doing the same thing in Crystal will also yield an error, but it will occur at compile time, providing an explanation of what went wrong.
00:07:25.380 Also, Crystal has type inference, meaning it can often deduce the types without needing explicit declaration.
00:07:34.740 For instance, if you have an array defined with strings, Crystal can infer it’s an array of strings.
00:07:45.060 In cases where types are ambiguous, you would still be required to explicitly define the type.
00:07:54.500 For instance, in both languages, you have flexibility. If you don't declare any types, it will work similarly in both Ruby and Crystal.
00:08:06.020 Crystal, having been inspired by Ruby, retains many identical method names so that a lot of code functions similarly across both languages.
00:08:15.600 For example, the code here will work the same in either language, except when it doesn’t.
00:08:25.740 Ruby has a method called time.now, which doesn’t exist in Crystal. Instead, Crystal has time.local and time.utc.
00:08:34.440 It took me some time to realize that it didn’t work for this reason, which may confuse new users.
00:08:40.800 Similarly, in Ruby the inject method of an array is called reduce in Crystal.
00:08:50.580 Some naming changes are inspired by languages like Go, where the authors feel specific names are more appropriate.
00:09:02.520 In the context of our Fibonacci example, both languages provide succinct ways to compute Fibonacci numbers.
00:09:09.780 The Ruby code example demonstrates that Ruby will automatically cast large integers into BigInts, while in Crystal, we must specify the BigInt from the start.
00:09:20.040 Overall, both codes look very similar, but Crystal requires that extra bit of type information.
00:09:30.600 Another key difference involves concurrency. In Ruby, we have fibers, threads, and reactors, with optional event-based concurrency.
00:09:39.480 With fibers, you manage them manually through declaring them, yielding to them, and resuming. This complicates the code, especially for intricate scenarios.
00:09:50.640 To be more approachable, Ruby offers threads that you can manage via queues, which provides a particular design.
00:10:00.720 In Crystal, the concurrency model mirrors that of Go, using processes and channels which streamline interacting without managing low-level details.
00:10:08.540 This approach allows for easy communication through channels, so you send data in one direction and retrieve it from another, hiding a lot of complexity.
00:10:20.880 You quickly have a coordinated system of processes communicating through these channels, simplifying concurrency handling.
00:10:31.380 Some additional differences: Ruby libraries are referred to as gems, while Crystal libraries are called shards.
00:10:38.640 Similarly, while Ruby has a gem command, Crystal has a shard command. The low-level management of these libraries is a bit different.
00:10:48.840 In Crystal, descriptors are created in a YAML file, which differs from Ruby’s capacity to describe libraries through code.
00:10:57.000 Yet both languages still provide functionalities, allowing you to manage your libraries effectively.
00:11:05.880 Testing is another strong point for Ruby, with a robust testing culture that supports testing libraries such as RSpec and Minitest.
00:11:13.560 Crystal offers a similar built-in testing library called Spec, which closely resembles RSpec and should feel quite familiar.
00:11:22.560 There’s also an implementation of MiniTest in Crystal, effectively ported from Ruby, representing the testing methodologies well.
00:11:32.760 So when it comes to testing in regard to syntax, if you can read RSpec, you can read Crystal's tests.
00:11:42.660 To wrap it up, Crystal is not Ruby, but its syntax is closely aligned, allowing Rubyists to transition comfortably into Crystal.
00:11:55.200 At this point, I am done with my presentation, and it's your turn. We can pair program, discuss topics, or work on exercises.
00:12:04.300 One valuable approach when learning Crystal is to take Ruby code that you're familiar with and rewrite it in Crystal.
00:12:11.400 This can illuminate small differences and sometimes lead to significantly different approaches, highlighting the learning opportunities.
00:12:21.300 One suggestion is to utilize Exorcism, which provides programming tasks that teach individual concepts for various languages.
00:12:31.200 Exorcism has both Ruby and Crystal tracks, allowing you to learn and practice concepts in both languages.
00:12:41.400 Another idea is to take a Ruby program like a dice roller for role-playing games, found in the repo, and implement it in Crystal.
00:12:50.700 In the repo, you'll also find a stub of the Crystal implementation, which would make for an interesting exercise.
00:12:59.400 If you find yourself stuck, a finished version is also available for reference.
00:13:05.400 Ultimately, I'm open to whatever you are interested in discussing, whether questions or collaborative coding.
00:13:13.200 I'll be happy to help, and you can fire away with any questions!
00:13:17.000 One question asked was what features I find Crystal lacking that would lead me to choose Ruby instead.
00:13:25.600 Most projects intended for sustainability can be approached effectively with Crystal or Ruby.
00:13:33.800 When dynamic code evaluation is essential, Ruby may be preferable. Crystal does have macros, providing some flexibility, but the ease of dynamic evaluation is missing.
00:13:43.100 For small, non-critical projects that may not last, Ruby may lend itself better due to its flexibility and lightweight capabilities.
00:13:51.300 As for real-world implementations of Crystal, it’s gaining traction, particularly in web development where robust frameworks exist.
00:14:04.600 A notable project is Place OS, an infrastructure management system widely used in big companies like Cisco and hospitals.
00:14:12.360 There are even gaming development projects leveraging Crystal for its ability to integrate seamlessly with C libraries.
00:14:20.580 In the core development community, discussions are ongoing regarding implementing features borrowed from Ruby and other languages.
00:14:30.240 Crystal's evolution involves innovations like pattern matching, which are broadly discussed for potential inclusion.
00:14:38.160 Regarding class management, yes, you can reopen classes in Crystal to add new methods, similar to Ruby.
00:14:46.920 Interestingly, Crystal itself is implemented in Crystal, allowing for extensions and comprehensive modification.
00:14:55.900 Crystal's community is vibrant, fostering contributions from diverse programmers eager to develop the language further.
00:15:05.600 If you want to continue the discussion or engage in collaborative learning, we still have time!
00:15:13.600 You’re all welcome to mingle, ask further questions, or explore the topic deeper. Thank you all for being here!
00:15:23.200 It’s been a pleasure—thank you!