rdl 1.0.0.rc3 → 1.0.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +44 -0
  3. data/.travis.yml +19 -0
  4. data/LICENSE +29 -0
  5. data/README.md +509 -0
  6. data/Rakefile +27 -0
  7. data/gemfiles/Gemfile.travis +5 -0
  8. data/lib/rdl/wrap.rb +1 -1
  9. data/lib/rdl_types.rb +4 -2
  10. data/rdl.gemspec +23 -0
  11. data/test/disabled_test_coverage.rb +156 -0
  12. data/test/disabled_test_rdoc.rb +116 -0
  13. data/test/test_alias.rb +66 -0
  14. data/test/test_contract.rb +70 -0
  15. data/test/test_dsl.rb +85 -0
  16. data/test/test_generic.rb +188 -0
  17. data/test/test_intersection.rb +41 -0
  18. data/test/test_le.rb +193 -0
  19. data/test/test_lib_types.rb +194 -0
  20. data/test/test_member.rb +148 -0
  21. data/test/test_parser.rb +196 -0
  22. data/test/test_rdl.rb +301 -0
  23. data/test/test_rdl_type.rb +70 -0
  24. data/test/test_type_contract.rb +187 -0
  25. data/test/test_types.rb +221 -0
  26. data/test/test_wrap.rb +46 -0
  27. data/types/{ruby-2.2.0 → ruby-2.x}/_aliases.rb +0 -0
  28. data/types/{ruby-2.2.0 → ruby-2.x}/abbrev.rb +2 -2
  29. data/types/{ruby-2.2.0 → ruby-2.x}/array.rb +2 -2
  30. data/types/{ruby-2.2.0 → ruby-2.x}/base64.rb +2 -2
  31. data/types/{ruby-2.2.0 → ruby-2.x}/basic_object.rb +2 -1
  32. data/types/{ruby-2.2.0 → ruby-2.x}/benchmark.rb +2 -2
  33. data/types/{ruby-2.2.0 → ruby-2.x}/bigdecimal.rb +4 -4
  34. data/types/{ruby-2.2.0 → ruby-2.x}/bigmath.rb +2 -2
  35. data/types/{ruby-2.2.0 → ruby-2.x}/class.rb +3 -3
  36. data/types/{ruby-2.2.0 → ruby-2.x}/complex.rb +2 -2
  37. data/types/{ruby-2.2.0 → ruby-2.x}/coverage.rb +2 -2
  38. data/types/{ruby-2.2.0 → ruby-2.x}/csv.rb +2 -2
  39. data/types/{ruby-2.2.0 → ruby-2.x}/date.rb +2 -2
  40. data/types/{ruby-2.2.0 → ruby-2.x}/dir.rb +3 -3
  41. data/types/{ruby-2.2.0 → ruby-2.x}/encoding.rb +2 -2
  42. data/types/{ruby-2.2.0 → ruby-2.x}/enumerable.rb +2 -2
  43. data/types/{ruby-2.2.0 → ruby-2.x}/enumerator.rb +1 -1
  44. data/types/{ruby-2.2.0 → ruby-2.x}/exception.rb +2 -0
  45. data/types/{ruby-2.2.0 → ruby-2.x}/file.rb +2 -2
  46. data/types/{ruby-2.2.0 → ruby-2.x}/fileutils.rb +1 -1
  47. data/types/{ruby-2.2.0 → ruby-2.x}/fixnum.rb +2 -2
  48. data/types/{ruby-2.2.0 → ruby-2.x}/float.rb +2 -2
  49. data/types/{ruby-2.2.0 → ruby-2.x}/gem.rb +3 -1
  50. data/types/{ruby-2.2.0 → ruby-2.x}/hash.rb +2 -2
  51. data/types/{ruby-2.2.0 → ruby-2.x}/integer.rb +2 -2
  52. data/types/{ruby-2.2.0 → ruby-2.x}/io.rb +1 -1
  53. data/types/{ruby-2.2.0 → ruby-2.x}/kernel.rb +1 -1
  54. data/types/{ruby-2.2.0 → ruby-2.x}/marshal.rb +2 -2
  55. data/types/{ruby-2.2.0 → ruby-2.x}/matchdata.rb +3 -3
  56. data/types/{ruby-2.2.0 → ruby-2.x}/math.rb +2 -2
  57. data/types/{ruby-2.2.0 → ruby-2.x}/numeric.rb +2 -2
  58. data/types/{ruby-2.2.0 → ruby-2.x}/object.rb +1 -1
  59. data/types/{ruby-2.2.0 → ruby-2.x}/pathname.rb +2 -2
  60. data/types/{ruby-2.2.0 → ruby-2.x}/process.rb +8 -8
  61. data/types/{ruby-2.2.0 → ruby-2.x}/random.rb +2 -2
  62. data/types/{ruby-2.2.0 → ruby-2.x}/range.rb +2 -2
  63. data/types/{ruby-2.2.0 → ruby-2.x}/rational.rb +2 -2
  64. data/types/{ruby-2.2.0 → ruby-2.x}/regexp.rb +2 -2
  65. data/types/{ruby-2.2.0 → ruby-2.x}/set.rb +2 -2
  66. data/types/{ruby-2.2.0 → ruby-2.x}/string.rb +1 -1
  67. data/types/{ruby-2.2.0 → ruby-2.x}/strscan.rb +3 -3
  68. data/types/{ruby-2.2.0 → ruby-2.x}/symbol.rb +2 -2
  69. data/types/{ruby-2.2.0 → ruby-2.x}/time.rb +2 -2
  70. data/types/ruby-2.x/uri.rb +20 -0
  71. data/types/{ruby-2.2.0 → ruby-2.x}/yaml.rb +3 -3
  72. metadata +70 -51
  73. data/lib/rdl/types/wild.rb +0 -26
  74. data/types/other/chronic.rb +0 -5
  75. data/types/other/paperclip_attachment.rb +0 -7
  76. data/types/other/securerandom.rb +0 -4
  77. data/types/ruby-2.2.0/uri.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 865f3153019b7c93742a00d8da6258278b067050
