Programming Crystal Book Club

Oh and I tried out Lucky (Crystal web framework), it was interesting to look into a new web framework for the first time since 2017 (and Phoenix). The folks seemed nice too. Which reminds me, I will probably not be reading or reviewing the chapter on web framework in Crystal book. And instead talk about Lucky (the book mentioned Kemal and Amber, and for some reason I didn’t feel connected with either).

Cheers

2 Likes

I’m guessing it is an allowed digit separator in Crystal and they are just taking advantage of that to do a nice-looking separation between the integer literal and its type ‘annotation’. We can do the same thing in OCaml: 25_l is a valid int32 literal.

You’re right, ReasonML and OCaml both allow not only named arguments but also (if you choose to do that) separate internal and external names for the arguments. Same as Swift, actually.

3 Likes

After I’m done with Crystal, I’m going for ReasonML. I don’t even remember why I didn’t continue the last time. Don’t know Swift, nor do I think I ever will.

2 Likes

Chapter 6 - Working with Modules

Modules! Things Crystal require-s.

So modules in crystal are two things, 1. Things you require and 2. Things you include as mix-in. This chapter talks all about it.

It starts with the art of requiring modules. To be honest, I am not sure I like this, require "x" means there is either an x.cr in the same path, or x/x.cr or lib/x.cr or lib/x/x.cr. It does a ../ and ./ for relativity and * and ** for “all” and “recursively all”-s. Too many things to remember if you ask me. Didn’t like this bit. Or maybe I’m spoilt by Elixir.

The “lib” bit was not mentioned in the book, I found that while searching Crystal document because the rules were confusing me. Not anymore.

So after defining the requiring regulations, the chapter teaches about about the dual roles of Crystal modules- as namespaces and as mixins. :: is how you navigate modules to their members, so AdventOfCode::Year2020::Day1.solve would mean, if we used Elixir, alias AdventOfCode.Year2020.Day1 followed by Day1.solve(), I did not see a way to alias and remove pulling the entire family tree of the module, so I guess we’re stuck with the long::form::of::names everywhere (I foresee this annoying me in tests). However, I could be wrong and I didn’t dig for it enough. The book doesn’t have it though.

Modules can have methods, static methods, the def self.* kind, by extending itself, you can be relieved of typing self. over and over again, but I am not sure this bit was necessary, I’d rather keep low number of namespace modules and keep most inside classes, it is after all, an OOPL.

Just when I thought single inheritance was awesome! You can mix-in different behaviors by include-ing modules. And with that, comes method resolution! And the next section, “Modules as Mixins” deals with that.

So, if one includes a mixin, then it is as if one is copy pasting the code of the mixed in module replacing the include site. So what if A inherits from B and B inherits from C and also has a module M and M and C both has the function f and you call A.new.f ? M wins. Since mixin is copy pasting, f is essentially B-s. Not Python or C++ level graph traversal, but still a traversal. Any guess for who will win if multiple modules are included that have the same signatured function?

One thing here, in the book, in case of class A < B, it is mentioned that A is the ancestor of B. But I think the opposite is true.

The chapter then goes on giving examples of Iterator and Comparable mixins, which let you have a lot of built-in goodies in your class.

Finally- a conversation with Diploid, and their software Moon (When I first saw it I thought Crystal got into space tech but Moon is the name of their software which is a medical diagnostics tool). Praises Crystal received here was its healthy combination of good speed and pleasant syntax.

So, module chapter taught me a lot, which probably also makes the readers knowledge of Crystal “syntax complete” (except for macros). Next chapter’s on tooling, which I have read and will describe tomorrow morning, before closing with the “Macro” chapter.

Have a great evening friends!

2 Likes

Chapter 7 - Managing Projects

I am at the penultimate chapter (of the ones I am reviewing). Probably the shortest of the discussions.

After having a near-syntax complete knowledge of the language, we are met with the tooling system in this chapter. Enter Shards. Aptly named, this is Crystal equivalent of Mix and this chapter does not offer anything the documentation does not. You know the drill, initialize, build, compile, test etc- an environment for the programmer to run tasks that aid in compilation or testing of the program without having to write too much on the shell.

