Crystal Syntax Questions

Hello, I am a new dev, I am experienced in Python, but not ruby, or crystal, I am excited to learn crystal, but I had some questions:

  1. Method Parameters - When would you use @param and when do you use param? I was looking at the reference guide, and was kinda confused.

  2. Structs vs. Classes - What is the difference, and when should one be used over the other?

  3. What are Modules? - Title explains it really, just what are modules, and why would you use them?

  4. Plotting Shards (Frameworks, Libraries)? - Any advice on this would be appreciated.

Thanks in advance!

1 Like

Hello @poett , and welcome :slight_smile:

Have you looked at Introduction - Crystal?

Here are answers for your questions:

  1. Methods and instance variables - Crystal
  2. Structs - Crystal
  3. Modules - Crystal
  4. "plot" Search on Shardbox

Be free to ask here if parts of the book are unclear.

Welcome to Crystal @poett!

We have a few different types of variables in crystal, and the different types allow for different levels of scoping. Variable with no symbols in front like param are local variables that are available within the local scope (i.e. inside a method). The variables with the @ in front are instance variables which are scoped to the instance of an object.

class Person:
  def __init__(self, name):
    self.name = name

dude = Person("Jeremy")
print(dude.name)
class Person
  getter name : String
  def initialize(@name : String)
  end
end

dude = Person.new("Jeremy")
puts dude.name

In the example, dude is the local variable, and @name is the instance variable

This one still gets me, but basically the difference is how they are handled in memory. The simple answer is Struct is more performant, but less flexible. Class is more flexible and less performant. That’s an over simplification, but gets you started.

A module is a mixin. It allows you to define methods in one place, and use them in several classes. They can do a ton more, but if you’re familiar with the concept of mixins then that should help.

module Logger
  def debug(thing)
    puts "********"
    puts thing.inspect
    puts "********"
  end
end

class Graph
  include Logger

  def plot(point)
     debug(point) # this method was defined in the Logger module
  end
end

class Point
  include Logger

  def draw(x, y)
    debug(x)
    debug(y)
  end
end

I’m assuming by “plotting” you mean doing graphs and stuff? Here’s one I found. But just searching on https://shardbox.org is a good place to start for that sort of stuff.

I hope that helps answer some of those. I saw @j8r also posted some pretty helpful links for you to check out. Enjoy!

3 Likes

Just to add to the otherwise excellent information given above:

If you see param in a method inside of a class, struct, or module, it can be one of two things: a local variable or a method call. Since the local variable case has been discussed above, I’ll focus on method calls. In Crystal, the parentheses on methods are optional in many cases, so param can be the same as param(). Something you’ll see a lot in Crystal is a pattern like below:

class Something
  getter var = 10

  def do_thing
    puts var * 2
  end
end

In the class above, there are 2 things that you might not be familiar with from other, non-Ruby languages.

First is getter: basically, it creates an instance variable for you and then also creates a getter method with the same name; in this case, the name of that method is var.

The second thing is the line puts var * 2. In this line, var isn’t directly using the instance variable. Instead, it’s calling the getter method that we created earlier using the getter macro.

Much of the time, you don’t have to worry about this distinction, but you can run into certain issues if you don’t remember it. For example, in the class above, you can’t just write var += 2 because we don’t have a setter method for it. If you wanted to do that, you’d either have to say @var += 2 (directly accessing the instance variable) or change our getter var... to property var..., which creates both a getter and a setter.

Now, about @var. Say we actually want our Something class to be able to take a value for @var in its constructor (and we want to be able to set it later). We could do this:

class Something
  property var : Int32

  def initialize(var_to_set)
    @var = var_to_set
  end

  def do_thing
    puts var * 2
  end
end

Note that we must use the @ when we’re setting variables in our initializer. We can’t just use our nice setter method that property gives us because… I think it’s because you don’t actually have an object to call it on yet, but I’m admittedly a bit shaky about why. A lot of the time, you’ll see classes that have the @var only in the initializer and use getters and setters everywhere else. It’s usually just nicer to use the getter if you can because @ is (subjectively) ugly and we like to put things behind methods in case our underlying representation needs to change.

Also, just to make your life easier in case you haven’t learned this yet,

def initialize(var_to_set)
  @var = var_to_set
end

can be shortened to

def initialize(@var)
end

That second syntax is just a cleaner shortcut for the first one (I think it is probably expanded out into the first one during compilation).

Anyway, I hope that our combined explanations can help you out! I also came to Crystal not knowing any Ruby, and a lot of stuff initially confused me (e.g. returning stuff without the return keyword). Luckily, people in this community has been really patient and helpful when I have questions.

4 Likes

This is required for the compiler to be determine that the instance variable gets initialized. Otherwise it wouldn’t have a value.

Besides that, your explanation is very well written. Perhaps you could add some of that to the language reference?

3 Likes