This page is not available in every language. Missing translations are disabled in the language selector.

Quick reference

Goby Programming Language Cheatsheet

A comprehensive reference guide for the Goby programming language: syntax, classes, modules, concurrency with channels, standard library, and Go interop.

· Published

Goby is an object-oriented language written in Go, inspired by Ruby and designed for cloud microservices.

Getting Started

Hello world

hello.gb

class Greet
  attr_accessor :audience, :head, :tail

  def initialize
    @head = "Hello, "
    @tail = "!"
  end

  def name
    audience.name
  end

  def say
    puts head + name + tail
  end
end

module MyName
  attr_reader :name

  def initialize
    @name = self.class.to_s
  end
end

class World
  include MyName
end

greet = Greet.new
greet.audience = World.new
greet.say

Then run with Goby:

$ goby hello.gb
#=> Hello, World!

REPL (igb)

$ goby -i
  • reset: Reset the VM state
  • exit: Exit the interactive REPL
  • help: Show interactive help
  • Ctrl + C: Cancel the current input buffer or exit if at top level

See the igb manual and test script for readline navigation and history.

Variables

Local Variables

zip101 = "233-7383"
magic_number = 42

Variable names follow [a-z][a-z0-9_]+ (snake_case).

Instance Variables

module State
  def initialize(state)
    @state = state      # declaring an instance variable by assignment
  end
  def show
    @state              # accessible from other instance methods
  end
end

state = State.new "success"
state.show
#=> success

Instance variables follow @[a-z][a-z0-9_]+ (prefixed with @).

Multiple Assignment

# array literal
a, b, c = [1, 2, 3]

# array with '*'
a = [1, 2, 3]
x, y, z = *a

# array literal with '*'
a, b, c = *[1, 2, 3]

# bare assignment: unsupported
a, b, c = 1, 2, 3  #=> unexpected 3 Line: 0

Discard Variable (_)

# '_' is write-only
a, _ = [1, 2]

Class Variables

Not supported.

Global Variables

Not supported.

Methods

Defining and Calling Methods

def foo_bar?(baz)
  if baz == "Hi, Goby!"
    true
  else
    false
  end
end

foo_bar? "Hi, Goby!" #=> true

Method names follow [a-z][a-z0-9_]+ (snake_case). Parentheses () can be omitted when calling methods without arguments. Methods ending in ! are not supported.

Method Parameter Ordering

def foo(normal, default="value", hash={}, ary=[], keyword:, keyword_default:"key", *sprat)
end

If a parameter has a default value, it can be omitted upon invocation. Parameter order in method definitions follows these rules:

  1. Required positional parameters (e.g. a)
  2. Positional parameters with defaults (e.g. a=1)
  3. Optional collection parameters (e.g. ary=[] or hs={})
  4. Required keyword arguments (e.g. kwd:)
  5. Keyword arguments with defaults (e.g. kwd: 1 or hsh: {key: "value"})
  6. Splat parameter (e.g. *sp)

Violating this sequence causes a compile-time syntax error.

Keyword Arguments

def foo(process:, verb: :GET, opt:{ csp: :enabled }, ary: [1, 2, 3])
end

Return Values

PI = 3.14
def area(radius)
  radius * PI      # returns the result of evaluation
end

area 6             #=> 18.84

Returning Multiple Values

def my_array
  [1, 2, 3]
end

my_array   #=> [1, 2, 3]

Instance Methods

module Foo
  def bar       # defining instance method
    puts "bar"
  end

  def baz(count, email: "[email protected]")
    count.times do
      puts email
    end
  end
end

foo = Foo.new
foo.bar     #=> bar
foo.baz(3)  #↓
[email protected]
[email protected]
[email protected]

Singleton Methods (Class Methods) #1

str = "Goby"
def str.foo     #1 singleton method on the object
  self * 2
end

str.foo
#=> GobyGoby

Singleton Methods #2

module Foo
  def self.bar  #2 singleton method with `self.`
    92
  end
end

Singleton Methods #3

