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!
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.