Learn Crystal in One Video

Crystal TutorialCrystal is one of the most exciting new languages to come out in some time, so I decided to teach the core language in one video. Crystal is as easy to use as Ruby, but has the speed of C. In this one video I’ll cover Data Types, Strings, Arrays, Map, Looping, Conditionals, Math, Hashes, Tuples, Procs, Functions, Enums, Symbols, Classes, Inheritance, Modules, Polymorphism, Structs, Exceptions, File IO and More.

A cheat follows the video below to help you learn.

If you like videos like this, consider donating $1, or simply turn off Ad Blocking software. Either helps me to continue making free tutorials for the world.

Cheat Sheet

# Crystal utilizes code that is as easy
# to understand as Ruby, but has the speed
# of C

# Output to the console
puts "Hello World"

# ----- DATA TYPES -----
# Everything is an object

# Variables start with _ or a lowercase
# letter and may contain numbers

# This is a constant
PI = 3.14

# You can assign multiple values
a, b = 1, 2

# Bools are either true or false
canIVote = true

# sizeof returns the size in bytes
puts sizeof(Int8)
puts sizeof(Int16)
puts sizeof(Int32)
puts sizeof(Int64)

# There are also unsigned versions
# UInt8, UInt16, UInt32, UInt64

# There are 2 float types Float32 & Float64
# They are accurate up to around 13 digits
bigFloat = 1.12345678901234

# You can cast to a string using .to_s
puts (bigFloat + 0.00000000000005).to_s

# A Char is surrounded by single quotes
# and is either a single character or
# a special character like \n, \t, \', \"
myGrade = 'A'

# ----- STRINGS -----
# Strings are a series of characters
# surrounded by ""
myName = "Derek"
num1 = 1
num2 = 2

# String interpolation is available
puts "#{num1} + #{num2} = #{num1 + num2}"

# Multiline strings
multString = "I
use
many
lines"
puts multString

# Concatenate strings
puts myName + " Banas"

# String size
puts "Egg".size

# Duplicate string
puts "Money " * 3

# Compare equality
puts "Doug" == "Paul"

# Returns -1, 0, or 1
puts "Doug".compare("Paul")

# Get value at index
puts "Hello"[0]

# Index where value is
puts "Monkey".index("key")

# Does string contain a string
puts "Turtledove".includes?("dove")

# Get a range of values
puts "Pigman"[0..2]

# Return an array of Chars
puts "Sue".chars

# Return an array using a separator
puts "Happy Birthday".split(" ")

# Number of times a character is used
puts "Derek".count('e')

# Check if empty
puts "".empty?

# Replace substring with another
puts "Have a good day".gsub("good") {"ok"}

# Insert in a string
puts "Have a day".insert(6, " good")

# ----- ARRAYS -----
# Arrays are ordered lists with integer indexes

# Create array
array1 = [8,4,3,7,9]

# You can use multiple types
array2 = [5, "Word", 3.2]

# You can define accepted types
array3 = [1,2,3] of String | Int32

# Empty arrays should specify a type
array4 = Array(Int32).new

# Assign values to array using a separator
names = "Paul,Sue,Sam".split(",")

# Get size
puts array1.size

# Get index
puts array1[1]

# Get from index to other
puts array1[1, 4]

# Get last
puts array1[-1]

# Add value
array1 << 10

# Remove last value
puts array1.pop

# Remove 1st
puts array1.shift

# Check if value exists
puts array1.includes? 7

# Delete values
array1.delete(7)

# Delete from index to index
puts array1.delete_at(0,2)

# Insert a value at index and shift values
puts array1.insert(0, 3)

# Push on multiple values
puts array1.push(4,5,6,7)

# Sort values
puts array1.sort

# Return uniques
puts array1.uniq

# Perform operation on all values
puts array1.map! {|x| x * x}

# Return array of all permutations
puts array1.permutations

# Output array in loop
array1.each do |value|
  puts value
end

# ----- LOOPING -----
# Print evens from 1 to 10
x = 1
loop do
  x += 1

  # Skip rest of loop unless even
  next unless (x % 2) == 0
  puts x

  # Stop looping if x >= 10
  break if x >= 10
end

# While loops as long as a condition is true
y = 1