module Foo
  def Foo.bar   #3 singleton method with a class name (unrecommended)
    88
  end
end

Singleton Methods #4

module Foo end

def Foo.bar     #4 singleton methods outside the Foo
  9999
end

Foo.bar #=> 9999

Attribute Accessors

class Foo
  attr_accessor :bar, :baz

  def initialize
    @bar = 42
    @baz = 99
  end
end

foo = Foo.new

foo.bar = 77
foo.baz = 88

Declare attribute reader, writer, or accessor macros inside classes or modules:

  • attr_accessor
  • attr_reader
  • attr_writer

Private Methods (WIP)

class Foo
  def bar
    42
  end

  def _baz  # leading '_' means private method
    99
  end
end

Modules and Classes

Module Definition and include

module Foo
  def foo
    "Foo's instance method"
  end
end

class Bar
  include Foo   # to include Foo
end

Bar.new.foo     #=> Foo's instance method

Module names follow [A-Z][A-Za-z0-9_]+ (UpperCamelCase). Modules cannot be inherited directly.

Module Definition and extend

module Foo
  def foo
    "Foo's instance method will be a singleton method"
  end
end

class Bar
  extend Foo   # to extend Foo
end

Bar.foo        #=> Foo's instance method will be a singleton method

extend imports instance methods of a module as singleton (class) methods on the target class/module.

Module Instantiation

module Foo   #module definition
  def foo
    99
  end
end

Foo.new.foo  #=> 99

In Goby, modules can also be instantiated directly via .new (e.g., Foo.new).

Class Definition and Inheritance

class Foo       # class definition
  def bar
    99
  end
end

class Baz < Foo # inheritance
end

Baz.new.bar  #=> 99

Class names follow UpperCamelCase. Single inheritance is supported using <.

Constants

HTTP_ERROR_404 = 404
HTTP_ERROR_404 = 500    # error

Constants follow UPPER_SNAKE_CASE. Constants are globally scoped.

Reopening Classes & Modules

class Foo
  def bar
    99
  end
end

class Foo
  def bar  # redefining is possible
    77
  end
end

Namespaces

class Foo
  module Bar
    MAGIC = 99
    def baz
      99
    end
  end
end

Foo::Bar.new.baz     # Use '::' for namespacing
Foo::Bar::MAGIC      # Use '::' for namespacing

Loading Libraries

require

require("uri")   # to activate URL class

u = URI.parse("http://example.com")
u.scheme   #=> "http"

require_relative

require_relative("bar")  # loading the local bar.gb

class Foo
  def self.bar(x)
    Bar.foo do |ten|
      x * ten
    end
  end

  def self.baz
    yield(100)
  end
end

Literals

Keywords

def, true, false, nil, if, elsif, else, case, when, return, self, end, while, do, yield, get_block, next, class, module, break

String Literals

"double quote"
'single quote'

Both single and double quotes are supported.

Symbol Literals

:symbol           # equivalent to "symbol"
{ symbol: "value" }

Goby symbols (:name) belong to the String class.

Numeric Literals

year   =  2018   # Integer
offset = -42     # Integer
PI     = 3.14    # Float
G      = -9.8    # Float

Array Literals

[1, 2, 3, "hello", :goby, { key: "value"}]
[1, 2, [3, 4], 5, 6]

Hash Literals

h = { key: "value", key2: "value2" }
h[:key2]   #=> value2

Hash literal keys are typically symbols or strings.

Range Literals

(1..10).each do |x|    # '..' represents a range
  puts x*x
end

Booleans and nil

true       # Boolean class
false      # Boolean class
nil        # Null class

!nil  #=> true

In boolean contexts, only nil and false are falsy; all other values are truthy.

Operators

Arithmetic, Logical & Assignment Operators

+           # unary
**          # power
-           # unary
* / %       # multiplication, division, modulus
+ -         # addition, subtraction
!           # logical inversion
> >= < <=   # inequality comparison
== !=       # equality comparison, negative comparison
&&          # logical AND
||          # logical OR
+= -=       # shorthand of addition/subtraction
=           # assignment