There is a small discussion on using external library, with the example of a Logger. And another small section on Testing. This is where I have a problem. I believe, in any book based on modern programming language, testing should have its own chapter early on, and be present throughout the book. Maybe not necessarily the latter but testing totally deserves its own chapter.

Anyways, Crystal test tool is called spec which is, according to the book, like RSpec, never worked with RSpec but spec looked clean enough, only I feel like the book didn’t have much to expand on it and I had to look into the documentation. I still have questions about describe vs context, or maybe it’s my lack of BDD experience? Felt like it was organization/readability thing but I could be wrong. Also, in my example try outs, before and after did not work. Though I could be typing wrong, another trial is in order for tomorrow.

Adding a Shard seems simple enough, as it should be in all modern languages. And the Benchmarking section too left me wanting for more. While one can say that being a typed language, one is saved from a lot of extra tests and therefore, less emphasis on test compared to, say, Ruby (I do not agree with the one), but for a language and a book that has performance as selling point, much more emphasis should have been given on the benchmark section. And while we are at tooling, an interesting debugging story should have been told.

The chapter ended with sequel to the Diploid discussion (first sequel I’ve read), but the discussions made it sound more like a prequel, good read nevertheless! More praise for performance and syntax, with a screenshot of Moon (the software, not the satellite).

Wow, this is the first time I had a little criticism about a chapter of this book. But nothing’s perfect and I have enjoyed 75% of the book with only small concerns (I am a very critical person), so this is not that bad, and this is my opinion.

So what next? Looks like I have one more chapter to discuss, Chapter - 8: Advanced Features, I actually read the chapter and will have (and this time good) things to say about it tomorrow, right after I am done with Love, Death and Robots (I am disappointed with the first two episodes). After that discussion, I will talk a little about how this book made me smarter than I was 2 weeks ago and summarize the experience. And then move on to my two parallel reviews: 1. Concurrent Data Processing in Elxir and 2. Programming Phoenix LiveView.

Have a great weekend everyone.

2 Likes