while y <= 10
  y += 1
  next unless (y % 2) == 0
  puts y
  break if y >= 10
end

# Loop until a condition is met
a = 1

until a >= 10
  a += 1
  next unless (a % 2) == 0
  puts a
end

# ----- CONDITIONALS -----
# Comparison Operators : == != < > <= >=
# Logical Operators : && || ! (and, or, not)
age = 12
if (age >= 5) && (age <= 6)
  puts "You're in Kindergarten"
elsif (age >= 7) && (age <= 13)
  puts "You're in Middle School"
  puts "Yeah"
elsif (age >= 14) && (age <= 18)
  puts "You're in High School"
else
  puts "Stay Home"
end

puts "true || false = " + (true || false).to_s
puts "!true = " + (!true).to_s

# Unless comparison
unless age > 4
  puts "No School"
else
  puts "Go to School"
end

# Case is used with limited options
print "Language : "
lang = gets

case lang
when "French", "french"
  puts "Bonjour"

  # without end it would check the next condition
  exit

when "Spanish", "spanish"
  puts "Hola"
  exit

# else is the default
else
  puts "Hello"
end

# Ternary operator
age = 55
puts (age >= 50) ? "Old" : "Young"

# ----- USER INTERACTION & MATH -----
print "Enter 1st value "

# Get input from the user
# not_nil! provides us with a way to handle
# nil inputs which I cover later with
# exception handling
n1 = gets.not_nil!
print "Enter 2nd value "
n2 = gets.not_nil!

# to_i converts a string into an int
# to_s, to_f, to_f64, to_i64, etc.
puts "#{n1} + #{n2} = #{n1.to_i + n2.to_i}"
puts "#{n1} - #{n2} = #{n1.to_i - n2.to_i}"
puts "#{n1} * #{n2} = #{n1.to_i * n2.to_i}"
puts "#{n1} / #{n2} = #{n1.to_i / n2.to_i}"
puts "#{n1} % #{n2} = #{n1.to_i % n2.to_i}"
puts "#{n1} ** #{n2} = #{n1.to_i ** n2.to_i}"

# Trig functions cos, sin, tan, acos, asin,
# atan, cosh, sinh, tanh, acosh, asinh, atanh
puts "sqrt(#{n1}) = #{Math.cbrt(n1.to_i)}"
puts "cbrt(#{n1}) = #{Math.cbrt(n1.to_i)}"
puts "hypot(2,2) = #{Math.hypot(2,2)}"
puts "log(100) = #{Math.log(100)}"
puts "log10(100) = #{Math.log10(100)}"
puts "log2(100) = #{Math.log2(100)}"
puts "max(100,101) = #{Math.max(100,101)}"
puts "min(100,101) = #{Math.min(100,101)}"

# ----- HASHES -----
# A hash is a collection of key object pairs

nHash = { "PI" => 3.14,
                "Golden" => 1.618,
                "e" => 2.718}

puts nHash["PI"]

# Print out each key and value
nHash.each do |key, value|
  puts key.to_s + " : " + value.to_s
end

puts "Size of Hash : " + nHash.size.to_s

# Check if empty
puts nHash.empty?

# Check for key
nHash.has_key?("PI")

# Check for value
nHash.has_value?(3.14)

# Merge hashes
nHash.merge({"Absolute0" => -459})

# Get hash without keys
puts nHash.reject("PI")

# Return based on value condition
puts nHash.select {|k, v| v > 2}

# Remove all but given keys
puts nHash.select!("PI")

# Delete using key
nHash.delete("PI")

# Delete if condition is true
nHash.delete_if {|key, value| key.starts_with?("Go")}

# Clear a hash
nHash.clear

# ----- TUPLES -----
# Tuples are fixed in size, may have different types

# Create a tuple
t1 = {"Derek", 43, 174.5}

# Value at index
puts t1[0]

# Loop through tuple
t1.each do |value|
  puts value
end

# Get first value or use last?
puts t1.first?

# Get size
puts t1.size

# ----- PROCS -----
# Procs represent a function pointer

proc = ->(x : Int32) {x * x}

puts proc.call 5

# ----- FUNCTIONS -----
# Functions start with def, name,
# parameters if any and maybe a return
# value
def addNums(n1, n2)
  return n1 + n2
end