*Operator precedence is TBD

Other Operators

()          # changing priority of interpretation
[]          # array literal
*           # multiple assignment
..          # range

*Operator precedence is TBD

Delimiters

class Foo; end   # ';' to delimit

class Bar end    # recommended

String Interpolation (WIP)

puts "Error: #{error_message}"  # double quotation is required

Comments

puts "Goby"    # comments

Use single-line # for comments and annotations.

  • TODO
  • FIXME
  • OPTIMIZE
  • HACK
  • REVIEW

I/O

  • #puts

  • Built-in special constants: ARGV, STDIN, STDOUT, STDERR, ENV

Control Flow

if, else, elsif

def foo(str)
  if str.size > 10
    puts "too big!"
  elsif str.size < 3
    puts "too short!"
  else
    puts "moderate"
  end
end

then keyword is not supported in if statements.

Break

def foo(tail)
  (5..tail).each do |t|
    if t % 2 == 0 && t % 5 == 0
      puts "ouch!"
      break       # finish the block
    else
      puts t
    end
  end
  puts "out of the block"
end

foo 20
#=> 5 6 7 8 9
#=> ouch!
#=> out of the block

Case / When

def foo(str)
  case str
  when "Elf"
    puts "You might be Aragorn II!"
  when "Aragorn"
    puts "Long time no see, Aragorn!"
  when "Frodo", "Sam", "Gandalf"
    puts "One of us!"
  else
    puts "You're not yourself"
  end
end

While

decr = 10
while decr do
  if decr < 1
    break
  end
  puts decr
  decr -= 1
end

while loops and iteration blocks (do/end) handle repetitive execution.

Rescue

Under development. See #605.

Blocks and Procs

Blocks and Procs

def foo(ary: [1, 2, 3])
  ary.each do |s|      # start of the block with |block variable|
    puts s
  end                  # end of the block
end

Curly braces { } cannot be used to define code blocks; use do ... end.

yield

def foo
  yield(10)  # executes the block given
end

foo do |ten|
  ten + 20
end

Block Objects and call

b = Block.new do
  100
end

b.call  #=> 100

Block.new accepts a block that can later be invoked with .call.

Passing Blocks

def baz
  1000
end

class Foo
  def exec_block(block)
	block.call
  end

  def baz
    100
  end
end

b = Block.new do
  baz
end

f = Foo.new
f.exec_block(b)

Passing Blocks with Arguments

b = Block.new do |arg, offset|
  arg + 1000 - offset
end

b.call(49, 500) #=> 549

The get_block Keyword

def bar(block)
  # runs the block object and the block arg simultaneously
  block.call + get_block.call
end

def foo
  bar(get_block) do # passes two blocks to `bar`
    20
  end
end

foo do
  10
end

get_block is a built-in keyword that captures the passed block as a first-class Block object.

Closures

count = 0          # the declaration is used
b = Block.new do
  count += 1       # the block looks preserving the `count`
end

class Foo
  def bar(blk)
    count = 9      # (does not affect)
    puts blk.call  # local variable is resolved to the one above
  end
end

Foo.new.bar b  #=> 1
Foo.new.bar b  #=> 2
Foo.new.bar b  #=> 3

Built-in Classes (Core)

Core classes that form the root object model.

Object

Bar.ancestors
#» [Bar, Foo, Object]
Bar.singleton_class.ancestors
#» [#<Class:Bar>, #<Class:Object>, Class, Object]

Object serves as the base class for objects and singletons.

  • Object.methods: !, !=, ==, block_given?, class, exit, instance_eval, instance_variable_get, instance_variable_set, is_a?, methods, nil?, object_id, puts, raise, require, require_relative, send, singleton_class, sleep, thread, to_s, <, <=, >, >=, ancestors, attr_accessor, attr_reader, attr_writer, extend, include, name, new, superclass

  • Object.new.methods: !, !=, ==, block_given?, class, exit, instance_eval, instance_variable_get, instance_variable_set, is_a?, methods, nil?, object_id, puts, raise, require, require_relative, send, singleton_class, sleep, thread, to_s

