typed.rb 0.0.14 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +107 -2
- data/lib/typed/language.rb +20 -7
- data/lib/typed/model/tm_fun.rb +4 -3
- data/lib/typed/prelude.rb +1 -0
- data/lib/typed/prelude_existential_registry.bin +0 -0
- data/lib/typed/prelude_registry.bin +0 -0
- data/lib/typed/runtime.rb +0 -1
- data/lib/typed/types/polymorphism/type_variable.rb +0 -17
- data/lib/typed/types/polymorphism/type_variable_register.rb +5 -2
- data/lib/typed/types/polymorphism/unification.rb +1 -1
- data/lib/typed/types/ty_existential_type.rb +3 -0
- data/lib/typed/types/ty_object.rb +5 -5
- data/lib/typed/version.rb +3 -1
- data/spec/lib/examples/monoid/monoid_error1.rb +44 -0
- data/spec/lib/examples/monoid/monoid_error2.rb +44 -0
- data/spec/lib/examples/monoid/monoid_error3.rb +44 -0
- data/spec/lib/examples/monoid/monoid_error4.rb +44 -0
- data/spec/lib/language_spec.rb +49 -1
- data/spec/lib/typed_spec.rb +19 -0
- data/spec/lib/typing/generics_spec.rb +5 -3
- data/spec/lib/typing/tm_fun_spec.rb +1 -1
- data/spec/lib/typing/tm_module_spec.rb +1 -1
- data/spec/spec_helper.rb +3 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb42b861c759a6aaa03b52fd351f5a1615d5232a
|
4
|
+
data.tar.gz: 1d76b7a85bdd7758d28b54a1506734c123e2384c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 133005549d678a09477e777dc944e6cc3300bd89fee267f529d92986e20757170d4005dfa0385b4ec4722507458f0b52d2315f6355dcbac2aea135c1ac09bd83
|
7
|
+
data.tar.gz: 6bcbbe9cb52ffd37a466fec62fae0a11d6e14c04f25e3c66e4b4918c283f4d70faed9eb2e43210cc7cec08fac76234c543989ade1933791eed743e47850fd331
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#Typed.rb [![Build Status](https://circleci.com/gh/antoniogarrote/typed.rb.png?circle-token=:circle-token)](https://circleci.com/gh/antoniogarrote/typed.rb/tree/master)
|
1
|
+
#Typed.rb [![Build Status](https://circleci.com/gh/antoniogarrote/typed.rb.png?circle-token=:circle-token)](https://circleci.com/gh/antoniogarrote/typed.rb/tree/master) [![Coverage Status](https://coveralls.io/repos/github/antoniogarrote/typed.rb/badge.svg?branch=master)](https://coveralls.io/github/antoniogarrote/typed.rb?branch=master)
|
2
2
|
|
3
3
|
Gradual type checker for Ruby.
|
4
4
|
|
@@ -15,7 +15,7 @@ module Monoid
|
|
15
15
|
|
16
16
|
ts '#mempty / -> [T]'
|
17
17
|
abstract(:mempty)
|
18
|
-
|
18
|
+
|
19
19
|
end
|
20
20
|
|
21
21
|
|
@@ -53,3 +53,108 @@ end
|
|
53
53
|
moncat([1,2,3], Sum.new)
|
54
54
|
}
|
55
55
|
```
|
56
|
+
|
57
|
+
|
58
|
+
## Introduction
|
59
|
+
|
60
|
+
Typed.rb is an attempt to build a gradual type checker for the Ruby programming language.
|
61
|
+
|
62
|
+
Typed.rb provides a mechanism to add type annotations to Ruby program's classes, modules and methods, and makes it possible to type-check the annotated code statically.
|
63
|
+
Types annotations has no impact on performance during the execution of the program. Typed ruby code is valid regular ruby code.
|
64
|
+
Typed.rb leverages gradual typing for Ruby, this means that typed and untyped code can be freely mixed.
|
65
|
+
|
66
|
+
In it's current implementation Typed.rb includes the following components:
|
67
|
+
|
68
|
+
- A type signature language to annotate classes, modules and methods.
|
69
|
+
- A small run-time library that introduces a few extensions to ruby core types, like the type annotations method or the application of type parameters, it introduces some new types like Boolean and Pair and the notion of abstract methods.
|
70
|
+
- Type annotations for the Ruby Standard Library.
|
71
|
+
- A gradual type checker that can be executed statically to check the provided annotations.
|
72
|
+
|
73
|
+
Typed.rb tries to typecheck most of the static subset of Ruby with as few type annotations as possible, however, due to the highly dynamic nature of Ruby some compromises need to be made and the whole dynamic set of the language, say, dynamically evaluated methods and classes cannot be type-checked.
|
74
|
+
|
75
|
+
Typed.rb is still an ongoing effort and should be considered pre-alpha.
|
76
|
+
|
77
|
+
|
78
|
+
## Type system
|
79
|
+
|
80
|
+
Typed.rb type system's matches Ruby type system with a few exceptions:
|
81
|
+
|
82
|
+
- A ```unit``` matching the runtime ```NilClass``` class has been added to the type signature language. It is inhabited by a single instalce ```nil``` and it's a subclass of all other types. ```unit``` must always be used in type signatures
|
83
|
+
- A ```dynamic``` type that by-passes type checking will be generated byt the type-checker when no typing information is available for a given method or type. Type checking untyped code will produce the ```dynamic``` type as a result. ```dynamic``` must never diractely used in a type annotation.
|
84
|
+
- A ```Boolean ``` type has been added inhabited by only two values ```true``` and ```false``` and matching both run-time classes ```TrueClass``` and ```FalseClass``` has been added to the type signature language. ```Boolean``` must always be used in type signatures
|
85
|
+
- A polymorphic ```Pair[T][U]``` extending ```Array``` type has been added to the type signature language and to the runtime. Function and blocks receiving or returning arrays of two elements can be annotated as using the ```Pair``` type
|
86
|
+
- A ```Showable``` type with no methods has been added as mixin into the ```String``` and ```Symbol``` class and can be used to annotate methods receiving an instance of any of these two classes
|
87
|
+
|
88
|
+
|
89
|
+
## Type annotations
|
90
|
+
|
91
|
+
To introduce a new type annotation, the ```BasicObject#ts``` method can be used. They can appear in any part of the source code without taking in cosideration the location of the method being annotated, however as a rule, we try to add the type annotation before the definition of the method being annotated.
|
92
|
+
The method receives a string with the type signature and returns unit. At run-time the method is a noop.
|
93
|
+
|
94
|
+
A type signature for a method is composed of the following parts:
|
95
|
+
|
96
|
+
```
|
97
|
+
Type?(#/.)method[T]* / ArgumentType? (-> ArgumentType)* -> &BlockType? -> ReturnType
|
98
|
+
```
|
99
|
+
|
100
|
+
For example, the type signature of the ```#ts``` method itself is ```BasicObject#ts / String -> unit```.
|
101
|
+
|
102
|
+
The main components of a type signature are:
|
103
|
+
|
104
|
+
- ```Type``` (optional), type the method belongs to. If the type part of the signature is ommitted, the current wrapping class or module is considered to be the target of the method
|
105
|
+
- ```(#/.)``` instance/class indicator. Instance level methods are prefixed with ```#```, class level methods are prefixed with ```.```
|
106
|
+
- ```method``` message this method is handling
|
107
|
+
- ```[T]*``` method type variables for polymorphic methods
|
108
|
+
- ```/``` signature separator, a fixed character ```/``` separating the method information from the actual type arguments and return type information
|
109
|
+
- ```ArgumentType? (-> ArgumentType)* -> ReturnType``` an arrow (```->```) separated list of types representing the types of the input arguments and the output type of the method
|
110
|
+
|
111
|
+
If the method has no input arguments, the only return type can be expressed with a type with an initial ```->```. For example, a method with no arguments and returning ```nil``` can be expressed as ```-> unit```.
|
112
|
+
Higher order methods accepting procs or lambdas as inputs can be expressed between parentheses ```(ArgumentType? (-> ArgumentType)* -> ReturnType)```. Methods yielding a block can be expressed with final input argument of function type prefixed with a ```&```.
|
113
|
+
For example, the ```Enumerable#map``` method from the Ruby Standard Library accepting a block and parametric on the return value of that block has the following type annotation:
|
114
|
+
|
115
|
+
```
|
116
|
+
ts '#map[E] / &([T] -> [E]) -> Array[E]'
|
117
|
+
```
|
118
|
+
|
119
|
+
Methods accepting a variable number of arguments can be expressed using a suffix ellipsis ```...``` after the type of the vararg type. For instance, a method adding any number of integers could be expressed with the following type annotation:
|
120
|
+
|
121
|
+
```
|
122
|
+
ts '#my_sum / Integer... -> Integer'
|
123
|
+
```
|
124
|
+
|
125
|
+
More than one type annotation can be provided for the same method, but the arity of the annotations must be unique. Optional blocks are taking into consideration to compute the arity of the annotation.
|
126
|
+
|
127
|
+
In order to annotate classes and modules, the ```BasicObject#ts``` method is also used. Again, the type annotation can appear in any location of the source code but by convention we try to locate them before the implementation of the type if it is available.
|
128
|
+
|
129
|
+
Type annotations are only required for polymorphic types.
|
130
|
+
|
131
|
+
The type signature for a class/module has the following components:
|
132
|
+
|
133
|
+
```
|
134
|
+
type TypeName[T]? (super BaseType[U]*)?
|
135
|
+
```
|
136
|
+
|
137
|
+
- ```type``` prefix for all type annotations
|
138
|
+
- ```TypeName``` name of the class/module
|
139
|
+
- ```[T]*``` type variables for the polymorphic type
|
140
|
+
- ```(super BaseType[U]*)?``` base polymorphic class/module for the type
|
141
|
+
|
142
|
+
|
143
|
+
## Type inference and minimal typing
|
144
|
+
|
145
|
+
Typed.rb will try to perform automatic type inference for the following language constructs and don't need to be annotated:
|
146
|
+
|
147
|
+
- global variables
|
148
|
+
- instance variables
|
149
|
+
- local variables
|
150
|
+
- constants
|
151
|
+
|
152
|
+
To know more about the type inferance mechanism, check the implementation of the unification algorithm used by the type checker in ```lib/typed/types/polymorphism/unification.rb```.
|
153
|
+
|
154
|
+
Class and modules only need to be annotated if they are defined as polymorphic. All methods must be annotated.
|
155
|
+
|
156
|
+
## Running the type-checker
|
157
|
+
|
158
|
+
To execute the type checker, the ```bin/typed.rb``` script can be used. The script accepts a list of options and path to the entry-point of the source code to be type-checked. Currently the only option available is ```--missing-type``` that will force the type-checker to produce a warning every single time a default dynamic type is introduced due to lack of typing information.
|
159
|
+
|
160
|
+
The script will produce some error report output and return an appropriate exit value according to the result of the the check.
|
data/lib/typed/language.rb
CHANGED
@@ -87,6 +87,7 @@ module TypedRb
|
|
87
87
|
TypingContext.clear(:top_level)
|
88
88
|
check_result = nil
|
89
89
|
errors = {}
|
90
|
+
errors_accum = {}
|
90
91
|
ordered_files.each do |file|
|
91
92
|
$TYPECHECK_FILE = file
|
92
93
|
expr = File.open(file, 'r').read
|
@@ -95,9 +96,13 @@ module TypedRb
|
|
95
96
|
print '.'.green
|
96
97
|
rescue TypedRb::Types::UncomparableTypes, TypedRb::TypeCheckError => e
|
97
98
|
print 'E'.red
|
98
|
-
|
99
|
-
|
100
|
-
|
99
|
+
hash = e.to_s.hash
|
100
|
+
unless errors_accum[hash]
|
101
|
+
errors_accum[hash] = true
|
102
|
+
errors_for_file = errors[file] || []
|
103
|
+
errors_for_file << e
|
104
|
+
errors[file] = errors_for_file
|
105
|
+
end
|
101
106
|
end
|
102
107
|
end
|
103
108
|
top_level_errors = []
|
@@ -105,8 +110,12 @@ module TypedRb
|
|
105
110
|
::BasicObject::TypeRegistry.check_super_type_annotations
|
106
111
|
@unification_result = run_unification
|
107
112
|
rescue TypedRb::Types::UncomparableTypes, TypedRb::TypeCheckError => e
|
108
|
-
|
109
|
-
|
113
|
+
hash = e.to_s.hash
|
114
|
+
unless errors_accum[hash]
|
115
|
+
errors_accum[hash] = true
|
116
|
+
print 'E'.red
|
117
|
+
top_level_errors << e
|
118
|
+
end
|
110
119
|
end
|
111
120
|
puts "\n"
|
112
121
|
total_errors = all_errors(top_level_errors, errors)
|
@@ -134,8 +143,12 @@ module TypedRb
|
|
134
143
|
puts error.message.red
|
135
144
|
end
|
136
145
|
warnings_for_file.each do |warning|
|
137
|
-
|
138
|
-
|
146
|
+
hash = warning.to_s.hash
|
147
|
+
unless errors_accum[hash]
|
148
|
+
errors_accum[hash] = true
|
149
|
+
puts "\n"
|
150
|
+
puts warning.message.yellow
|
151
|
+
end
|
139
152
|
end
|
140
153
|
end
|
141
154
|
top_level_errors.each do |error|
|
data/lib/typed/model/tm_fun.rb
CHANGED
@@ -87,7 +87,7 @@ module TypedRb
|
|
87
87
|
function_arg_type = function_type.from[i]
|
88
88
|
# Generic arguments are parsed by runtime without checking constraints since they are not available at parsing type.
|
89
89
|
# We need to run unification in them before using the type to detect invalid type argument applications.
|
90
|
-
function_arg_type = function_arg_type.self_materialize if function_arg_type.is_a?(Types::TyGenericSingletonObject)
|
90
|
+
function_arg_type = function_arg_type.self_materialize.as_object_type if function_arg_type.is_a?(Types::TyGenericSingletonObject)
|
91
91
|
context = case arg.first
|
92
92
|
when :arg, :restarg
|
93
93
|
context.add_binding(arg[1], function_arg_type)
|
@@ -157,8 +157,9 @@ module TypedRb
|
|
157
157
|
# TODO:
|
158
158
|
# A TyObject(Symbol) should be returned not the function type
|
159
159
|
# x = def id(x); x; end / => x == :id
|
160
|
-
error_message = "
|
161
|
-
|
160
|
+
error_message = ". Wrong return type, expected #{function_type.to}, found #{body_return_type}."
|
161
|
+
body_return_type.compatible?(function_type_to, :lt)
|
162
|
+
fail Types::UncomparableTypes.new(function_type.to, body_return_type, node, error_message)
|
162
163
|
end
|
163
164
|
end
|
164
165
|
end
|
data/lib/typed/prelude.rb
CHANGED
Binary file
|
Binary file
|
data/lib/typed/runtime.rb
CHANGED
@@ -44,8 +44,6 @@ module TypedRb
|
|
44
44
|
def compatible?(type, relation = :lt)
|
45
45
|
if @bound
|
46
46
|
@bound.compatible?(type, relation)
|
47
|
-
elsif incompatible_vars?(type)
|
48
|
-
false
|
49
47
|
else
|
50
48
|
add_constraint(relation, type)
|
51
49
|
true
|
@@ -117,21 +115,6 @@ module TypedRb
|
|
117
115
|
def bound_to_generic?
|
118
116
|
bound && bound.respond_to?(:generic?) && bound.generic?
|
119
117
|
end
|
120
|
-
|
121
|
-
private
|
122
|
-
|
123
|
-
def incompatible_vars?(type)
|
124
|
-
if type.is_a?(TypeVariable)
|
125
|
-
left_var = bound || self
|
126
|
-
right_var = type.bound || type
|
127
|
-
|
128
|
-
left_var.is_a?(TypeVariable) &&
|
129
|
-
right_var.is_a?(TypeVariable) &&
|
130
|
-
left_var.variable != right_var.variable &&
|
131
|
-
(TypingContext.bound_generic_type_var?(left_var) &&
|
132
|
-
TypingContext.bound_generic_type_var?(right_var))
|
133
|
-
end
|
134
|
-
end
|
135
118
|
end
|
136
119
|
end
|
137
120
|
end
|
@@ -161,10 +161,13 @@ module TypedRb
|
|
161
161
|
var_constraints = @constraints[variable_name] || []
|
162
162
|
var_constraints << [relation_type, type]
|
163
163
|
@constraints[variable_name] = var_constraints
|
164
|
-
elsif parent
|
164
|
+
elsif parent.include?(variable_name)
|
165
165
|
parent.add_constraint(variable_name, relation_type, type)
|
166
166
|
else
|
167
|
-
|
167
|
+
key = [:local, nil, variable_name]
|
168
|
+
type_variables_register[key] = TypeVariable.new(variable_name, gen_name: false)
|
169
|
+
@constraints[variable_name] = [relation_type, type]
|
170
|
+
# fail StandardError, "Cannot find variable #{variable_name} to add a constraint"
|
168
171
|
end
|
169
172
|
end
|
170
173
|
|
@@ -47,7 +47,7 @@ module TypedRb
|
|
47
47
|
begin
|
48
48
|
value_l <= value_r ? value_l : fail(UnificationError, error_message)
|
49
49
|
rescue ArgumentError
|
50
|
-
raise(Types::UncomparableTypes.new(value_l, value_r))
|
50
|
+
raise(Types::UncomparableTypes.new(value_l, value_r, nil, ", #{value_l} is not a subtype of #{value_r}"))
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -7,6 +7,9 @@ module TypedRb
|
|
7
7
|
|
8
8
|
def initialize(ruby_type, node = nil)
|
9
9
|
super(ruby_type, node)
|
10
|
+
@hierarchy = [ruby_type] + Module.ancestors
|
11
|
+
@classes = @hierarchy.select { |klass| klass.instance_of?(Class) }
|
12
|
+
@modules = @hierarchy.select { |klass| klass.instance_of?(Module) }
|
10
13
|
end
|
11
14
|
|
12
15
|
def check_inclusion(self_type)
|
@@ -2,16 +2,16 @@ module TypedRb
|
|
2
2
|
module Types
|
3
3
|
class UncomparableTypes < TypeCheckError
|
4
4
|
attr_reader :from, :to
|
5
|
-
def initialize(from, to, node = nil)
|
5
|
+
def initialize(from, to, node = nil, msg='')
|
6
6
|
nodes = [from.node, to.node].compact
|
7
7
|
if node
|
8
|
-
super("Cannot compare types #{from} <=> #{to}", node)
|
8
|
+
super("Cannot compare types #{from} <=> #{to}#{msg}", node)
|
9
9
|
elsif nodes.size == 2
|
10
|
-
super("Cannot compare types #{from} <=> #{to}", nodes)
|
10
|
+
super("Cannot compare types #{from} <=> #{to}#{msg}", nodes)
|
11
11
|
elsif nodes.size == 1
|
12
|
-
super("Cannot compare types #{from} <=> #{to}", nodes.first)
|
12
|
+
super("Cannot compare types #{from} <=> #{to}#{msg}", nodes.first)
|
13
13
|
else
|
14
|
-
super("Cannot compare types #{from} <=> #{to}", nil)
|
14
|
+
super("Cannot compare types #{from} <=> #{to}#{msg}", nil)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
data/lib/typed/version.rb
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
ts 'type Monoid[T]'
|
2
|
+
module Monoid
|
3
|
+
ts '#mappend / [T] -> [T] -> [T]'
|
4
|
+
abstract(:mappend)
|
5
|
+
|
6
|
+
ts '#mempty / -> [T]'
|
7
|
+
abstract(:mempty)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
ts 'type Sum[String] super Monoid[T]'
|
12
|
+
class Sum
|
13
|
+
|
14
|
+
include Monoid
|
15
|
+
|
16
|
+
def mappend(a,b)
|
17
|
+
a + b
|
18
|
+
end
|
19
|
+
|
20
|
+
def mempty
|
21
|
+
0
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
ts '#sum / Array[Integer] -> Integer'
|
28
|
+
def sum(xs)
|
29
|
+
monoid = Sum.new
|
30
|
+
zero = monoid.mempty
|
31
|
+
xs.reduce(zero) { |a,b| monoid.mappend(a,b) }
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
ts '#moncat[T] / Array[T] -> Monoid[T] -> [T]'
|
36
|
+
def moncat(xs, m)
|
37
|
+
zero = m.mempty
|
38
|
+
xs.reduce(zero) { |a,b| m.mappend(a,b) }
|
39
|
+
end
|
40
|
+
|
41
|
+
->() {
|
42
|
+
moncat([1,2,3], Sum.new)
|
43
|
+
}
|
44
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
ts 'type Monoid[T]'
|
2
|
+
module Monoid
|
3
|
+
ts '#mappend / [T] -> [T] -> [T]'
|
4
|
+
abstract(:mappend)
|
5
|
+
|
6
|
+
ts '#mempty / -> [T]'
|
7
|
+
abstract(:mempty)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
ts 'type Sum[Integer] super Monoid[T]'
|
12
|
+
class Sum
|
13
|
+
|
14
|
+
include Monoid
|
15
|
+
|
16
|
+
def mappend(a,b)
|
17
|
+
a + b
|
18
|
+
end
|
19
|
+
|
20
|
+
def mempty
|
21
|
+
'0'
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
ts '#sum / Array[Integer] -> Integer'
|
28
|
+
def sum(xs)
|
29
|
+
monoid = Sum.new
|
30
|
+
zero = monoid.mempty
|
31
|
+
xs.reduce(zero) { |a,b| monoid.mappend(a,b) }
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
ts '#moncat[T] / Array[T] -> Monoid[T] -> [T]'
|
36
|
+
def moncat(xs, m)
|
37
|
+
zero = m.mempty
|
38
|
+
xs.reduce(zero) { |a,b| m.mappend(a,b) }
|
39
|
+
end
|
40
|
+
|
41
|
+
->() {
|
42
|
+
moncat([1,2,3], Sum.new)
|
43
|
+
}
|
44
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
ts 'type Monoid[T]'
|
2
|
+
module Monoid
|
3
|
+
ts '#mappend / [T] -> [T] -> [T]'
|
4
|
+
abstract(:mappend)
|
5
|
+
|
6
|
+
ts '#mempty / -> [T]'
|
7
|
+
abstract(:mempty)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
ts 'type Sum[Integer] super Monoid[T]'
|
12
|
+
class Sum
|
13
|
+
|
14
|
+
include Monoid
|
15
|
+
|
16
|
+
def mappend(a,b)
|
17
|
+
a + b
|
18
|
+
end
|
19
|
+
|
20
|
+
def mempty
|
21
|
+
0
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
ts '#sum / Array[String] -> Integer'
|
28
|
+
def sum(xs)
|
29
|
+
monoid = Sum.new
|
30
|
+
zero = monoid.mempty
|
31
|
+
xs.reduce(zero) { |a,b| monoid.mappend(a,b) }
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
ts '#moncat[T] / Array[T] -> Monoid[T] -> [T]'
|
36
|
+
def moncat(xs, m)
|
37
|
+
zero = m.mempty
|
38
|
+
xs.reduce(zero) { |a,b| m.mappend(a,b) }
|
39
|
+
end
|
40
|
+
|
41
|
+
->() {
|
42
|
+
moncat([1,2,3], Sum.new)
|
43
|
+
}
|
44
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
ts 'type Monoid[T]'
|
2
|
+
module Monoid
|
3
|
+
ts '#mappend / [T] -> [T] -> [T]'
|
4
|
+
abstract(:mappend)
|
5
|
+
|
6
|
+
ts '#mempty / -> [T]'
|
7
|
+
abstract(:mempty)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
ts 'type Sum[Integer] super Monoid[T]'
|
12
|
+
class Sum
|
13
|
+
|
14
|
+
include Monoid
|
15
|
+
|
16
|
+
def mappend(a,b)
|
17
|
+
a + b
|
18
|
+
end
|
19
|
+
|
20
|
+
def mempty
|
21
|
+
0
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
ts '#sum / Array[Integer] -> Integer'
|
28
|
+
def sum(xs)
|
29
|
+
monoid = Sum.new
|
30
|
+
zero = monoid.mempty
|
31
|
+
xs.reduce(zero) { |a,b| monoid.mappend(a,b) }
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
ts '#moncat[T] / Array[T] -> Monoid[T] -> [T]'
|
36
|
+
def moncat(xs, m)
|
37
|
+
zero = m.mempty
|
38
|
+
xs.reduce(zero) { |a,b| m.mappend(a,b) }
|
39
|
+
end
|
40
|
+
|
41
|
+
->() {
|
42
|
+
moncat([1,'2',3], Sum.new)
|
43
|
+
}
|
44
|
+
|
data/spec/lib/language_spec.rb
CHANGED
@@ -50,7 +50,7 @@ describe TypedRb::Language do
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
context 'with monoid
|
53
|
+
context 'with monoid example' do
|
54
54
|
let(:example) { 'monoid.rb' }
|
55
55
|
|
56
56
|
it 'should be possible to type check the example correctly' do
|
@@ -61,4 +61,52 @@ describe TypedRb::Language do
|
|
61
61
|
}.not_to raise_error
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
65
|
+
context 'with monoid error example, inconsistent type annotation' do
|
66
|
+
let(:example) { 'monoid/monoid_error1.rb' }
|
67
|
+
|
68
|
+
it 'should be possible to type check the example correctly' do
|
69
|
+
expect {
|
70
|
+
silence_stream(STDOUT) do
|
71
|
+
language.check_file(file, true)
|
72
|
+
end
|
73
|
+
}.to raise_error(TypedRb::Types::UncomparableTypes)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with monoid error example, inconsistent type annotation' do
|
78
|
+
let(:example) { 'monoid/monoid_error2.rb' }
|
79
|
+
|
80
|
+
it 'should be possible to type check the example correctly' do
|
81
|
+
expect {
|
82
|
+
silence_stream(STDOUT) do
|
83
|
+
language.check_file(file, true)
|
84
|
+
end
|
85
|
+
}.to raise_error(TypedRb::Types::UncomparableTypes)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'with monoid error example, inconsistent type annotation' do
|
90
|
+
let(:example) { 'monoid/monoid_error3.rb' }
|
91
|
+
|
92
|
+
it 'should be possible to type check the example correctly' do
|
93
|
+
expect {
|
94
|
+
silence_stream(STDOUT) do
|
95
|
+
language.check_file(file, true)
|
96
|
+
end
|
97
|
+
}.to raise_error(TypedRb::Types::UncomparableTypes)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'with monoid error example, inconsistent type annotation' do
|
102
|
+
let(:example) { 'monoid/monoid_error4.rb' }
|
103
|
+
|
104
|
+
it 'should be possible to type check the example correctly' do
|
105
|
+
expect {
|
106
|
+
silence_stream(STDOUT) do
|
107
|
+
language.check_file(file, true)
|
108
|
+
end
|
109
|
+
}.to raise_error(TypedRb::Types::Polymorphism::UnificationError)
|
110
|
+
end
|
111
|
+
end
|
64
112
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe TypedRb do
|
4
|
+
|
5
|
+
describe '#log_dynamic_warning' do
|
6
|
+
it 'stores a warning if the :dynamic_warnings flag is enabled' do
|
7
|
+
TypedRb.options = {:dynamic_warnings => true }
|
8
|
+
TypedRb.dynamic_warnings.clear
|
9
|
+
TypedRb.log_dynamic_warning(nil, nil, nil)
|
10
|
+
expect(TypedRb.dynamic_warnings.count).to eq(1)
|
11
|
+
|
12
|
+
TypedRb.options = {}
|
13
|
+
TypedRb.dynamic_warnings.clear
|
14
|
+
TypedRb.log_dynamic_warning(nil, nil, nil)
|
15
|
+
expect(TypedRb.dynamic_warnings.count).to eq(0)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require_relative '../../spec_helper'
|
2
2
|
|
3
|
-
describe 'generics use
|
3
|
+
describe 'generics use case
|
4
|
+
when
|
5
|
+
|
6
|
+
end' do
|
4
7
|
let(:language) { TypedRb::Language.new }
|
5
8
|
|
6
9
|
it 'applies arguments to generic types' do
|
@@ -108,8 +111,7 @@ __END
|
|
108
111
|
|
109
112
|
expect do
|
110
113
|
language.check(code)
|
111
|
-
end.to raise_error(TypedRb::TypeCheckError
|
112
|
-
/TestGen2\:T\:\:\[\?,\?\] expected, TestGen2\:U\:\:\[\?,\?\]/)
|
114
|
+
end.to raise_error(TypedRb::TypeCheckError)
|
113
115
|
end
|
114
116
|
|
115
117
|
it 'type-checks correctly super type generics' do
|
@@ -37,7 +37,7 @@ __CODE
|
|
37
37
|
expect {
|
38
38
|
language.check(code)
|
39
39
|
}.to raise_error(TypedRb::Types::UncomparableTypes,
|
40
|
-
|
40
|
+
/Cannot compare types Integer <=> String/)
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'includes a module referencing instance variables in a class' do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typed.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Antonio Garrote
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -148,6 +148,10 @@ files:
|
|
148
148
|
- spec/lib/examples/counter.rb
|
149
149
|
- spec/lib/examples/if.rb
|
150
150
|
- spec/lib/examples/monoid.rb
|
151
|
+
- spec/lib/examples/monoid/monoid_error1.rb
|
152
|
+
- spec/lib/examples/monoid/monoid_error2.rb
|
153
|
+
- spec/lib/examples/monoid/monoid_error3.rb
|
154
|
+
- spec/lib/examples/monoid/monoid_error4.rb
|
151
155
|
- spec/lib/language_spec.rb
|
152
156
|
- spec/lib/model/tm_abs_spec.rb
|
153
157
|
- spec/lib/model/tm_array_literal_spec.rb
|
@@ -183,6 +187,7 @@ files:
|
|
183
187
|
- spec/lib/runtime/validations_spec.rb
|
184
188
|
- spec/lib/runtime_spec.rb
|
185
189
|
- spec/lib/type_signature/parser_spec.rb
|
190
|
+
- spec/lib/typed_spec.rb
|
186
191
|
- spec/lib/types/comparisons_spec.rb
|
187
192
|
- spec/lib/types/polymorphism/generic_comparisons_spec.rb
|
188
193
|
- spec/lib/types/polymorphism/type_variable_register_spec.rb
|