Like most tutorials here we will be talking about the crystal programming language which is a compiled ruby syntax like programming language designed for server side development and automotive systems. This tutorial will go into data types, JSON, ECR, Classes, Modules, Methods, Method Arguments, Arrays, Loaders, Servers and finally building your own router to understand exactly how crystal works as a programming language
Crystal was a language that is still in the experimental phases which was released back in 2014 with a idea to be more like ruby but compiled. This language is used mainly for server side development and as defined by https://nikolamotor.com/ used as a base for automotive systems. This language is a really odd one but fits a wierd syntax for a compiled language. This language is reallyt for people who also enjoy the ruby syntax with most things such as basic variables, routers, importing and modules and classes being setup differently.
Crystal is an easy language to get around and you can understand such as deep amount about it if you come from ruby. For example below is how you initialize variables.
variable1 = "Data"
you can also init variables with @
symbols which make them instance variables for example.
@var : String
@var2 : String | Int64
you can use the |
to make it a or operator saying that variable can be either String or Integer64. This actually is a decently typed and developed system for variables as most languages do not allow you to do that.
you can also assign multiple variables at once
a, b, c = 1, 2, 3
pretty simple and readable right?
well now you can also output those statements and variables in different ways using puts like so.
a = 1
puts a
puts "#{a}"
what about methods? Methods in crystal are a bit wackier to some and have some really useful ways of initializing variables. First off like ruby you can call what function or variable of the method you are assinging a variable to. For example
def function2(data)
puts data
end
function2(data: "value name")
which makes it alot easier to assign certain values using those functions. there are a few ways of defining data as well as you can define the data type of the variable inside of the method like so
def (x : String)
puts x
end
x must be a string for you to work with it. Like definitions and methods we have classes and modules. Starting a class is simple in crystal just use the class keyword followed by the class name and a definition.
class Classname
def task
"data" + self
end
end
what this class does is defines a method called task and under that returns or adds a string to the current value so if you wanted to call that function or method you would use.
class Classname
def task
"data" + self
end
end
def Classname.askin
"Data value two"
end
puts Classname.askin
will output
Data value two
The reason this happens is because we are using the class to define a new method not the same method. Also do note that we would need to construct a class named String
to make this output the way we need to. So for example
class String
def task
"data" + self
end
end
puts "more data".task
this will output
more data data
because we called the class name with the data type then proceeded to output a string then adding .task the name of our method under that class. Doing this with other data types is a bit hard as for some reason crystal sees String as a class but every other data type as a structure. So if you switch class String to class Int it will call it a struct and error out.
this makes code time alot more efficient the further you get into the language and gives you the most basic understanding of how methods can be used. Functions and methods also have return types which you can call in two ways. if you want more than one argument you use {datatype, datatype}
and if you want to return only one data type you use :
for example say we have a method that adds two values and returns the final single value
def multiply(x : Int, y : Int) : Int
return x*y
end
will return of typer integer and return the multipled values. So in the after definining a function you can go ahead and mark the return type after the () with : . Now as said before we can use a list of return types using {} which looks a little like this \
def afunction : {Int, String}
return 1, "some string"
end
Do you understand how helpful this can actually be? I think this is very neat because of how interesting the type system works with methods and in general. This type of definition will help you in later more advanced code bases with crystal. As you can see the basics of crystal are easy but it does get a bit wackier towards the end but either way still pretty good for a compiled language right?
- Task for this section: Build a basic application that will take the input of a single strign using a class and mash the first and last name together where the first name is static. Do not forget that definitions can not start with a capital letter because it then becomes constant.
View Assignment code
class String
def lastname
"bobert " + self
end
end
puts "Data".lastname
Classes are a bit wacky but are fun in crystal. Crystal has an amazing way to define classes and modules. The example below is off a simple module with a class which will output the data parsed inside of the class. Note that every module with a class must have an initializer if you want to run special rendering functions ( we will get more into that in the ECR section )
module Base
class Initializer
end
end
This is a simple module with a nested class named Initializer. So if you wanted to call the module and class you would use
a = Base::Initializer.new
despite there being no new or init function every module with an inline class must end with .new
anytime you want to use a variable that the class will accept as an argument you have to define an initialize
definition, this is standard. Below is an example of a Module with a nested class that uses the initialize method to make an argument public through the entire class.
module Base
class Initializer
def initialize(@data : String | Nil)
end
def caller
puts "#{@data}"
end
end
end
a = Base::Initializer.new("value")
a.caller()
we first define a module named base with a class named Initializer. Under the class is the used initialize function with one argument that is named @data of data type String or Nil this means that the value can be empty without raising any errors. After the initialize function was created the caller method prints out the value of @data. Now that we have the class setup we can call the .new method for the module->class with the argument for the initialize function. Once done we can use the variable a to call the caller function under the Initializer class. a.caller()
will output value