Class

String.ancestors      #=> [String, Object]

Class and Object share core dispatch mechanisms.

  • Class.methods: <, <=, >, >=, ancestors, attr_accessor, attr_reader, attr_writer, extend, include, name, new, superclass, !, !=, ==, block_given?, class, exit, instance_eval, instance_variable_get, instance_variable_set, is_a?, methods, nil?, object_id, puts, raise, require, require_relative, send, singleton_class, sleep, thread, to_s

String

puts "Hello" + ' ' + 'world'  #=> Hello world

Strings are fixed UTF-8 with full 4-byte multibyte support.

  • String.methods: fmt,
    • Remainder: Class.methods
  • "a".methods: !=, *, +, <, <=>, ==, =~, >, [], []=, capitalize, chop, concat, count, delete, downcase, each_byte, each_char, each_line, empty?, end_with?, eql?, fmt, include?, insert, length, ljust, match, new, replace, replace_once, reverse, rjust, size, slice, split, start_with, strip, to_a, to_bytes, to_d, to_f, to_i, to_s, upcase,
    • Remainder: Object.new.methods

Integer

37037 * 27      #=> 999999
  • Integer.methods:same as Class.methods
  • 1.methods: !=, %, *, **, +, -, /, <, <=, <=>, ==, >, >=, even?, new, next, odd?, pred, ptr, times, to_f, to_float32, to_float64, to_i, to_int, to_int16, to_int32, to_int64, to_int8, to_s, to_uint, to_uint16, to_uint32, to_uint64, to_uint8
    • Remainder: Object.new.methods

Array

[1, "2", :card, [4, 5], { john: "doe" }]
  • Array.methods:same as Class.methods
  • [1].methods: *, +, [], []=, any?, at, clear, concat, count, delete_at, dig, each, each_index, empty?, first, flatten, include?, join, last, lazy, length, map, new, pop, push, reduce, reverse, reverse_each, rotate, select, shift, to_enum, unshift, values_at
    • Remainder: Object.new.methods

Hash

h = { key: "value" }
h = { "key": "value" }  #=> error

h["key"]  #=> value
h[:key]   #=> value

Hash keys can be indexed with string or symbol identifiers.

  • Hash.methods:same as Class.methods
  • { key: "value" }.methods: [], []=, any?, clear, default, default=, delete, delete_if, dig, each, each_key, each_value, empty?, eql?, fetch, fetch_values, has_key?, has_value?, keys, length, map_values, merge, new, select, sorted_keys, to_a, to_json, to_s, transform_values, values, values_at
    • Remainder: Object.new.methods

Range

(1..10).each do |i|
  puts i ** 2
end
  • Range.methods:same as Class.methods
  • (1..10).methods: !=, ==, bsearch, each, first, include?, last, lazy, map, new, size, step, to_a, to_enum
    • Remainder: Object.new.methods

Block

b = Block.new do
  100
end

b.call  #=> 100
  • Block.methods:same as Class.methods
  • (Block.new do end).methods: call
    • Remainder: Object.new.methods

Additional Built-in Classes

Float

1.1 + 1.1   # => -2.2
2.1 * -2.1  # => -4.41

Floating point literals like 3.14 or -273.15. Float is backed by Go float64.

  • Float.methods:same as Class.methods
  • 3.14.methods: !=, %, *, **, +, -, /, <, <=, <=>, ==, >, >=, new, ptr, to_d, to_i
    • Remainder: Object.new.methods

Decimal

"3.14".to_d            # => 3.14
"-0.7238943".to_d      # => -0.7238943
"355/113".to_d
# => 3.1415929203539823008849557522123893805309734513274336283185840

a = "16.1".to_d
b = "1.1".to_d
e = "17.2".to_d
a + b # => 0.1
a + b == e # => true

('16.1'.to_d  + "1.1".to_d).to_s #=> 17.2
('16.1'.to_f  + "1.1".to_f).to_s #=> 17.200000000000003