This is becoming a recurring pattern among new languages I’m noticing. For example, a use blah; will first look for a child module of ‘this’ module with the name blah, it’s the same as use self::blah; if you want to force it without any other lookups, if that doesn’t exist then it looks for a linked in library named blah and uses that. You also have super::blah to refer to a sibling blah module or a crate::blah to refer to your library root with a main module named blah (with of course being able to double-colon index in to them like use crate::blah::blorp::{a_func, AStruct, another_module::*}; or whatever. When looking for a given module like blorp that can be defined in the same module’s file like mod blah { ... }, or it can be a blah.rs file adjacent to the current file, or it can be a blah/mod.rs (the full form, needed when it can have other child modules), it’s consistent at least but definitely not the C/C++ way, lol.

The mixin thing sounds like the difference between open and include in OCaml/ReasonML as well.

How does Crystal handle logging? I’m not actually a fan of Elixir’s way. I like how Rust does it, which is to just supply a set of extremely efficient log calls with the usual ‘levels’ of either a log or tracing crates style (log is simple text messages, tracing can be data, but you can delegate it to a log handler), and you can have a whole multitude of different libraries actually do the handling of the logging of whatever style that you want, like env_logger is one of the most simple, configured by environment variables on startup. I like to use log4rs, which is familiar to me of a java’ish style logger system, very configurable and pluggable. Regardless of the logger any program works with them all, not just a single hardcoded set like elixir has for example.

1 Like

The book gave the example of a logger library Katip as an example. It looked like you take a LOGGER object and configure it through a block and log away. I’d love to hear about why you’re not a fan of Elixir’s logging.

ReasonML is the language I am learning next. I’m afraid I may not use Crystal for now. It’s a good language, but I like keeping my brain well meshed with the functional/immutable worldview of things. Once I finish up this book I will start keeping a journal of my ReasonML journey here (No books for now, just free roaming).

I will write about the “Advanced Chapter” in a day or two. The weekend was so full of chores.

3 Likes

Maybe write a #community:journals for that as well Mafinar? I am sure many people will find it interesting :smiley:

1 Like

Yes of course I will. This is not the first time I attempted to learn an ML. I tried with OCaml, F# and even ReasonML numerous times and then backed out for some reason. Especially I went far with F# then all the .NET specific terms came up and I backed out. I love the language though.

I hope with a #community:journals it would work for me. I know I learned Crystal and completed a book from it.

2 Likes

There you go: My OCaml Journal

Had to switch to OCaml at the last minute, I did not like ReasonML documentation and the fact it took my a long time to get VSCode to work.

2 Likes

It has a lot of features, but it’s not pluggable. You can add backends to go through sure but if you are wanting ‘more’ capabilities then you can’t really do that in the existing framework. This is why I like how Rust’s works, it just supplies some basic ‘points’ that a logger can plug in to, then it can do whatever it wants, it’s significantly lower level though, but much more powerful.

For example, not only can you log text messages with warn!("blah") or whatever, which does also give a huge variety of information about its calling context (which, of course, the logger can do more to) but you can also add more information like warn!("blah", user_id=user.id, some_more=metadata) but you can even ‘span’ logs across more than just a single point, so you could do, for example, let _ = warn_span!("blah", ...) and it will be held in that entire scope, giving wrapping other logs that happen during it (like deeper in the callstack), can give timing details if the logger so wants it (or no cost if it doesn’t), etc… It is pluggable even with profilers, which makes timing code easy as well, you can do that in Elixir for example but it requires using the tracing infrastructure, not part of the log system and it’s part of the beam internals and doesn’t always correspond to elixir code due to its code generation.

I very much prefer OCaml straight over ReasonML. ReasonML just transpiles straight to OCaml anyway (and it can convert OCaml to ReasonML, it’s a lossless conversion, other than formatting just going bonkers, lol). But the OCaml syntax is so excessively more pleasant for me than ReasonML (which was designed to be a javascript’y syntax for OCaml, so it’s a lot more noisy).

2 Likes

I am back in this thread with a small review.

I have mentioned in the past that I will only skip the chapter involved with Web Frameworks in the book because neither of the frameworks (Amber and Kemal) really clicked, not to say they are bad frameworks, quite the opposite (I actually motivated a friend of mine to use Amber in his start-up for a small service and he loved it)- it’s just that I feel like it wouldn’t give me anything new. Unlike a programming language, investing in a specialized library or framework is a more time consuming one as it involves you to think up a non-trivial end product and go after it. Kemal is just Sinatra (or any of it’s brethren i.e. Express or Flask) crystallized.

But what I missed mentioning about is are the appendices. These are often not reviewed or talked about but in some cases, could give you a boost to get on with the rest of the book, In this case, it’s the latter.

There are three appendices here- 1. Setting up the environment, 2. Ruby to Crystal conversion and 3. Answers to the exercises. So the third one is your typical appendice that you only use as needed and may never care beyond that. But the setting up the environment one was pretty good. Except for the one where they claim about “nice IDE experience” in relation to VS Code. I don’t think that’s anywhere near nice. I can’t speak for other editors. But, I had forgotten about “Sublime Editor” when talking about Onivim to a former colleague of mine yesterday, I kept refering to as “… that fast editor that kept asking for money which I never used and promised not to ever use…”, she happened to forget the name too. So this appendix at least gave me that aha moment.

The next appendix, porting Ruby to Crystal, is just a table with syntaxes of the languages side by side. I am not a Ruby programmer but I can feel the convenience of having it. If one is a Ruby developer, I’d suggest skimming through that little table before any else. It’s weird, Java and Clojure belong to the same VM, but a table converting one syntax unit to other in entirety would not be useful at all (I am talking as a whole, not just the interoperability part, that’s useful). Same goes for Scala and Clojure. But Ruby and Crystal makes sense, they’re both the same paradigm-wise and philosophy-wise, has an overlap in the community and libraries with similar pattern. This makes total sense. Probably, Erlang and Elixir would be in this group too, but I think Ruby knowledge gets you close to Crystal knowledge faster than Erlang would to to Elixir, initially (eventually the role is reversed, knowing Erlang well helps with in your Elixir journey better as you tackle advanced things, Ruby/Crystal, may not be as much).

So, this is my penultimate thought regarding this book. Should have been the last one but I kept the advanced chapter, which I would tomorrow. After this, I will just note down any Crystal things I do.

One nice side-effect of this book though, I feel like I know more Ruby now! :smiley:

3 Likes

If you want to learn an interesting web framework that is ‘different’ I’d recommend Rocket in Rust, it’s method of handling ‘how’ a route is acquired and the variety of ways it can be skipped and so forth is fascinating!

Something more Phoenix-like in Rust in Warp, it’s concept of filters is essentially identical to Plugs in Elixir. ^.^

(Honestly Rocket’s style is like plugs/filters but baked in to the very types that the route function accepts, which makes it amazingly succinct and clear compared to the filter style!)

2 Likes

I do want to learn a web framework that is different. Only problem for me with Rocket is, I have 0 Rust knowledge. I do have it in my to learn list.

2 Likes

After OCaml, I’ll start a thread on Rust.

2 Likes

Chapter 8 - Advanced Features

Finally, my final chapter! This is an interesting one, I learned quite a bit from here, though I think each of the major features deserved its own chapter instead of a section.

So, advanced crystal here is comprised of-

  1. Macros
  2. Concurrency
  3. C-binding and
  4. Database Access

First, let’s check out Marco. I remember teaching Macro to a Django Developer colleague through Django templating language. It was very effectively communicated I believe. I was happy to see Crystal being much closed to the Django Template syntax than Clojure (my only macro example back then). It has {{}} and {% for <loop> %}...{% end %}. I think the chapter does great in explaning macros both in terms of what they are and how they are implemented in Crystal. method_missing, inherited, extended and included are hook-ish Macro-s that you can define to metaprogram on specific events (i.e. what happens when a method is missing? A subclass is defined? etc). I only tried out method_missing on playground, rest will follow. But it does seem straight forward.

Next comes C binding. This too looks quite simple in Crystal (compared to any other language I used). I got introduced to “annotations”, “lib” and “fun” syntaxes! I don’t recall having seen them described earlier. Anyways, upon visual inspection- lib is grouping of C libraries and fun inside a lib is a signature by signature mapping of same named C functions (More new syntaxes: * at the end for pointers that were introduced in this chapter). Annotations would map the C library with the following lib block. After this setup is made, we’re pretty much back to Crystal-land calling the FUNctions.

However, upon inspecting the Crystal Docs, there seems to be a lot more in binding Crystal with C than it was mentioned in the book (though it is suggested to reach out to that link to know more). Examples include, unsafe, enums, threadlocals, callbacks and many more. I think C <> Crystal is a big selling point for Crystal and from whatever I have skimmed through (have not executed anything) I do seem to be impressed at the simplicity of it. I would have been a lot happier if there were an example that does more than just a C void greet(const* char) function being called from Crystal, but something a little more. Perhaps the temperature converter mentioned in the first chapter? Or the Mineral system in the subsequent ones converted into struct?

One other thing, this chapter mentions once- “C is a strongly typed language” where it should have been C is a statically typed language. “Strong” and “Weak” typing is rather relative in my opinion and also in my opinion, “Strongly Typed” is not really a strong suit of C-s, it’s rather on the “weaker” end when it comes to “strength” of typing. Again, very nit. Also, I got to learn of system in Crystal which lets you execute an OS command (as does backticks, though not mentioned in the book).

Anyways, this book is not a reference and it DID do a great job of pointing me to the right direction after motivating me enough. Also, this chapter, particularly this section, had inspired me to continue playing with Crystal, so that too is a good thing.

After C-binding came concurrency. Crystal concurrency is like Go concurrency where go is spawn. You have channels, channels can be buffered, selected, yielded etc. The unit of concurrency is called Fiber. I think anyone understanding the conveyor belt metaphor and Go’s way of concurrency will find it easier to understand Crystal’s point of view. And in my opinion, Crystal does it with a much more beautiful syntax:

def generator(n : T) forall T
  chan = Channel(T).new
  spawn do
    loop do
      sleep n
      chan.send n
    end
	end	
  chan
end

ch1 = generator(1)
ch2 = generator(1.5)
ch3 = generator(5)

loop do
  select
  when n1 = ch1.receive
    puts "Int: #{n1}"
  when f1 = ch2.receive
    puts "Float: #{f1}"
  when ch3.receive
    break
  end
end

^ Another example from the book.

Final section for this chapter was DB integration. The pattern is simple:

DB.open do |db|
  db.query sql do |r|
    <do your thing with `r`>
  end
end

And that db also has exec, query_one, query_all etc. It only briefly mentioned Transactions at the end but looking at the design pattern, it’s simple to guess: db.transaction do |tx| ... end, I verified my guessing from Crystal docs.

So this was the final chapter highlights from my side. I left one out (the one with web frameworks) but I will revisit it from a “meta” viewpoint if I look into a Crystal framework, but that will be my highlights on the framework, not the chapter, and it may happen much later. As far as chapter-by-chapter exploration is concerned, I am finished here :smiley:

I could go on and on about how this book club journey has been for me (It was obviously good, I learned a language in ~40 posts!), but I will keep that for my upcoming post where I will summarize my experience, as for conclusion of this chapter/post, I will summarize my overall feelings of this chapter.

It was a “heavy” chapter, a lot of concepts to swallow in one unit. I wish this chapter was a PART with at least three chapters- Advanced Syntax (Macro + Generics maybe?), C-Binding, and Concurrency. The chapter on Database could’ve been merged with the Web Frameworks chapters. However, it is my opinion based on my worldview of things and this really is not a bad thing of this book. This chapter did a great job by having me see all these things and occasionally try them out in one weekend evening. I understand those things are out there in the docs, but the book prepared and outlined all the things and placed it in the right sequence of the ToC and got me there, if I am ever to deal with a problem in Crystal that does any of these, I am prepared, the remainder of the execution is the reference docs and my effort’s job. Despite my nitpicks and complaints, I will give it 4/5, based on the benefits I received from reading it!

Yes, I will continue with Crystal, and possible this year’s Advent of Code will be divided between Crystal and OCaml for me :smiley:

2 Likes

One pain point of my using Crystal! The crystal play borne playground is painful! When I copy the code from the book and paste it there, clicking on Run Formatter does nothing. The error labels often do not go away, and it crashed on my multiple times. Not sure it should be supplied with a Version 1.0 system.

Maybe Elixir spoiled me, but having a good interactive system (along with good IDE integration) receive strong points from me and Crystal receives low points in them both.

2 Likes

Entirely correct. C is a statically weakly typed language. OCaml or Rust would be statically strongly typed languages. Python would be a dynamically strongly typed language. Javascript would be dynamically weakly typed. ^.^

I have a blog about these definitions somewhere… >.>

How very windows of that term. Generally greenthread or microthread is the term, but windows had an API for stackframe swapping that people (poorly) implemented greenthreads/microthreads on called the Fiber API. ^.^;

Oooo that gives me an idea for a rust async description…

OT but there are a couple libraries for C called libdill/libmill that add similar concurrency, one of them is a go-style system and the other is more traditionally C style, but same backend for both. ^.^

I’m curious about hearing of your comparisons. OCaml I really love as a language, though you can definitely feel a lot of the cruft in its age, it still ranks as one of my favs. I still say look at Rust, Rust was originally built on OCaml after all. :wink:

1 Like

You know what, I think I really should. I never fought back with the compiler so my Rust progress never happened beyond a day or an hour. I should leverage this social learning spree of mine and see if I can get any Rust.

Do you suggest any learning resources? Any books? Working by doing didn’t seem to help much with it.

3 Likes

The official “Rust Book” on the main website is fantastic learning material, but I’m of the opinion that 1-on-1 help is often the easiest and best way to learn (I’m a fan of socratic style teaching). I have some time at work today (yay Fridays) that if you want to then we can. I’m on IRC and Discord if you have a preference. I mainly use Clion and it has awesome pair coding functionality (though sadly I can’t use the mic/audio while at work) though I think vscode has similar features as well, I can use either if you want pair coding. :slight_smile:

2 Likes