4
- data.tar.gz: 51006514c6074ff9ebb6e13f48267966a6c4445f
3
+ metadata.gz: 34e6eddfee2d08722132c424591d878ffbc18f02
4
+ data.tar.gz: ff24c0cc1d42115c74a08eb71c4e559387b70ec9
5
5
  SHA512:
6
- metadata.gz: 8e63cbe09247ef295b49ceb627f3f473950981d76cb2a4cd553fb0bf3a4c84ed824105fe5b0ec75b52eb5f540cad0a973a80b24ba6946069a081bcb77f32d2c8
7
- data.tar.gz: 0a8e2be67f67638d5016cdc5f238ed4e2a88c64d99c588b8b76f6ae48e259b98884326c4b18d83add06dec3ad9ed075c57f1bcac63b9c62b50b295233d01d673
6
+ metadata.gz: 4b73359d42f51dd62711d909c45871e5846f8f07550ec9dee14c5f37e01768fc2d7fd50cb2fcdda5589eb2a8e6ae3c7f4c71c3207eb125116704ece7533d57e2
7
+ data.tar.gz: 5fa178c68df8d9e1f302c921d6037b3b4178f8219c1d4d9ac133118958b6b3f9a8b9181a459b59611e2dccd30b4772787defd10e6885fd322bb358bde5efb593
@@ -0,0 +1,44 @@
1
+ {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf210
2
+ {\fonttbl\f0\fnil\fcharset0 Consolas;}
3
+ {\colortbl;\red255\green255\blue255;\red38\green38\blue38;}
4
+ \margl1440\margr1440\vieww10800\viewh8400\viewkind0
5
+ \deftab720
6
+ \pard\pardeftab720
7
+
8
+ \f0\fs24 \cf2 # Compiled source #\
9
+ ###################\
10
+ *.com\
11
+ *.class\
12
+ *.dll\
13
+ *.exe\
14
+ *.o\
15
+ *.so\
16
+ \'a0\
17
+ # Packages #\
18
+ ############\
19
+ # it's better to unpack these files and commit the raw source\
20
+ # git has its own built in compression methods\
21
+ *.7z\
22
+ *.dmg\
23
+ *.gz\
24
+ *.iso\
25
+ *.jar\
26
+ *.rar\
27
+ *.tar\
28
+ *.zip\
29
+ \'a0\
30
+ # Logs and databases #\
31
+ ######################\
32
+ *.log\
33
+ *.sql\
34
+ *.sqlite\
35
+ \'a0\
36
+ # OS generated files #\
37
+ ######################\
38
+ .DS_Store\
39
+ .DS_Store?\
40
+ ._*\
41
+ .Spotlight-V100\
42
+ .Trashes\
43
+ ehthumbs.db\
44
+ Thumbs.db}
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+ gemfile:
3
+ - gemfiles/Gemfile.travis
4
+ rvm:
5
+ - 2.1.0
6
+ - 2.1.1
7
+ - 2.1.2
8
+ - 2.1.3
9
+ - 2.1.4
10
+ - 2.1.5
11
+ - 2.1.6
12
+ - 2.1.7
13
+ - 2.1.8
14
+ - 2.2.0
15
+ - 2.2.1
16
+ - 2.2.2
17
+ - 2.2.3
18
+ - 2.2.4
19
+ sudo: false
data/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ Copyright (c) 2014-2015, University of Maryland, College Park.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are
6
+ met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,509 @@
1
+ # Introduction
2
+
3
+ RDL is a lightweight system for adding contracts to Ruby. A *contract* decorates a method with assertions describing what the method assumes about its inputs (called a *precondition*) and what the method guarantees about its outputs (called a *postcondition*). For example, using RDL we can write
4
+
5
+ ```
6
+ require 'rdl'
7
+
8
+ pre { |x| x > 0 }
9
+ post { |r,x| r > 0 }
10
+ def sqrt(x)
11
+ # return the square root of x
12
+ end
13
+ ```
14
+
15
+ Given this program, RDL intercepts the call to `sqrt` and passes its argument to the `pre` block, which checks that the arugment is positive. Then when `sqrt` returns, RDL passes the return value (as `r`) and the initial argument (as `x`) to the `post` block, which checks that the return is positive. (Let's ignore complex numbers to keep things simple...)
16
+
17
+
18
+ RDL contracts are enforced at method entry and exit. For example, if we call `sqrt(49)`, RDL first checks that `49 > 0`; then it passes `49` to `sqrt`, which (presumably) returns `7`; then RDL checks that `7 > 0`; and finally it returns `7`.
19
+
20
+ In addition to arbitrary pre- and post-conditions, RDL also has extensive support for contracts that are *types*. For example, we can write the following in RDL:
21
+
22
+ ```
23
+ require 'rdl'
24
+
25
+ type '(Fixnum, Fixnum) -> String'
26
+ def m(x,y) ... end
27
+ ```
28
+
29
+ This indicates that `m` is that method that returns a `String` if given two `Fixnum` arguments. Again this contract is enforced at run-time: When `m` is called, RDL checks that `m` is given exactly two arguments and both are `Fixnums`, and that `m` returns an instance of `String`. RDL supports many more complex type annotations; see below for a complete discussion and examples. We should emphasize here that RDL types are enforced as contracts at method entry and exit. There is no static checking that the method body conforms to the types.
30
+
31
+ RDL contracts and types are stored in memory at run time, so it's also possible for programs to query them. RDL includes lots of contracts and types for the core and standard libraries. Since those methods are generally trustworthy, RDL doesn't actually enforce the contracts (since that would add overhead), but they are available to search and query. For example:
32
+
33
+ ```
34
+ > require 'rdl'
35
+ => true
36
+ > require 'rdl_types'
37
+ => true
38
+
39
+ > rdl_query 'hash' # get type for instance method of current class
40
+ Object#hash: () -> Fixnum
41
+ => nil
42
+ > rdl_query 'String#include' # get type for instance method of another class
43
+ String#include?: (String) -> FalseClass or TrueClass
44
+ => nil
45
+ > rdl_query 'Pathname.glob' # get type for singleton method of a class
46
+ Pathname.glob: (String p1, ?String p2) -> Array<Pathname>
47
+ => nil
48
+ ```
49
+
50
+ Currently only type information is returned by `rdl_query` (and not other pre or postconditions).
51
+
52
+ # RDL Reference
53
+
54
+ ## Supported versions of Ruby
55
+
56
+ RDL currently supports Ruby 2.2. It may or may not work with other versions.
57
+
58
+ ## Installing RDL
59
+
60
+ `gem install rdl` should do it.
61
+
62
+ ## Loading RDL
63
+
64
+ Use `require 'rdl'` to load the RDL library. If you want to use the core and standard library type signatures that come with RDL, follow it with `require 'rdl_types'`. This will load the types based on the current `RUBY_VERSION`. Currently RDL has types for the following versions of Ruby:
65
+
66
+ * 2.x
67
+
68
+ (Currently all these are assume to have the same library type signatures, which may not be correct.)
69
+
70
+ If you're using Ruby on Rails, you can similarly `require 'rails_types'` to load in type annotations for the current `Rails::VERSION::STRING`. More specifically, add the following lines in `application.rb` after the `Bundler.require` call. (This placement is needed so the Rails version string is available and the Rails environment is loaded):
71
+
72
+ ```
73
+ require 'rdl'
74
+ require 'rdl_types'
75
+ require 'rails_types'
76
+ ```
77
+
78
+ Currently RDL has types for the following versions of Rails:
79
+
80
+ * Rails support is currently almost non-existent; more coming in the future
81
+
82
+ ## Preconditions and Postconditions
83
+
84
+ The `pre` method takes a block and adds that block as a precondition to a method. When it's time to check the precondition, the block will be called with the method's arguments. If the block returns `false` or `nil` the precondition is considered to have failed, and RDL will raise a `ContractError`. Otherwise, if the block returns a true value, then the method executes as usual. The block can also raise its own error if the contract fails.
85
+
86
+ The `pre` method can be called in several ways:
87
+
88
+ * `pre { block }` - Apply precondition to the next method to be defined
89
+ * `pre(mth) { block }` - Apply precondition to method `mth` of the current class, where `mth` is a `Symbol` or `String`
90
+ * `pre(cls, mth) { block }` - Apply precondition to method `mth` of class `cls`, where `cls` is a `Class`, `Symbol`, or `String`, and `mth` is a `Symbol` or `String`
91
+
92
+ The `post` method is similar, except its block is called with the return value of the method (in the first position) followed by all the method's arguments. For example, you probably noticed that for `sqrt` above the `post` block took the return value `r` and the method argument `x`.
93
+
94
+ (Note: RDL does *not* clone or dup the arguments at method entry. So, for example, if the method body has mutated fields stored inside those argument objects, the `post` block or any other check evaluated afterwards will see the mutated field values rather than the original values.)
95
+
96
+ The `post` method can be called in the same ways as `pre`.
97
+
98
+ Methods can have no contracts, `pre` by itself, `post` by itself, both, or multiple instances of either. If there are multiple contracts, RDL checks that *all* contracts are satisfied, in the order that the contracts were bound to the method.
99
+
100
+ ## Type Signatures
101
+
102
+ The `type` method adds a type contract to a method. It supports the same calling patterns as `pre` and `post`, except rather than a block, it takes a string argument describing the type. More specifically, `type` can be called as:
103
+
104
+ * `type 'typ'`
105
+ * `type m, 'typ'`
106
+ * `type cls, mth, 'typ'`
107
+
108
+ A type string generally has the form `(typ1, ..., typn) -> typ` indicating a method that takes `n` arguments of types `typ1` through `typn` and returns type `typ`. To illustrate the various types RDL supports, we'll use examples from the core library type annotations.
109
+
110
+ ### Nominal Types
111
+
112
+ A nominal type is simply a class name, and it matches any object of that class or any subclass.
113
+
114
+ ```
115
+ type String, :insert, '(Fixnum, String) -> String'
116
+ ```
117
+
118
+ ### Nil Type
119
+
120
+ The nominal type `NilClass` can also be written as `nil`. The only object of this type is `nil`:
121
+
122
+ ```
123
+ type IO, :close, '() -> nil' # IO#close always returns nil
124
+ ```
125
+
126
+ Currently, `nil` is treated as if it were an instance of any class.
127
+ ```
128
+ x = "foo"
129
+ x.insert(0, nil) # RDL does not report a type error
130
+ ```
131
+ We chose this design based on prior experience with static type systems for Ruby, where not allowing this leads to a lot of false positive errors from the type system. However, we may change this in the future.
132
+
133
+ ### Top Type (%any)
134
+
135
+ RDL includes a special "top" type `%any` that matches any object:
136
+ ```
137
+ type Object, :=~, '(%any) -> nil'
138
+ ```
139
+ We call this the "top" type because it is the top of the subclassing hierarchy RDL uses. Note that `%any` is more general than `Object`, because not all classes inherit from `Object`, e.g., `BasicObject` does not.
140
+
141
+ ### Union Types
142
+
143
+ Many Ruby methods can take several different types of arguments or return different types of results. The union operator `or` can be used to indicate a position where multiple types are possible.
144
+
145
+ ```
146
+ type IO, :putc, '(Numeric or String) -> %any'
147
+ type String, :getbyte, '(Fixnum) -> Fixnum or nil'
148
+ ```
149
+
150
+ Note that for `getbyte`, we could leave off the `nil`, but we include it to match the current documentation of this method.
151
+
152
+ ### Intersection Types
153
+
154
+ Sometimes Ruby methods have several different type signatures. (In Java these would be called *overloaded* methods.) In RDL, such methods are assigned a set of type signatures:
155
+
156
+ ```
157
+ type String, :[], '(Fixnum) -> String or nil'
158
+ type String, :[], '(Fixnum, Fixnum) -> String or nil'
159
+ type String, :[], '(Range or Regexp) -> String or nil'
160
+ type String, :[], '(Regexp, Fixnum) -> String or nil'
161
+ type String, :[], '(Regexp, String) -> String or nil'
162
+ type String, :[], '(String) -> String or nil'
163
+ ```
164
+
165
+ We say the method's type is the *intersection* of the types above.
166
+
167
+ When this method is called at run time, RDL checks that at least one type signature matches the call:
168
+
169
+ ```
170
+ "foo"[0] # matches first type
171
+ "foo"[0,2] # matches second type
172
+ "foo"[0..2] # matches third type
173
+ "foo"[0, "bar"] # error, doesn't match any type
174
+ # etc
175
+ ```
176
+
177
+ Notice that union types in arguments could also be written as intersection types of methods, e.g., instead of the third type of `[]` above we could have equivalently written
178
+
179
+ ```
180
+ type String, :[], '(Range) -> String or nil'
181
+ type String, :[], '(Regexp) -> String or nil'
182
+ ```
183
+
184
+ ### Optional Argument Types
185
+
186
+ Optional arguments are denoted in RDL by putting `?` in front of the argument's type. For example:
187
+
188
+ ```
189
+ type String, :chomp, '(?String) -> String'
190
+ ```
191
+
192
+ This is actually just a shorthand for an equivalent intersection type:
193
+
194
+ ```
195
+ type String, :chomp, '() -> String'
196
+ type String, :chomp, '(String) -> String'
197
+ ```
198
+
199
+ but it helps make types more readable.
200
+
201
+ Like Ruby, RDL allows optional arguments to appear anywhere in a method's type signature.
202
+
203
+ ### Variable Length Argument Types
204
+
205
+ In RDL, `*` is used to decorate an argument that may appear zero or more times. Currently in RDL this annotation may only appear on the rightmost argument. For example, `String#delete` takes one or more `String` arguments:
206
+
207
+ ```
208
+ type String, :delete, '(String, *String) -> String'
209
+ ```
210
+
211
+ ### Named Argument Types
212
+
213
+ RDL allows arguments to be named, for documentation purposes. Names are given after the argument's type, and they do not affect type contract checking in any way. For example:
214
+
215
+ ```
216
+ type Fixnum, :to_s, '(?Fixnum base) -> String'
217
+ ```
218
+
219
+ Here we've named the first argument of `to_s` as `base` to give some extra hint as to its meaning.
220
+
221
+ ### Block Types
222
+
223
+ Types signatures can include a type for a method's block argument:
224
+
225
+ ```
226
+ type Pathname, :ascend, '() { (Pathname) -> %any } -> %any'
227
+ ```
228
+
229
+ Here the block passed to `Pathname.ascend` must take a `Pathname` and can return any object.
230
+
231
+ This is a *higher-order* contract, because it applies to a higher-order method, i.e., a method that can take a block argument.
232
+
233
+ Currently higher-order contracts are not enforced. That is, RDL will not actually check contracts on block arguments.
234
+
235
+ ### Class/Singleton Method Types
236
+
237
+ RDL method signatures can be used both for instance methods and for class methods (often called *singleton methods* in Ruby). To indicate a type signature applies to a singleton method, prefix the method name with `self.`:
238
+
239
+ ```
240
+ type File, 'self.dirname', '(String file) -> String dir'
241
+ ```
242
+
243
+ (Notice also the use of a named return type, which we haven't seen before.)
244
+
245
+ Type signatures can be added to `initialize` by giving a type signature for `self.new`:
246
+
247
+ ```
248
+ type File, 'self.new', '(String file, ?String mode, ?String perm, ?Fixnum opt) -> File'
249
+ ```
250
+
251
+ ### Structural Types
252
+
253
+ Some Ruby methods are intended to take any object that has certain methods. RDL uses *structural types* to denote such cases:
254
+
255
+ ```
256
+ type IO, :puts, '(*[to_s: () -> String]) -> nil'
257
+ ```
258
+
259
+ Here `IO#puts` can take zero or more arguments, all of which must have a `to_s` method that takes no arguments and returns a `String`.
260
+
261
+ The actual checking that RDL does here varies depending on what type information is available. Suppose we call `puts(o)`. If `o` is an instance of a class that has a type signature `t` for `to_s`, then RDL will check that `t` is compatible with `() -> String`. On the other hand, if `o` is an instance of a class with no type signature for `to_s`, RDL only checks that `o` has a `to_s` method, but it doesn't check its argument or return types.
262
+
263
+ ### Singleton Types
264
+
265
+ Not to be confused with types for singleton methods, RDL includes *singleton types* that denote positions that always have one particular value; this typically happens only in return positions. For example, `Dir#mkdir` always returns the value 0:
266
+
267
+ ```
268
+ type Dir, 'self.mkdir', '(String, ?Fixnum) -> 0'
269
+ ```
270
+
271
+ In RDL, any integer or floating point number denotes a singleton type. Arbitrary values can be turned into singleton types by wrapping them in `${.}`. For example, `Float#angle` always returns 0 or pi.
272
+
273
+ ```
274
+ type Float, :angle, '() -> 0 or ${Math::PI}'
275
+ ```
276
+
277
+ RDL checks if a value matches a singleton type using `equal?`. As a consequence, singleton string types aren't currently possible.
278
+
279
+ ### Self Type
280
+
281
+ Consider a method that returns `self`:
282
+
283
+ ```
284
+ class A
285
+ def id
286
+ self
287
+ end
288
+ end
289
+ ```
290
+
291
+ If that method might be inherited, we can't just give it a nominal type, because it will return a different object type in a subclass:
292
+
293
+ ```
294
+ class B < A
295
+ end
296
+
297
+ type A, :id, '() -> A'
298
+ A.new.id # okay, returns an A
299
+ B.new.id # type error, returns a B
300
+ ```
301
+
302
+ To solve this problem, RDL includes a special type `self` for this situation:
303
+
304
+ ```
305
+ type A, :id, '() -> self'
306
+ A.new.id # okay, returns self
307
+ B.new.id # also okay, returns self
308
+ ```
309
+
310
+ Note that type `self` means *exactly* the self object, i.e., it is a singleton type. It does not mean "any object of self's class." Thus, for example, `Object#clone` has type `() -> %any`, since it will return a different object. We might change this behavior in the future.
311
+
312
+ ### Type Aliases
313
+
314
+ RDL allows types to be aliases to make them faster to write down and more readable. All type aliases begin with `%`. RDL has one built-in alias, `%bool`, which is shorthand for `TrueClass or FalseClass`:
315
+
316
+ ```
317
+ type String, :==, '(%any) -> %bool'
318
+ ```
319
+
320
+ Note it is not a bug that `==` is typed to allow any object. Though you would think that developers would generally only compare objects of the same class (since otherwise `==` almost always returns false), in practice a lot of code does compare objects of different classes.
321
+
322
+ Method `type_alias(name, typ)` can be used to create a user-defined type alias, where `name` must begin with `%`:
323
+
324
+ ```
325
+ type_alias '%real', 'Integer or Float or Rational'
326
+ type_alias '%string', '[to_str: () -> String]'
327
+ type_alias '%path', '%string or Pathname'
328
+ ```
329
+
330
+ Type aliases have to be created before they are used (so above, `%path` must be defined after `%string`).
331
+
332
+ ### Generic Class Types
333
+
334
+ RDL supports *parametric polymorphism* for classes, a.k.a. *generics*. The `type_params` method names the type parameters of the class, and those parameters can then be used inside type signatures:
335
+
336
+ ```
337
+ class Array
338
+ type_params [:t], :all?
339
+
340
+ type :shift, '() -> t'
341
+ end
342
+ ```
343
+
344
+ Here the first argument to `type_params` is a list of symbols or strings that name the type parameters. In this case there is one parameter, `t`, and it is the return type of `shift`.
345
+
346
+ Generic types are applied to type arguments using `<...>` notation, e.g., `Array<Fixnum>` is an `Array` class where `t` is replaced by `Fixnum`. Thus, for example, if `o` is an `Array<Fixnum>`, then `o.shift` returns `Fixnum`. As another example, here is the type for the `[]` method of `Array`:
347
+
348
+ ```
349
+ type Array, :[], '(Range) -> Array<t>'
350
+ type Array, :[], '(Fixnum or Float) -> t'
351
+ type Array, :[], '(Fixnum, Fixnum) -> Array<t>'
352
+ ```
353
+
354
+ Thus if `o` is again an `Array<Fixnum>`, then `o[0]` returns a `Fixnum` and `o[0..5]` returns an `Array<Fixnum>`.
355
+
356
+ In general it's impossible to assign generic types to objects without knowing the programmer's intention. For example, consider code as simple as `x = [1,2]`. Is it the programmer's intention that `x` is an `Array<Fixnum>`? `Array<Numeric>`? `Array<Object>`?
357
+
358
+ Thus, by default, even though `Array` is declared to take type parameters, by default RDL treats array objects at the *raw* type `Array`, which means the type parameters are ignored whenever they appear in types. For our example, this means a call such as `x.push("three")` would not be reported as an error (the type signature of `Array#push` is `'(?t) -> Array<t>'`).
359
+
360
+ To fully enforce generic types, RDL requires that the developer `instantiate!` an object with the desired type parameters:
361
+
362
+ ```
363
+ x = [1,2]
364
+ x.instantate!('Fixnum')
365
+ x.push("three") # type error
366
+ ```
367
+
368
+ Note that the instantiated type is associated with the object, not the variable:
369
+ ```
370
+ y = x
371
+ y.push("three") # also a type error
372
+ ```
373
+
374
+ When RDL instantiates an object with type parameters, it needs to ensure the object's contents are consistent with the type. Currently this is enforced using the second parameter to `type_params`, which must name a method that behaves like `Array#all?`, i.e., it iterates through the contents, checking that a block argument is satisfied. As seen above, for `Array` we call `type_params(:t, :all?)`. Then at the call `x.instantiate('Fixnum')`, RDL will call `Array#all?` to iterate through the contents of `x` to check they have type `Fixnum`.
375
+
376
+ RDL also includes a `deinstantiate!` method to remove the type instantiation from an object:
377
+ ```
378
+ x.deinstantiate!
379
+ x.push("three") # no longer a type error
380
+ ```
381
+
382
+ Finally, `type_params` can optionally take a third argument that is an array of *variances*, which are either `:+` for covariance, `:-` for contravariance, or `:~` for invariance. If variances aren't listed, type parameters are assumed to be invariant, which is a safe default.
383
+
384
+ Variances are only used when RDL checks that one type is a subtype of another. This only happens in limited circumstances, e.g., arrays of arrays where all levels have instantiated types. So generally you don't need to worry much about the variance.
385
+
386
+ The rules for variances are standard. Let's assume `A` is a subclass of `B`. Also assume there is a class `C` that has one type parameter. Then:
387
+ * `C<A>` is a subtype of `C<A>` always
388
+ * `C<A>` is a subtype of `C<B>` if `C`'s type parameter is covariant
389
+ * `C<B>` is a subtype of `C<A>` if `C`'s type parameter is contravariant
390
+
391
+ ### Tuple Types
392
+
393
+ A type such as `Array<Fixnum>` is useful for homogeneous arrays, where all elements have the same type. But Ruby programs often use heterogenous arrays, e.g., `[1, "two"]`. The best generic type we can give this is `Array<Fixnum or String>`, but that's imprecise.
394
+
395
+ RDL includes special *tuple types* to handle this situation. Tuple types are written `[t1, ..., tn]`, denoting an `Array` of `n` elements of types `t1` through `tn`, in that order. For example, `[1, "two"]` has type `[Fixnum, String]`. As another example, here is the type of `Process#getrlimit`, which returns a two-element array of `Fixnums`:
396
+
397
+ ```
398
+ type Process, 'self.getrlimit', '(Symbol or String or Fixnum resource) -> [Fixnum, Fixnum] cur_max_limit'
399
+ ```
400
+
401
+ ### Finite Hash Types
402
+
403
+ Similarly to tuple types, RDL also supports *finite hash types* for heterogenous hashes. Finite hash types are written `{k1 => v1, ..., kn => vn}` to indicate a `Hash` with `n` mappings of type `ki` maps to `vi`. The `ki` may be strings, integers, floats, or constants denoted with `${.}`. If a key is a symbol, then the mapping should be written `ki: vi`. In the latter case, the `{}`'s can be left off:
404
+ ```
405
+ type MyClass, :foo, '(a: Fixnum, b: String) { () -> %any } -> %any'
406
+ ```
407
+ Here `foo`, takes a hash where key `:a` is mapped to a `Fixnum` and key `:b` is mapped to a `String`. Similarly, `{'a'=>Fixnum, 2=>String}` types a hash where keys `'a'` and `2` are mapped to a `Fixnum` and `String`, respectively. Both syntaxes can be used to define hash types.
408
+
409
+ ## Other Methods
410
+
411
+ RDL also includes a few other useful methods:
412
+
413
+ * `rdl_alias(new_name, old_name)` tells RDL that method `new_name` is an alias for method `old_name`, and therefore they should have the same contracts and types. This method is only needed when adding contracts and types to method that have already been aliased; it's not needed if the method is aliased after the contract or type has been added.
414
+
415
+ * `o.type_cast(t)` returns a new object that delegates all methods to `o` but that will be treated by RDL as if it had type `t`. For example, `x = "a".type_cast('nil')` will make RDL treat `x` as if it had type `nil`, even though it's a `String`.
416
+
417
+ * `rdl_nowrap`, if called at the top-level of a class, tells RDL to record contracts and types for methods in that class but *not* enforce them. This is mostly used for the core and standard libraries, which have trustworthy behavior hence enforcing their types and contracts is not worth the overhead.
418
+
419
+ # Bibliography
420
+
421
+ Here we list some papers on various systems we built exploring contracts, types and Ruby.
422
+
423
+ * T. Stephen Strickland, Brianna Ren, and Jeffrey S. Foster.
424
+ [Contracts for Domain-Specific Languages in Ruby](http://www.cs.umd.edu/~jfoster/papers/dls12.pdf).
425
+ In Dynamic Languages Symposium (DLS), Portland, OR, October 2014.
426
+
427
+ * Brianna M. Ren, John Toman, T. Stephen Strickland, and Jeffrey S. Foster.
428
+ [The Ruby Type Checker](http://www.cs.umd.edu/~jfoster/papers/oops13.pdf).
429
+ In Object-Oriented Program Languages and Systems (OOPS) Track at ACM Symposium on Applied Computing, pages 1565–1572, Coimbra, Portugal, March 2013.
430
+
431
+ * Jong-hoon (David) An, Avik Chaudhuri, Jeffrey S. Foster, and Michael Hicks.
432
+ [Dynamic Inference of Static Types for Ruby](http://www.cs.umd.edu/~jfoster/papers/popl11.pdf).
433
+ In ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (POPL), pages 459–472, Austin, TX, USA, January 2011.
434
+
435
+ * Jong-hoon (David) An.
436
+ [Dynamic Inference of Static Types for Ruby](http://www.cs.umd.edu/~jfoster/papers/thesis-an.pdf).
437
+ MS thesis, University of Maryland, College Park, 2010.
438
+
439
+ * Michael Furr.
440
+ [Combining Static and Dynamic Typing in Ruby](https://www.cs.umd.edu/~jfoster/papers/thesis-furr.pdf).
441
+ PhD thesis, University of Maryland, College Park, 2009.
442
+
443
+ * Michael Furr, Jong-hoon (David) An, Jeffrey S. Foster, and Michael Hicks.
444
+ [The Ruby Intermediate Langauge](http://www.cs.umd.edu/~jfoster/papers/dls09-ril.pdf).
445
+ In Dynamic Languages Symposium (DLS), pages 89–98, Orlando, Florida, October 2009.
446
+
447
+ * Michael Furr, Jong-hoon (David) An, and Jeffrey S. Foster.
448
+ [Profile-Guided Static Typing for Dynamic Scripting Languages](http://www.cs.umd.edu/~jfoster/papers/oopsla09.pdf).
449
+ In ACM SIGPLAN International Conference on Object-Oriented Programming, Systems, Languages and Applications (OOPSLA), pages 283–300, Orlando, Floria, October 2009. Best student paper award.
450
+
451
+ * Michael Furr, Jong-hoon (David) An, Jeffrey S. Foster, and Michael Hicks.
452
+ [Static Type Inference for Ruby](http://www.cs.umd.edu/~jfoster/papers/oops09.pdf).
453
+ In Object-Oriented Program Languages and Systems (OOPS) Track at ACM Symposium on Applied Computing (SAC), pages 1859–1866, Honolulu, Hawaii, March 2009.
454
+
455
+ # Copyright
456
+
457
+ Copyright (c) 2014-2015, University of Maryland, College Park. All rights reserved.
458
+
459
+ # Contributors
460
+
461
+ ## Authors
462
+
463
+ * [Jeffrey S. Foster](http://www.cs.umd.edu/~jfoster/)
464
+ * [Brianna M. Ren](https://www.cs.umd.edu/~bren/)
465
+ * [T. Stephen Strickland](https://www.cs.umd.edu/~sstrickl/)
466
+ * Alexander T. Yu
467
+
468
+ # RDL Build Status
469
+
470
+ [![Build Status](https://travis-ci.org/plum-umd/rdl.svg?branch=master)](https://travis-ci.org/plum-umd/rdl)
471
+
472
+ # TODO list
473
+
474
+ * ProcContract, Wrap, MethodType, support higher-order contracts for blocks
475
+ * And higher-order type checking
476
+ * Block passed to contracts don't work yet
477
+
478
+ * How to check whether initialize? is user-defined? method_defined? always
479
+ returns true, meaning wrapping isn't fully working with initialize.
480
+
481
+ * Currently if a NominalType name is expressed differently, e.g., A
482
+ vs. EnclosingClass::A, the types will be different when compared
483
+ with ==.
484
+
485
+ * Macros, %bool should really be %any
486
+
487
+ * Method types that are parametric themselves (not just ones that use
488
+ enclosing class parameters)
489
+
490
+ * Rails types
491
+
492
+ * Proc types
493
+
494
+ * Deferred contracts on new (watch for class addition)
495
+
496
+ * DSL contracts
497
+
498
+ * Documentation!
499
+
500
+ * double-splat arguments, which bind to an arbitrary set of keywords.
501
+
502
+ * included versus extended modules, e.g., Kernel is included in
503
+ + Object, so its class methods become Object's instance methods.
504
+
505
+ * Better story for different types for different Ruby versions.
506
+
507
+ * Better query facility (more kinds of searches). Contract queries?
508
+
509
+ * Write documentation on: Raw Contracts and Types, RDL Configuration, Code Overview