Arbitrary-precision decimal backed by Go big.Rat. Instantiated via .to_d.

  • Decimal.methods:same as Class.methods
  • (1.1).to_d.methods: !=, *, **, +, -, /, <, <=, <=>, ==, >, >=, denominator, fraction, inverse
    • Remainder: Object.new.methods

Regexp

a = Regexp.new("orl")
a.match?("Hello World")   #=> true
a.match?("Hello Regexp")  #=> false

b = Regexp.new("😏")
b.match?("🤡 😏 😐")    #=> true
b.match?("😝 😍 😊")    #=> false

c = Regexp.new("居(ら(?=れ)|さ(?=せ)|る|ろ|れ(?=[ばる])|よ|(?=な[いかくけそ]|ま[しすせ]|そう|た|て))")
c.match?("居られればいいのに")  #=> true
c.match?("居ずまいを正す")      #=> false

Regex literals /pattern/ (WIP).

  • Regexp.methods:same as Class.methods
  • Regexp.new("^aa$").methods: ==, match?
    • Remainder: Object.new.methods

MatchData

# numbered capture
'abcd'.match(Regexp.new('(b.)'))
#=> #<MatchData 0:"bc" 1:"bc">

# named capture
'abcd'.match(Regexp.new('a(?<first>b)(?<second>c)'))
#=> #<MatchData 0:"abc" first:"b" second:"c">

# converting to hash
» 'abcd'.match(Regexp.new('a(?<first>b)(?<second>c)')).to_h
#» { 0: "abc", first: "b", second: "c" }

Match captures use string keys. Key 0 holds the full matched string.

  • MatchData.methods:same as Class.methods
  • 'abcd'.match(Regexp.new('(b.)')).methods: captures, length, new, to_a, to_h
    • Remainder: Object.new.methods

File

f = File.new("../test_fixtures/file_test/size.gb")
f.name  #=> "../test_fixtures/file_test/size.gb"
  • File.methods: basename, chmod, delete, exist?, extname, join
    • Remainder: Class.methods
  • File.new.methods: basename, chmod, close, delete, exist?, extname, join, name
    • Remainder: Object.new.methods

Go Interoperability Classes

GoMap

h = { foo: "bar" }
m = GoMap.new(h)    # to pass values to Golang's code
h2 = m.to_hash
h2[:foo]   #=> "bar"
  • GoMap.methods:same as Class.methods
  • GoMap.new.methods: get, set, to_hash
    • Remainder: Object.new.methods

Channel

c = Channel.new

1001.times do |i| # i start from 0 to 1000
  thread do
  	c.deliver(i)
  end
end

r = 0
1001.times do
  r = r + c.receive
end

r #=> 500500

Channel wraps Go channels for concurrent message passing across threads.

  • Channel.methods:same as Class.methods
  • Channel.new.methods: close, deliver, new, receive
    • Remainder: Object.new.methods

Enumerators and Lazy Evaluation

Experimental library for lazy collection processing.

LazyEnumerator

# creating a lazy enumerator
enumerator = LazyEnumerator.new(ArrayEnumerator.new([1, 2, 3])) do |value|
	2 * value
end
result = []

enumerator.each do |value|
	result.push(value)
end

result   #=> [2, 4, 6]

The .lazy method is available on Array and Range.

  • LazyEnumerator.methods:same as Class.methods
  • [1, 2].lazy: each, first, has_next?, initialize, map, next
    • Remainder: Object.new.methods

ArrayEnumerator

iterated_values = []

enumerator = ArrayEnumerator.new([1, 2, 4])

while enumerator.has_next? do
	iterated_values.push(enumerator.next)
end

iterated_values   #=> [1, 2, 4]
  • ArrayEnumerator.methods:same as Class.methods
  • ArrayEnumerator.new([1, 2, 3]).methods: has_next?, initialize, next
    • Remainder: Object.new.methods

RangeEnumerator

iterated_values = []

enumerator = RangeEnumerator.new((1..4))

while enumerator.has_next? do
	iterated_values.push(enumerator.next)