puts addNums(5,4)

# Variables are pass by value
x = 1

def changeX(x)
  x = 5
end

puts x

# A function can receive a variable
# number of arguments using a splat
def getSum(*vals)
  sum = 0
  vals.each do |val|
    sum += val
  end
  vals
end

puts getSum 1,2,3,4

# ----- ENUMS -----
# Enums have a collection of names
# associated with an int starting with 0
# You can assign a datatype or use Int32
enum Color : UInt8
  Blue
  Red
  Green = 4 # You can change a value

  # You can have methods
  def green?
    # Check if enum is assigned to green
    self == Color::Green
  end

end

# Get the value
puts Color::Green.value

# Check if enum is green
puts Color::Green.green?

# ----- SYMBOLS -----
# Symbols are strings that can't change
# We'll cover them later with classes
:Derek

puts :Derek

# ----- CLASSES -----
# Classes are blueprints used to model
# real world objects
# Object attributes are called fields
# Object capabilities are called methods

class Animal

  # Called when an Animal object is created
  def initialize(name : String)
    puts "Animal created"
    @name = name
  end

  def setName(name)
    # Set value for the field
    @name = name
  end

  def getName
    # Return name value
    @name
  end

  # Another way to get the name value
  def name
    @name
  end

  # Protect our name data
  def name=(name)
    if name.is_a?(Number)
      puts "Name can't be a number"
    else
      @name = name
    end
  end

end

# Create an Animal
cat = Animal.new("Mittens")

# Change name
cat.name = 1
cat.name = "Sophie"

# Get name of cat
puts cat.getName

class Dog
  # Shortcut for creating getters and setters
  property name : String
  property height : Float32
  property weight : Float32

  def initialize
    @name = "No name"
    @height = 0
    @weight = 0
  end

  def bark
    return "Bark"
  end
end

rover = Dog.new
rover.name = "Rover"
puts "#{rover.name} says #{rover.bark}"

# Inherit all fields and methods of Dog
class GermanShephard < Dog
  def bark
    return "Loud Bark"
  end
end

max = GermanShephard.new
max.name = "Max"
puts "#{max.name} says #{max.bark}"

# ----- MODULES -----
# Modules add functionality to a class
# but they can't be instantiated
module Vehicle
  def makeSound
    puts "Rumm Rummmmm"
  end
end

class MacTruck
  include Vehicle
end

macTruck = MacTruck.new
macTruck.makeSound

# ----- POLYMORPHISM -----
# Crystal uses duck typing to achieve
# polymorphism
class Bird
  def tweet(bird_type)
    bird_type.tweet
  end
end

class Cardinal < Bird
  def tweet
    puts "Tweet tweet"
  end
end

class Parrot < Bird
  def tweet
    puts "Squawk"
  end
end

generic_bird = Bird.new

generic_bird.tweet(Cardinal.new)
generic_bird.tweet(Parrot.new)

# ----- STRUCTS -----
# Structs allow you to create light weight
# classes with some built in functionality
struct Location
  property lat : Float32
  property long : Float32
  def initialize(@lat : Float32,
    @long : Float32)
  end

  def position
    puts "Latitude : #{lat} Longitude : #{long}"
  end
end

where = Location.new(80.31, 72.62)
where.position

# ----- EXCEPTION HANDLING -----
# We catch exceptions with begin and rescue

print "Enter a number : "
fNum = gets.not_nil!
print "Enter Another : "
sNum = gets.not_nil!

begin
  answer = fNum.to_i / sNum.to_i

rescue # You could use rescue ZeroDivisionError
  puts "You can't divide by zero"
  exit
end

puts "#{fNum} / #{sNum} = #{answer}"

# You can throw your own exceptions with raise
# You catch them with rescue
age = -1
begin
  if(age < 0)
    raise "Enter Positive Number"
  end
rescue ex
  puts "That is an impossible age"
end

# ----- FILE IO -----
# Returns a File object for writing
write_handler = File.new("somefile.out", "w")
# Puts the text in the file
write_handler.puts("Random Text").to_s

# Closes the file
write_handler.close

# Read data from the defined file
data_from_file = File.read("somefile.out")

puts "Data From File : " + data_from_file

Leave a Reply

Your email address will not be published. Required fields are marked *