From db405af2532d830e4df00646ec8c0566c03713ec Mon Sep 17 00:00:00 2001 From: Shozo Hatta Date: Fri, 7 Dec 2018 07:23:31 +0900 Subject: [PATCH] Apply feedback (#487) --- goby.md | 1280 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1280 insertions(+) create mode 100644 goby.md diff --git a/goby.md b/goby.md new file mode 100644 index 000000000..f3f02c1de --- /dev/null +++ b/goby.md @@ -0,0 +1,1280 @@ +--- +title: Goby +layout: 2017/sheet +prism_languages: [ruby] +weight: -3 +tags: [Featured] +updated: 2018-04-07 +--- + +## Getting started + +## Introductoon + +Goby's language design is based on Ruby language's, slim and shaped up. Differences in syntax between them is very small. + +## Getting started + +### Hello world +{: .-prime} + +#### hello.gb +{: .-file} + +```ruby +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: + +```bash +$ goby hello.gb +#=> Hello, World! +``` + +### REPL (igb) + +```bash +$ goby -i +``` + +* `reset`: reset the VM +* `exit`: exit REPL +* `help`: show help +* ctrl-c: cancel the block entered, or exit (on top level) + +See [igb manual & test script](https://github.com/goby-lang/goby/blob/master/igb/manual_test.md). You can use `readline` features such as command history by arrow keys. + +## Variables +{: .-three-column} + +### Local variable + +```ruby +zip101 = "233-7383" +magic_number = 42 +``` + +Should be "`[a-z][a-z0-9_]+`"(snake_case). + +### Instance variable + +```ruby +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 +``` + +Should be "`@[a-z][a-z0-9_]+`"(snake_case). + +### Multiple assignment + +```ruby +# 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 +``` + +### Black hole variable + +```ruby +# '_' is write-only +a, _ = [1, 2] +``` + +### Class variable + +Unsupported. + +### Global variable + +Unsupported. + +## Method definition + +### Method definition and calling + +```ruby +def foo_bar?(baz) + if baz == "Hi, Goby!" + true + else + false + end +end + +foo_bar? "Hi, Goby!" #=> true +``` + +Method name should be "`[a-z][a-z0-9_]+\??`" (snake_case). You can omit the trailing "`()`" only if no parameters are taken. Trailing using "`!`" is **unsupported**. + +### Order of method parameter + +```ruby +def foo(normal, default="value", hash={}, ary=[], keyword:, keyword_default:"key", *sprat) +end +``` + +If a default value is provided to a parameter, the parameter can be omitted when calling. `()` can be omitted. The order of parameters in method definition is restricted as follows: + +1. **normal parameters** (like `a`) +2. **normal parameters with default value** (like `a=1`) +3. **optional parameters** (array or hash, like `ary=[]` or `hs={}`) +4. **keyword parameters** (like `kwd:`) +5. **keyword parameters with default value** (like `kwd: 1` or `ary: [1,2,3]` or `hsh: {key: "value"}`) +6. **splat parameters** (like `*sp`) + +Or you will receive an error. + +### Keyword parameter (WIP) + +```ruby +def foo(process:, verb: :GET, opt:{ csp: :enabled }, ary: [1, 2, 3]) +end +``` + +### Returning value + +```ruby +PI = 3.14 +def area(radius) + radius * PI # returns the result of evaluation +end + +area 6 #=> 18.84 +``` + +### Returning multiple value + +```ruby +def my_array + [1, 2, 3] +end + +my_array #=> [1, 2, 3] +``` + +### Instance method + +```ruby +module Foo + def bar # defining instance method + puts "bar" + end + + def baz(count, email: "goby@example.com") + count.times do + puts email + end + end +end + +foo = Foo.new +foo.bar #=> bar +foo.baz(3) #↓ +goby@example.com +goby@example.com +goby@example.com +``` + +### Singleton method #1 + +```ruby +str = "Goby" +def str.foo #1 singleton method on the object + self * 2 +end + +str.foo +#=> GobyGoby +``` + +### Singleton method #2 + +```ruby +module Foo + def self.bar #2 singleton method with `self.` + 92 + end +end +``` + +### Singleton method #3 + +```ruby +module Foo + def Foo.bar #3 singleton method with a class name (unrecommended) + 88 + end +end +``` + +### Singleton method #4 + +```ruby +module Foo end + +def Foo.bar #4 singleton methods outside the Foo + 9999 +end + +Foo.bar #=> 9999 +``` + +### Attribute accessor method + +```ruby +class Foo + attr_accessor :bar, :baz + + def initialize + @bar = 42 + @baz = 99 + end +end + +foo = Foo.new + +foo.bar = 77 +foo.baz = 88 +``` + +You can use the following shorthands to declare attribute accessor methods in classes/modules: + +* `attr_accessor` +* `attr_reader` +* `attr_writer` + +### Private method (to be implemented) + +```ruby +class Foo + def bar + 42 + end + + def _baz # leading '_' means private method + 99 + end +end +``` + +## Module/Class definition +{: .-three-column} + +### Module definition and `include` + +```ruby +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 should be "`[A-Z][A-Za-z0-9_]+`" (UpperCamelCase). Modules cannot be inherited. + +### Module definition and `extend` + +```ruby +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` is to use the instance methods in the specified modules as **singleton methods** in your class or module. + +### Module instantiation + +```ruby +module Foo #module definition + def foo + 99 + end +end + +Foo.new.foo #=> 99 +``` + +Actually, Goby's module can be even **instantiated** via "`new`" like "`Foo.new`". + +### Class definition and inheritance + +```ruby +class Foo # class definition + def bar + 99 + end +end + +class Baz < Foo # inheritance +end + +Baz.new.bar #=> 99 +``` + +Class names should be "`[A-Z][A-Za-z0-9]+`" (UpperCamelCase). Inheritance with "`<`" is supported. + +### Constants + +```ruby +HTTP_ERROR_404 = 404 +HTTP_ERROR_404 = 500 # error +``` + +Constants should be "`[A-Z][A-Za-z0-9_]+`" (UPPER_SNAKECASE). Constants are **not reentrant** and the scope is **global**. + +### Redefining class/modules + +```ruby +class Foo + def bar + 99 + end +end + +class Foo + def bar # redefining is possible + 77 + end +end +``` + +### Namespaces + +```ruby +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 +``` + +## Load library + +### `require` + +```ruby +require("uri") # to activate URL class + +u = URI.parse("http://example.com") +u.scheme #=> "http" +``` + +### `require_relative` + +```ruby +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 +``` + +## Literal +{: .-three-column} + +### Keyword + +`def`, `true`, `false`, `nil`, `if`, `elsif`, `else`, `case`, `when`, `return`, `self`, `end`, `while`, `do`, `yield`, `get_block`, `next`, `class`, `module`, `break` + +### String literal + +```ruby +"double quote" +'single quote' +``` + +Double and single quotation can be used. + +### Symbol literal + +```ruby +:symbol # equivalent to "symbol" +{ symbol: "value" } +``` + +Goby's symbol (using `:`) is always `String` class. + +### Numeric literal + +```ruby +year = 2018 # Integer +offset = -42 # Integer +PI = 3.14 # Float +G = -9.8 # Float +``` + +### Array literal + +```ruby +[1, 2, 3, "hello", :goby, { key: "value"}] +[1, 2, [3, 4], 5, 6] +``` + +### Hash literal + +```ruby +h = { key: "value", key2: "value2" } +h[:key2] #=> value2 +``` + +Hash literal's keys should always be **symbol literals**. + +### Range literal + +```ruby +(1..10).each do |x| # '..' represents a range + puts x*x +end +``` + +### Boolean and `nil` + +```ruby +true # Boolean class +false # Boolean class +nil # Null class + +!nil #=> true +``` + +Any objects except `nil` and `false` will be treated as `true` on conditionals. + +## Operator + +### Arithmetic/logical/assignment operators + +```ruby ++ # 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 +``` + +*Priority of operators are TBD + +### Other operators + +```ruby +() # chaning priority of interpretation +[] # array literal +* # multiple assignment +.. # range +``` + +*Priority of operators are TBD + +### Delimiter + +```ruby +class Foo; end # ';' to delimit + +class Bar end # recommended +``` + +### String interpolation (to be implemented) + +```ruby +puts "Error: #{error_message}" # double quotation is required +``` + +### Comment + +```ruby +puts "Goby" # comments +``` + +Use the annotations to keep the comments concise. + +- `TODO` +- `FIXME` +- `OPTIMIZE` +- `HACK` +- `REVIEW` + +### I/O + +* `#puts` + +* special constants: `ARGV`, `STDIN`, `STDOUT`, `STDERR`, `ENV` + +## Flow control +{: .-three-column} + +### `if`, `else`, `elsif` + +```ruby +def foo(str) + if str.size > 10 + puts "too big!" + elsif str.size < 3 + puts "too short!" + else + puts "moderate" + end +end +``` + +`then` is **not** supported. + +### Break + +```ruby +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 + +```ruby +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 + +```ruby +decr = 10 +while decr do + if decr < 1 + break + end + puts decr + decr -= 1 +end +``` + +`while`, conditional and a `do`/`end` block can be used for a loop. + +### Rescue + +Under construction. Join [#605](https://github.com/goby-lang/goby/issues/605). + +## Block +{: .-three-column} + +### Block + +```ruby +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 +``` + +`{ }` cannot be used for forming a block. + +### `yield` + +```ruby +def foo + yield(10) # executes the block given +end + +foo do |ten| + ten + 20 +end +``` + +### Block object and `call` + +```ruby +b = Block.new do + 100 +end + +b.call #=> 100 +``` + +`Block.new` can take a block and then `call`. + +### Passing a block + +```ruby +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 a block with block arguments + +```ruby +b = Block.new do |arg, offset| + arg + 1000 - offset +end + +b.call(49, 500) #=> 549 +``` + +### Special `get_block` keyword + +```ruby +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 not a method but a **keyword** to retrive a given block argument as a block object. By this, you can pass around or `call` the given block arguments as block objects. + +### Closure + +```ruby +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 +``` + +## Native class (Primary) +{: .-three-column} + +Goby's most "native" classes cannot instantiate with `new` in principle. + +### `Object` + +```ruby +Bar.ancestors +#» [Bar, Foo, Object] +Bar.singleton_class.ancestors +#» [#, #, Class, Object] +``` + +`Object` is actually just for creating singleton classes. See `Class`. + +* **`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` + +```ruby +String.ancestors #=> [String, Object] +``` + +`Class` and `Object`can actually be regarded as the same and you don't need to distinguish them in almost all the cases. + +* **`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` + +```ruby +puts "Hello" + ' ' + 'world' #=> Hello world +``` + +Fixed to **UTF-8** with mb4 support. + +* **`String.methods`**: `fmt`, + * the rest: `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`, + * the rest: `Object.new.methods` + +### `Integer` + +```ruby +37037 * 27 #=> 999999 +``` + +* **`Integer.methods`**: the 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` + * the rest: `Object.new.methods` + +### `Array` + +```ruby +[1, "2", :card, [4, 5], { john: "doe" }] +``` + +* **`Array.methods`**: the 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` + * the rest: `Object.new.methods` + +### `Hash` + +```ruby +h = { key: "value" } +h = { "key": "value" } #=> error + +h["key"] #=> value +h[:key] #=> value +``` + +Keys in hash literals should be **symbol literals**, while Hash index can be either string or symbol literals. + +* **`Hash.methods`**: the 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` + * the rest: `Object.new.methods` + +### `Range` + +```ruby +(1..10).each do |i| + puts i ** 2 +end +``` + +* **`Range.methods`**: the same as `Class.methods` +* **`(1..10).methods`**: `!=`, `==`, `bsearch`, `each`, `first`, `include?`, `last`, `lazy`, `map`, `new`, `size`, `step`, `to_a`, `to_enum` + * the rest: `Object.new.methods` + +### `Block` + +```ruby +b = Block.new do + 100 +end + +b.call #=> 100 +``` + +* **`Block.methods`**: the same as `Class.methods` +* **`(Block.new do end).methods`**: `call` + * the rest: `Object.new.methods` + +## Native class (secondary) +{: .-three-column} + +### `Float` + +```ruby +1.1 + 1.1 # => -2.2 +2.1 * -2.1 # => -4.41 +``` + +Float literals like `3.14` or `-273.15`. `Float` class is based on Golang's `float64` type. + +* **`Float.methods`**: the same as `Class.methods` +* **`3.14.methods`**: `!=`, `%`, `*`, `**`, `+`, `-`, `/`, `<`, `<=`, `<=>`, `==`, `>`, `>=`, `new`, `ptr`, `to_d`, `to_i` + * the rest: `Object.new.methods` + +### `Decimal` + +```ruby +"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 +``` + +Experimental: the size is arbitrary and internally a fraction from Golang's `big.Rat` type. Decimal literal is TBD for now and you can get `Decimal` number via `to_d` method from `Integer`/`Float`/`String`. + +* **`Decimal.methods`**: the same as `Class.methods` +* **`(1.1).to_d.methods`**: `!=`, `*`, `**`, `+`, `-`, `/`, `<`, `<=`, `<=>`, `==`, `>`, `>=`, `denominator`, `fraction`, `inverse` + * the rest: `Object.new.methods` + +### `Regexp` + +```ruby +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 +``` + +Using `/ /` is to be implemented. + +* **`Regexp.methods`**: the same as `Class.methods` +* **`Regexp.new("^aa$").methods`**: `==`, `match?` + * the rest: `Object.new.methods` + +### `MatchData` + +```ruby +# numbered capture +'abcd'.match(Regexp.new('(b.)')) +#=> # + +# named capture +'abcd'.match(Regexp.new('a(?b)(?c)')) +#=> # + +# converting to hash +» 'abcd'.match(Regexp.new('a(?b)(?c)')).to_h +#» { 0: "abc", first: "b", second: "c" } +``` + +The number keys in the captures are actually `String` class.The key `0` is the mached string. + +* **`MatchData.methods`**: the same as `Class.methods` +* **`'abcd'.match(Regexp.new('(b.)')).methods`**: `captures`, `length`, `new`, `to_a`, `to_h` + * the rest: `Object.new.methods` + +### `File` + +```ruby +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` + * the rest: `Class.methods` +* **`File.new.methods`**: `basename`, `chmod`, `close`, `delete`, `exist?`, `extname`, `join`, `name` + * the rest: `Object.new.methods` + +## Native class (Golang-oriented) +{: .-three-column} + +### `GoMap` + +```ruby +h = { foo: "bar" } +m = GoMap.new(h) # to pass values to Golang's code +h2 = m.to_hash +h2[:foo] #=> "bar" +``` + +* **`GoMap.methods`**: the same as `Class.methods` +* **`GoMap.new.methods`**: `get`, `set`, `to_hash` + * the rest: `Object.new.methods` + +### `Channel` + +```ruby +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` class is to hold channels to work with `#thread`. See `thread`. + +* **`Channel.methods`**: the same as `Class.methods` +* **`Channel.new.methods`**: `close`, `deliver`, `new`, `receive` + * the rest: `Object.new.methods` + +## Enumerator & lazy + +Pretty new experimental library. + +### `LazyEnumerator` + +```ruby +# 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] +``` + +A shorthand `#lazy` method is also provided in `Array` and `Range` by now. See "Tips & tricks" below. + +* **`LazyEnumerator.methods`**: the same as `Class.methods` +* **`[1, 2].lazy`**: `each`, `first`, `has_next?`, `initialize`, `map`, `next` + * the rest: `Object.new.methods` + +### `ArrayEnumerator` + +```ruby +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`**: the same as `Class.methods` +* **`ArrayEnumerator.new([1, 2, 3]).methods`**: `has_next?`, `initialize`, `next` + * the rest: `Object.new.methods` + +### `RangeEnumerator` + +```ruby +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`**: the same as `Class.methods` +* **`RangeEnumerator.new(1..2).methods`**: `has_next?`, `initialize`, `next` + * the rest: `Object.new.methods` + +## Special class + +### `Boolean` + +```ruby +true.class #=> Boolean +false.class #=> Boolean +``` + +A special class that just to hold `true` and `false`. Cannot be instantiate. + +### `Null` + +```ruby +nil.class #=> Null +``` + +A special class that just to hold `nil`. Cannot be instantiate. + +### `Method` + +(A special dummy class that just holds methods defined by Goby code.) + +### `Diggable` + +Provides `#dig` method. Currently. `Array` and `Hash` classes' instance can be `Diggable`. + +```ruby +[1, 2].dig(0, 1) #=> TypeError: Expect target to be Diggable, got Integer +``` + +## Testing framework + +### `Spec` + +```ruby +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` + * the rest: `Object.methods` +* **`Spec.new.methods`**: `describes`, `initialize`, `run`, `session_successful`, `session_successful=` + * the rest: `Hash.new.methods` + +## Tips & tricks + +### Showing methods + +```ruby +» "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"] +``` + +### Showing class + +```ruby +» "string".class +#» String +``` + +### Showing singleton class + +```ruby +» "moji".singleton_class +#» #> + +» "moji".class.singleton_class +#» # +``` + +### Showing ancestors + +```ruby +» Integer.ancestors +#» [Integer, Object] + +» "moji".class.ancestors +#» [String, Object] +``` + +### Showing singleton classes' ancestors + +```ruby +» "moji".class.singleton_class.ancestors +#» [#, #, Class, Object] +``` + +### Showing object's id + +```ruby +» "moji".object_id +#» 842352977920 +``` + +### `#to_json` + +```ruby +h = { a: 1, b: [1, "2", [4, 5, nil]]} +h.to_json # converts hash to JSON +#=> {"a":1, "b":[1, "2", [4, 5, null]]} +``` + +### Customize `#to_json` + +Overwrite the `#to_json` in your class: + +```ruby +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 + +To avoid N + 1 query. + +```ruby +enumerator = [1, 2, 3].lazy.map do |value| + 2 * value +end +result = [] + +enumerator.each do |value| + result.push(value) +end + +result #=> [2, 4, 6] +``` + +You can call `#lazy.map` on `Array`, `Range`, or `JSON` objects. + +## Styling + +### Quick style guide + +* UTF-8 should be used. +* Only two spaces ` ` should be used for one indentation. + * Tab cannot be used for indentation. +* For more, follow [RuboCop's style guide](https://github.com/bbatsov/ruby-style-guide) in principle. + +### Document notation + +* `Class#instance_method` -- use `#` to represent instance methods in documents +* `Class.class_method` +* `Module.module_method` + +### Syntax highlighting + +Ready for Vim and Sublime text. You can also use Ruby's syntax highlighting so far. + +## References + +### Official + +* Official site: [https://goby-lang.org/](https://goby-lang.org/) +* Repository: [https://github.com/goby-lang/goby/](https://github.com/goby-lang/goby/) +* DevHints: [https://devhints.io/goby](https://devhints.io/goby) (this page) + +### Readings for Goby developers + +* [Write an Interpreter in Go](https://interpreterbook.com/) +* [Nand2Tetris II](https://www.coursera.org/learn/nand2tetris2/home/welcome) +* [Ruby under a microscope](http://patshaughnessy.net/ruby-under-a-microscope) +* [YARV's instruction table](http://www.atdot.net/yarv/insnstbl.html) + +### JP resource + +* [Goby: Rubyライクな言語(1)Gobyを動かしてみる](https://techracho.bpsinc.jp/hachi8833/2017_11_10/47787) +* [Gobyの組み込みクラスにメソッドを追加する方法](https://qiita.com/hanachin_/items/efc1c976a4f5749514ef)