end

iterated_values   #=> [1, 2, 3, 4]
  • RangeEnumerator.methods:same as Class.methods
  • RangeEnumerator.new(1..2).methods: has_next?, initialize, next
    • Remainder: Object.new.methods

Special Classes

Boolean

true.class  #=> Boolean
false.class #=> Boolean

Boolean carrier class for true and false. Cannot be instantiated directly.

Null

nil.class   #=> Null

Class for nil. Cannot be instantiated directly.

Method

Special container class for user-defined Goby methods.

Diggable

Provides #dig traversal. Arrays and Hashes include Diggable.

[1, 2].dig(0, 1)  #=> TypeError: Expect target to be Diggable, got Integer

Testing Framework

Spec

require "spec"

Spec.describe Spec do
  it "fails and exit with code 1" do
	expect(1).to eq(2)
  end
end

Spec.run
  • Spec.methods: describe, describes, instance, run
    • Remainder: Object.methods
  • Spec.new.methods: describes, initialize, run, session_successful, session_successful=
    • Remainder: Hash.new.methods

Tips and Tricks

Inspecting Methods

» "string".methods
#» ["!=", "*", "+", "<", "<=>", "==", "=~", ">", "[]", "[]=", "capitalize", "chop", "concat", "count", "delete", "downcase", "each_byte", "each_char", "each_line", "empty?", "end_with?", "eql?", "fmt", "include?", "insert", "length", "ljust", "match", "new", "replace", "replace_once", "reverse", "rjust", "size", "slice", "split", "start_with", "strip", "to_a", "to_bytes", "to_d", "to_f", "to_i", "to_s", "upcase", "!", "block_given?", "class", "exit", "instance_eval", "instance_variable_get", "instance_variable_set", "is_a?", "methods", "nil?", "object_id", "puts", "raise", "require", "require_relative", "send", "singleton_class", "sleep", "thread"]

Inspecting Classes

» "string".class
#» String

Inspecting Singleton Classes

» "moji".singleton_class
#» #<Class:#<String:842352325152>>

» "moji".class.singleton_class
#» #<Class:String>

Inspecting Ancestor Classes

» Integer.ancestors
#» [Integer, Object]

» "moji".class.ancestors
#» [String, Object]

Inspecting Singleton Class Ancestors

» "moji".class.singleton_class.ancestors
#» [#<Class:String>, #<Class:Object>, Class, Object]

Inspecting Object IDs

» "moji".object_id
#» 842352977920

#to_json

h = { a: 1, b: [1, "2", [4, 5, nil]]}
h.to_json         # converts hash to JSON
#=> {"a":1, "b":[1, "2", [4, 5, null]]}

Customizing #to_json

Override #to_json in your classes:

class JobTitle
  def initialize(name)
    @name = name
  end

  def to_json
    { title: @name }.to_json
  end
end

class Person
  def initialize(name, age)
    @name = name
    @age = age
    @job = JobTitle.new("software engineer")
  end

  def to_json
    { name: @name, age: @age, job: @job }.to_json
  end
end

stan = Person.new("Stan", 23)
h = { person: stan }
h.to_json #=> {"person":{"name":"Stan","job":{"title":"software engineer"},"age":23}}

Lazy Enumeration

Avoid eager evaluation overhead:

enumerator = [1, 2, 3].lazy.map do |value|
	2 * value
end
result = []

enumerator.each do |value|
	result.push(value)
end

result  #=> [2, 4, 6]

Call .lazy.map on Arrays, Ranges, or JSON collections.

Code Style Guide

Style Guidelines

  • Files must use UTF-8 encoding.
  • Use 2 spaces per indentation level.
    • Never use tabs for indentation.
  • Follow the standard Ruby Style Guide where applicable.

Documentation Conventions

  • Class#instance_method designates an instance method in documentation
  • Class.class_method
  • Module.module_method

Syntax Highlighting

Supported in Vim and Sublime Text, or by selecting standard Ruby syntax highlighting.

References

Japanese Articles

Related topics