rbs 1.2.0 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +5 -1
  3. data/.gitignore +2 -0
  4. data/CHANGELOG.md +53 -0
  5. data/README.md +1 -1
  6. data/Rakefile +9 -0
  7. data/Steepfile +1 -0
  8. data/core/array.rbs +1 -1
  9. data/core/basic_object.rbs +1 -1
  10. data/core/io.rbs +1 -1
  11. data/core/kernel.rbs +2 -2
  12. data/core/marshal.rbs +4 -3
  13. data/docs/rbs_by_example.md +328 -0
  14. data/docs/sigs.md +3 -1
  15. data/docs/stdlib.md +1 -1
  16. data/docs/syntax.md +0 -3
  17. data/lib/rbs/definition_builder.rb +2 -18
  18. data/lib/rbs/definition_builder/ancestor_builder.rb +9 -2
  19. data/lib/rbs/errors.rb +36 -0
  20. data/lib/rbs/parser.rb +913 -892
  21. data/lib/rbs/parser.y +10 -6
  22. data/lib/rbs/prototype/rb.rb +7 -3
  23. data/lib/rbs/prototype/runtime.rb +118 -42
  24. data/lib/rbs/version.rb +1 -1
  25. data/rbs.gemspec +1 -1
  26. data/sig/ancestor_builder.rbs +2 -0
  27. data/sig/errors.rbs +15 -0
  28. data/sig/namespace.rbs +1 -1
  29. data/sig/polyfill.rbs +0 -18
  30. data/stdlib/dbm/0/dbm.rbs +43 -30
  31. data/stdlib/mutex_m/0/mutex_m.rbs +1 -1
  32. data/stdlib/net-http/0/net-http.rbs +1846 -0
  33. data/stdlib/optparse/0/optparse.rbs +1214 -0
  34. data/stdlib/resolv/0/resolv.rbs +1504 -0
  35. data/stdlib/socket/0/addrinfo.rbs +469 -0
  36. data/stdlib/socket/0/basic_socket.rbs +503 -0
  37. data/stdlib/socket/0/ip_socket.rbs +72 -0
  38. data/stdlib/socket/0/socket.rbs +2687 -0
  39. data/stdlib/socket/0/tcp_server.rbs +177 -0
  40. data/stdlib/socket/0/tcp_socket.rbs +35 -0
  41. data/stdlib/socket/0/udp_socket.rbs +111 -0
  42. data/stdlib/socket/0/unix_server.rbs +154 -0
  43. data/stdlib/socket/0/unix_socket.rbs +132 -0
  44. data/stdlib/timeout/0/timeout.rbs +5 -0
  45. data/steep/Gemfile.lock +12 -12
  46. metadata +16 -12
  47. data/bin/annotate-with-rdoc +0 -153
  48. data/bin/console +0 -14
  49. data/bin/query-rdoc +0 -103
  50. data/bin/rbs-prof +0 -9
  51. data/bin/run_in_md.rb +0 -49
  52. data/bin/setup +0 -8
  53. data/bin/sort +0 -89
  54. data/bin/steep +0 -4
  55. data/bin/test_runner.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3be8bb7e853829dddbff29b1a3337a33fd61e2860f6c83771dfdbe2ad097eb75
4
- data.tar.gz: e569b69fe73f92c80e621521be85e8292fdb43a14d4f1fdc8f7537cdd04efbb5
3
+ metadata.gz: c090bba432d9f46788964c4ae2e0def7a084141fb847d214b7153429ce2afe1d
4
+ data.tar.gz: 8441fe573994cbe36ba2e87706aca5e063136859b127ae6df9773e5593754f12
5
5
  SHA512:
6
- metadata.gz: 53895724feba94cfc7695ee7013f7cfa83618d566f4eec14fab0b5bed00074cda0394668a92d1f9eaa572bc3f0d8099a2f0bf201b05a444b3b2b8201c797ed48
7
- data.tar.gz: 924a8b04e9b88464b5443fc1b8269cd20cfbfb25e04cc93737d52bbe1b4a895ac389371e373129ac6238a1152369ce71c3cca9183608fc9cebca4f571effc018
6
+ metadata.gz: 13b2d6ff0fe6ddc6fc46c6499f88246b37e953e9784a543b3e17ef0457965dde9874fa61ae69de2fb22bb27bf0f155ffe62655cc8a4d95f45a3d80fa38599e61
7
+ data.tar.gz: 9c6d7ff8e47a97235d4aebcb6cec1728cae0a03d9e18285e7649c585672690a0bbd4ff6de048c8fe36fb85f34b294c27ed28f97478bec456fe1b5645bedbafc3
@@ -18,7 +18,11 @@ jobs:
18
18
  job:
19
19
  - test
20
20
  - stdlib_test
21
- - rubocop validate test_doc build test_generate_stdlib confirm_parser
21
+ - rubocop validate test_doc build test_generate_stdlib
22
+ - confirm_parser
23
+ exclude:
24
+ - container_tag: master-nightly-focal
25
+ job: confirm_parser
22
26
  container:
23
27
  image: rubylang/ruby:${{ matrix.container_tag }}
24
28
  steps:
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /.bundle/
2
+ /steep/.bundle/
2
3
  /.yardoc
3
4
  /_yardoc/
4
5
  /coverage/
@@ -9,3 +10,4 @@
9
10
  /lib/rbs/parser.output
10
11
  /vendor/sigs
11
12
  /Gemfile.lock
13
+
data/CHANGELOG.md CHANGED
@@ -2,6 +2,59 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.3.2 (2021-07-23)
6
+
7
+ This release is to fix a bug introduced by parser update in 1.3.0.
8
+
9
+ * Fix parser to accept alias names starting with `_` ([#723](https://github.com/ruby/rbs/pull/723))
10
+
11
+ ## 1.3.1 (2021-07-21)
12
+
13
+ This release is to fix a bug introduced by parser update in 1.3.0.
14
+
15
+ * Fix parser to accept param names starting with `_` ([#721](https://github.com/ruby/rbs/pull/721))
16
+
17
+ ## 1.3.0 (2021-07-20)
18
+
19
+ ### Summary
20
+
21
+ RBS 1.3.0 includes bug fixees of the parser and class/module definition validations.
22
+
23
+ ### Signature updates
24
+
25
+ * dbm ([#718](https://github.com/ruby/rbs/pull/718))
26
+ * net-http ([#686](https://github.com/ruby/rbs/pull/686))
27
+ * optparse ([#693](https://github.com/ruby/rbs/pull/693))
28
+ * resolv ([#697](https://github.com/ruby/rbs/pull/697))
29
+ * socket ([#699](https://github.com/ruby/rbs/pull/699))
30
+ * `IO` ([#698](https://github.com/ruby/rbs/pull/698))
31
+ * `Marshal` ([#684](https://github.com/ruby/rbs/pull/684))
32
+ * `Mutex` ([#683](https://github.com/ruby/rbs/pull/683))
33
+ * `Array#shift` ([#707](https://github.com/ruby/rbs/pull/707))
34
+ * `BasicObject#method_missing` ([#707](https://github.com/ruby/rbs/pull/706), [#710](https://github.com/ruby/rbs/pull/710))
35
+ * `Kernel#caller` ([#705](https://github.com/ruby/rbs/pull/705))
36
+
37
+ ### Library changes
38
+
39
+ * Interface names starting with lower case characters are now syntax error ([#678](https://github.com/ruby/rbs/pull/678), [#720](https://github.com/ruby/rbs/pull/720))
40
+ * Mixins of classes are rejected ([#681](https://github.com/ruby/rbs/pull/681))
41
+ * Generate prototype of `initialize` method with return type `void` ([#685](https://github.com/ruby/rbs/pull/685))
42
+ * Let `prototype runtime` generate class/module declarations in nested syntax ([#700](https://github.com/ruby/rbs/pull/700))
43
+ * Fix race condition for multi-thread support ([#702](https://github.com/ruby/rbs/pull/702))
44
+
45
+ ### Miscellaneous
46
+
47
+ * Add new doc `docs/rbs_by_example.md` ([#694](https://github.com/ruby/rbs/pull/694))
48
+
49
+ ## 1.2.1 (2021-05-27)
50
+
51
+ This release includes the following minor changes:
52
+
53
+ * Fix test to run the tests in ruby repository. ([#679](https://github.com/ruby/rbs/pull/679))
54
+ * Remove unnecessary files from the gem package. ([#675](https://github.com/ruby/rbs/pull/675))
55
+ * Suppress unused variable warning ([#674](https://github.com/ruby/rbs/pull/674))
56
+ * Update documents ([#672](https://github.com/ruby/rbs/pull/672))
57
+
5
58
  ## 1.2.0 (2021-04-21)
6
59
 
7
60
  ### Summary
data/README.md CHANGED
@@ -7,7 +7,7 @@ It also allows declaring constants and global variables.
7
7
  The following is a small example of RBS for a chat app.
8
8
 
9
9
  <!-- run-start:a.rbs:bundle exec rbs -I a.rbs validate -->
10
- ```rbs
10
+ ```rb
11
11
  module ChatApp
12
12
  VERSION: String
13
13
 
data/Rakefile CHANGED
@@ -54,6 +54,15 @@ task :validate => :parser do
54
54
  lib << "singleton"
55
55
  end
56
56
 
57
+ if lib == ["net-http"]
58
+ lib << "uri"
59
+ end
60
+
61
+ if lib == ["resolv"]
62
+ lib << "socket"
63
+ lib << "timeout"
64
+ end
65
+
57
66
  sh "#{ruby} #{rbs} #{lib.map {|l| "-r #{l}"}.join(" ")} validate --silent"
58
67
  end
59
68
  end
data/Steepfile CHANGED
@@ -7,6 +7,7 @@ target :lib do
7
7
  library "set", "pathname", "json", "logger", "monitor", "tsort"
8
8
  signature "stdlib/strscan/0/"
9
9
  signature "stdlib/rubygems/0/"
10
+ signature "stdlib/optparse/0/"
10
11
  end
11
12
 
12
13
  # target :lib do
data/core/array.rbs CHANGED
@@ -1637,7 +1637,7 @@ class Array[unchecked out Elem] < Object
1637
1637
  # args #=> ["filename"]
1638
1638
  #
1639
1639
  def shift: () -> Elem?
1640
- | (?int n) -> ::Array[Elem]
1640
+ | (int n) -> ::Array[Elem]
1641
1641
 
1642
1642
  # Returns a new array with elements of `self` shuffled.
1643
1643
  #
@@ -215,7 +215,7 @@ class BasicObject
215
215
  # r.xxiii #=> 23
216
216
  # r.mm #=> 2000
217
217
  #
218
- def method_missing: (Symbol, *untyped) -> untyped
218
+ def method_missing: (Symbol, *untyped, **untyped) ?{ (*untyped, **untyped) -> untyped } -> untyped
219
219
 
220
220
  # Invoked as a callback whenever a singleton method is added to the receiver.
221
221
  #
data/core/io.rbs CHANGED
@@ -746,7 +746,7 @@ class IO < Object
746
746
 
747
747
  def self.readlines: (String name, ?String sep, ?Integer limit, ?external_encoding: String external_encoding, ?internal_encoding: String internal_encoding, ?encoding: String encoding, ?textmode: untyped textmode, ?binmode: untyped binmode, ?autoclose: untyped autoclose, ?mode: String mode) -> ::Array[String]
748
748
 
749
- def self.select: (::Array[io]? read_array, ?::Array[io]? write_array, ?::Array[io]? error_array, ?Integer? timeout) -> ::Array[::Array[io]]?
749
+ def self.select: (::Array[io]? read_array, ?::Array[io]? write_array, ?::Array[io]? error_array, ?Numeric? timeout) -> ::Array[::Array[io]]?
750
750
 
751
751
  def self.sysopen: (String path, ?String mode, ?String perm) -> Integer
752
752
 
data/core/kernel.rbs CHANGED
@@ -13,8 +13,8 @@
13
13
  module Kernel : BasicObject
14
14
  private
15
15
 
16
- def self?.caller: (?Integer start_or_range, ?Integer length) -> ::Array[String]?
17
- | (?::Range[Integer] start_or_range) -> ::Array[String]?
16
+ def self?.caller: (Integer start_or_range, ?Integer length) -> ::Array[String]?
17
+ | (::Range[Integer] start_or_range) -> ::Array[String]?
18
18
  | () -> ::Array[String]
19
19
 
20
20
  def self?.caller_locations: (?Integer start_or_range, ?Integer length) -> ::Array[Thread::Backtrace::Location]?
data/core/marshal.rbs CHANGED
@@ -136,8 +136,8 @@ module Marshal
136
136
  # ThreadGroup, Continuation
137
137
  # * objects which define singleton methods
138
138
  #
139
- def self.dump: (Object arg0, ?IO arg1, ?Integer arg2) -> Object
140
- | (Object arg0, ?Integer arg1) -> Object
139
+ def self.dump: (untyped obj, untyped port, ?Integer limit) -> untyped
140
+ | (untyped obj, ?Integer limit) -> String
141
141
 
142
142
  # Returns the result of converting the serialized data in source into a Ruby
143
143
  # object (possibly with associated subordinate objects). source may be either an
@@ -147,7 +147,8 @@ module Marshal
147
147
  # Never pass untrusted data (including user supplied input) to this method.
148
148
  # Please see the overview for further details.
149
149
  #
150
- def self.load: (String arg0, ?Proc arg1) -> Object
150
+ def self.load: (String | untyped port) -> untyped
151
+ | [A] (String | untyped port, ^(untyped) -> A) -> A
151
152
 
152
153
  alias self.restore self.load
153
154
  end
@@ -0,0 +1,328 @@
1
+ # RBS By Example
2
+
3
+ ## Goal
4
+
5
+ The purpose of this doc is to teach you how to write RBS signatures by using the standard library's methods as a guide.
6
+
7
+ ## Examples
8
+
9
+ ### Zero argument methods
10
+
11
+ **Example:** `String#empty?`
12
+
13
+ ```ruby
14
+ # .rb
15
+ "".empty?
16
+ # => true
17
+ "hello".empty?
18
+ # => false
19
+ ```
20
+
21
+ ```ruby
22
+ # .rbs
23
+ class String
24
+ def empty?: () -> bool
25
+ end
26
+ ```
27
+
28
+ `String`'s `#empty` method takes no parameters, and returns a boolean value
29
+
30
+ ### Single argument methods
31
+
32
+ **Example:** `String#include?`
33
+
34
+ ```ruby
35
+ # .rb
36
+ "homeowner".include?("house")
37
+ # => false
38
+ "homeowner".include?("meow")
39
+ # => true
40
+ ```
41
+
42
+ ```ruby
43
+ class String
44
+ def include?: (String) -> bool
45
+ end
46
+ ```
47
+
48
+ `String`'s `include?` method takes one argument, a `String`, and returns a
49
+ boolean value
50
+
51
+ ### Variable argument methods
52
+
53
+ **Example:** `String#end_with?`
54
+
55
+ ```ruby
56
+ # .rb
57
+ "hello?".end_with?("!")
58
+ # => false
59
+ "hello?".end_with?("?")
60
+ # => true
61
+ "hello?".end_with?("?", "!")
62
+ # => true
63
+ "hello?".end_with?(".", "!")
64
+ # => false
65
+ ```
66
+
67
+ ```ruby
68
+ # .rbs
69
+ class String
70
+ def end_with?: (*String) -> bool
71
+ end
72
+ ```
73
+
74
+ `String`'s `#end_with?` method takes any number of `String` arguments, and
75
+ returns a boolean value.
76
+
77
+ ### Optional positional arguments
78
+
79
+ **Example:** `String#ljust`
80
+
81
+ ```ruby
82
+ # .rb
83
+ "hello".ljust(4)
84
+ #=> "hello"
85
+ "hello".ljust(20)
86
+ #=> "hello "
87
+ "hello".ljust(20, '1234')
88
+ #=> "hello123412341234123"
89
+ ```
90
+
91
+ ```ruby
92
+ # .rbs
93
+ class String
94
+ def ljust: (Integer, ?String) -> String
95
+ end
96
+ ```
97
+
98
+ `String`'s `ljust` takes one `Integer` argument, and an optional `String` argument, indicated by the the `?` prefix marker. It returns a `String`.
99
+
100
+ ### Multiple signatures for a single method
101
+
102
+ **Example:** `Array#*`
103
+
104
+ ```ruby
105
+ # .rb
106
+ [1, 2, 3] * ","
107
+ # => "1,2,3"
108
+ [1, 2, 3] * 2
109
+ # => [1, 2, 3, 1, 2, 3]
110
+ ```
111
+
112
+ *Note:* Some of the signatures after this point include type variables (e.g. `Elem`, `T`).
113
+ For now, it's safe to ignore them, but they're included for completeness.
114
+
115
+ ```ruby
116
+ # .rbs
117
+ class Array[Elem]
118
+ def *: (String) -> String
119
+ | (Integer) -> Array[Elem]
120
+ end
121
+ ```
122
+
123
+ `Array`'s `*` method, when given a `String` returns a `String`. When given an
124
+ `Integer`, it returns an `Array` of the same contained type `Elem` (in our example case, `Elem` corresponds to `Integer`).
125
+
126
+ ### Union types
127
+
128
+ **Example:** `String#<<`
129
+
130
+ ```ruby
131
+ # .rb
132
+ a = "hello "
133
+ a << "world"
134
+ #=> "hello world"
135
+ a << 33
136
+ #=> "hello world!"
137
+ ```
138
+
139
+ ```ruby
140
+ # .rbs
141
+ class String
142
+ def <<: (String | Integer) -> String
143
+ end
144
+ ```
145
+
146
+ `String`'s `<<` operator takes either a `String` or an `Integer`, and returns a `String`.
147
+
148
+ ### Nilable types
149
+
150
+ ```ruby
151
+ # .rb
152
+ [1, 2, 3].first
153
+ # => 1
154
+ [].first
155
+ # => nil
156
+ [1, 2, 3].first(2)
157
+ # => [1, 2]
158
+ [].first(2)
159
+ # => []
160
+ ```
161
+
162
+ ```ruby
163
+ # .rbs
164
+ class Enumerable[Elem]
165
+ def first: () -> Elem?
166
+ | (Integer) -> Array[Elem]
167
+ end
168
+ ```
169
+
170
+ `Enumerable`'s `#first` method has two different signatures.
171
+
172
+ When called with no arguments, the return value will either be an instance of
173
+ whatever type is contained in the enumerable, or `nil`. We represent that with
174
+ the type variable `Elem`, and the `?` suffix nilable marker.
175
+
176
+ When called with an `Integer` positional argument, the return value will be an
177
+ `Array` of whatever type is contained.
178
+
179
+ The `?` syntax is a convenient shorthand for a union with nil. An equivalent union type woould be `(Elem | nil)`.
180
+
181
+ ### Keyword Arguments
182
+
183
+ **Example**: `String#lines`
184
+
185
+ ```ruby
186
+ # .rb
187
+ "hello\nworld\n".lines
188
+ # => ["hello\n", "world\n"]
189
+ "hello world".lines(' ')
190
+ # => ["hello ", " ", "world"]
191
+ "hello\nworld\n".lines(chomp: true)
192
+ # => ["hello", "world"]
193
+ ```
194
+
195
+ ```ruby
196
+ # .rbs
197
+ class String
198
+ def lines: (?String, ?chomp: bool) -> Array[String]
199
+ end
200
+ ```
201
+
202
+ `String`'s `#lines` method take two arguments: one optional String argument, and another optional boolean keyword argument. It returns an `Array` of `String`s.
203
+
204
+ Keyword arguments are declared similar to in ruby, with the keyword immediately followed by a colon. Keyword arguments that are optional are indicated as optional using the same `?` prefix as positional arguments.
205
+
206
+
207
+ ### Class methods
208
+
209
+ **Example**: `Time.now`
210
+
211
+ ```ruby
212
+ # .rb
213
+ Time.now
214
+ # => 2009-06-24 12:39:54 +0900
215
+ ```
216
+
217
+ ```ruby
218
+ class Time
219
+ def self.now: () -> Time
220
+ end
221
+ ```
222
+
223
+ `Time`'s class method `now` takes no arguments, and returns an instance of the
224
+ `Time` class.
225
+
226
+ ### Block Arguments
227
+
228
+ **Example**: `Array#filter`
229
+
230
+ ```ruby
231
+ # .rb
232
+ [1,2,3,4,5].select {|num| num.even? }
233
+ # => [2, 4]
234
+ %w[ a b c d e f ].select {|v| v =~ /[aeiou]/ }
235
+ # => ["a", "e"]
236
+ [1,2,3,4,5].filter
237
+ ```
238
+
239
+ ```ruby
240
+ # .rbs
241
+ class Array[Elem]
242
+ def filter: () { (Elem) -> boolish } -> ::Array[Elem]
243
+ | () -> ::Enumerator[Elem, ::Array[Elem]]
244
+ end
245
+ ```
246
+
247
+ `Array`'s `#filter` method, when called with no arguments returns an Enumerator.
248
+
249
+ When called with a block, the method returns an `Array` of whatever type the original contained. The block will take one argument, of the type of the contained value, and the block will return a truthy or falsy value.
250
+
251
+ `boolish` is a special keyword for any type that will be treated as if it were a `bool`.
252
+
253
+ ### Type Variables
254
+
255
+ **Example**: `Hash`, `Hash#keys`
256
+
257
+ ```ruby
258
+ h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 }
259
+ h.keys
260
+ # => ["a", "b", "c", "d"]
261
+ ```
262
+
263
+ ```ruby
264
+ # .rbs
265
+ class Hash[K, V]
266
+ def keys: () -> Array[K]
267
+ end
268
+ ```
269
+
270
+ Generic types in RBS are parameterized at declaration time. These type variables are then available throughout all the methods contained in the `class` block.
271
+
272
+ `Hash`'s `#keys` method takes no arguments, and returns an `Array` of the first type parameter. In the above example, `a` is of concrete type `Hash[String, Integer]`, so `#keys` returns an `Array` for `String`.
273
+
274
+
275
+ ```ruby
276
+ # .rb
277
+ a = [ "a", "b", "c", "d" ]
278
+ a.collect {|x| x + "!"}
279
+ # => ["a!", "b!", "c!", "d!"]
280
+ a.collect.with_index {|x, i| x * i}
281
+ # => ["", "b", "cc", "ddd"]
282
+ ```
283
+
284
+ ```ruby
285
+ # .rbs
286
+ class Array[Elem]
287
+ def collect: [U] () { (Elem) -> U } -> Array[U]
288
+ | () -> Enumerator[Elem, Array[untyped]]
289
+ end
290
+ ```
291
+
292
+ Type variables can also be introduced in methods. Here, in `Array`'s `#collect` method, we introduce a type variable `U`. The block passed to `#collect` will receive a parameter of type `Elem`, and return a value of type `U`. Then `#collect` will return an `Array` of type `U`.
293
+
294
+ In this example, the method receives its signature from the inferred return type of the passed block. When then block is absent, as in when the method returns an `Enumerator`, we can't infer the type, and so the return value of the enumerator can only be described as `Array[untyped]`.
295
+
296
+ ### Tuples
297
+
298
+ **Examples**: `Enumerable#partition`, `Enumerable#to_h`
299
+
300
+ ```ruby
301
+ (1..6).partition { |v| v.even? }
302
+ # => [[2, 4, 6], [1, 3, 5]]
303
+ ```
304
+
305
+ ```ruby
306
+ class Enumerable[Elem]
307
+ def partition: () { (Elem) -> boolish } -> [Array[Elem], Array[Elem]]
308
+ | () -> ::Enumerator[Elem, [Array[Elem], Array[Elem] ]]
309
+ end
310
+ ```
311
+
312
+ `Enumerable`'s `partition` method, when given a block, returns a 2-item tuple of `Array`s containing the original type of the `Enumerable`.
313
+
314
+ Tuples can be of any size, and they can have mixed types.
315
+
316
+ ```ruby
317
+ (1..5).to_h {|x| [x, x ** 2]}
318
+ # => {1=>1, 2=>4, 3=>9, 4=>16, 5=>25}
319
+ ```
320
+
321
+ ```ruby
322
+ class Enumerable[Elem]
323
+ def to_h: () -> ::Hash[untyped, untyped]
324
+ | [T, U] () { (Elem) -> [T, U] } -> ::Hash[T, U]
325
+ end
326
+ ```
327
+
328
+ `Enumerable`'s `to_h` method, when given a block that returns a 2-item tuple, returns a `Hash` with keys the type of the first position in the tuple, and values the type of the second position in the tuple.