Skip to content

Commit b3e0765

Browse files
author
José Valim
committed
Range is no longer a record
The goal is to simplify and allow us to extend ranges in the future. Also, incentivate people to use `first .. last` in pattern matching instead of `Range[first: first, last: last]`.
1 parent cad7551 commit b3e0765

File tree

5 files changed

+85
-60
lines changed

5 files changed

+85
-60
lines changed

lib/elixir/lib/range.ex

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
1-
defrecord Range, [:first, :last] do
1+
defmodule Range do
22
@moduledoc """
33
Defines a Range.
44
"""
5+
6+
@type t :: { Range, any, any }
7+
@type t(first, last) :: { Range, first, last }
8+
9+
@doc """
10+
Creates a new range.
11+
"""
12+
def new(first, last) do
13+
{ Range, first, last }
14+
end
15+
16+
@doc """
17+
Returns the first item of the range.
18+
"""
19+
def first({ Range, first, _ }) do
20+
first
21+
end
22+
23+
@doc """
24+
Returns the last item of the range.
25+
"""
26+
def last({ Range, _, last }) do
27+
last
28+
end
529
end
630

731
defprotocol Range.Iterator do
@@ -55,15 +79,15 @@ defimpl Enumerable, for: Range do
5579
end
5680

5781
defimpl Range.Iterator, for: Integer do
58-
def next(first, Range[last: last]) when is_integer(last) do
82+
def next(first, _ .. last) when is_integer(last) do
5983
if last >= first do
6084
&(&1 + 1)
6185
else
6286
&(&1 - 1)
6387
end
6488
end
6589

66-
def count(first, Range[last: last]) when is_integer(last) do
90+
def count(first, _ .. last) when is_integer(last) do
6791
if last >= first do
6892
last - first + 1
6993
else
@@ -75,7 +99,7 @@ end
7599
defimpl Inspect, for: Range do
76100
import Inspect.Algebra
77101

78-
def inspect(Range[first: first, last: last], opts) do
102+
def inspect(first .. last, opts) do
79103
concat [to_doc(first, opts), "..", to_doc(last, opts)]
80104
end
81105
end

lib/elixir/test/elixir/kernel/record_rewriter_test.exs

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Code.require_file "../test_helper.exs", __DIR__
22

3-
defrecord BadRange, first: 0, last: 0 do
3+
defrecord Rec, first: 0, last: 0
4+
5+
defrecord BadRec, first: 0, last: 0 do
46
defoverridable [first: 1]
57

68
def first(_) do
@@ -67,11 +69,11 @@ defmodule Kernel.RecordRewriterTest do
6769
end
6870

6971
test "with tuple match" do
70-
clause = clause(fn({ x, y } = { Macro.Env[], Range[] }) -> :foo end)
71-
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Range], nil }
72+
clause = clause(fn({ x, y } = { Macro.Env[], Rec[] }) -> :foo end)
73+
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Rec], nil }
7274

73-
clause = clause(fn({ Macro.Env[], y } = { x, Range[] }) -> :foo end)
74-
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Range], nil }
75+
clause = clause(fn({ Macro.Env[], y } = { x, Rec[] }) -> :foo end)
76+
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Rec], nil }
7577
end
7678

7779
test "inside body" do
@@ -80,13 +82,13 @@ defmodule Kernel.RecordRewriterTest do
8082
end
8183

8284
test "inside body with variable overridden" do
83-
clause = clause(fn(x = Macro.Env[], y = Range[]) -> y = x end)
84-
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Range, "y@1": Macro.Env], { Macro.Env, nil } }
85+
clause = clause(fn(x = Macro.Env[], y = Rec[]) -> y = x end)
86+
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Rec, "y@1": Macro.Env], { Macro.Env, nil } }
8587
end
8688

8789
test "inside body with nested tuple" do
88-
clause = clause(fn(x = Range[]) -> ^x = Range[first: { :hello, 2 }] end)
89-
assert optimize_clause(clause) == { clause, [x: Range], { Range, [nil, { :hello, nil }, nil] } }
90+
clause = clause(fn(x = Rec[]) -> ^x = Rec[first: { :hello, 2 }] end)
91+
assert optimize_clause(clause) == { clause, [x: Rec], { Rec, [nil, { :hello, nil }, nil] } }
9092
end
9193

9294
test "with setelement" do
@@ -102,7 +104,7 @@ defmodule Kernel.RecordRewriterTest do
102104
end
103105

104106
test "conflicting definition" do
105-
clause = clause(fn(x = Macro.Env[]) -> ^x = Range[]; :foo end)
107+
clause = clause(fn(x = Macro.Env[]) -> ^x = Rec[]; :foo end)
106108
assert optimize_clause(clause) == { clause, [x: nil], nil }
107109
end
108110

@@ -132,13 +134,13 @@ defmodule Kernel.RecordRewriterTest do
132134
end
133135

134136
test "inside local call" do
135-
clause = clause(fn -> (x = Macro.Env[]).(y = Range[]) end)
136-
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Range], nil }
137+
clause = clause(fn -> (x = Macro.Env[]).(y = Rec[]) end)
138+
assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Rec], nil }
137139
end
138140

139141
test "inside remote call" do
140-
clause = clause(fn -> x.call(y = Range[]) end)
141-
assert optimize_clause(clause) == { clause, [y: Range], nil }
142+
clause = clause(fn -> x.call(y = Rec[]) end)
143+
assert optimize_clause(clause) == { clause, [y: Rec], nil }
142144
end
143145

144146
test "inside list comprehension" do
@@ -161,19 +163,19 @@ defmodule Kernel.RecordRewriterTest do
161163
clause = clause(fn -> case something do 1 -> x = Macro.Env[]; 2 -> x = Macro.Env[] end end)
162164
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
163165

164-
clause = clause(fn -> case something do 1 -> x = Macro.Env[]; 2 -> x = Range[] end end)
166+
clause = clause(fn -> case something do 1 -> x = Macro.Env[]; 2 -> x = Rec[] end end)
165167
assert optimize_clause(clause) == { clause, [x: nil], nil }
166168

167-
clause = clause(fn -> case something do x = Macro.Env[] -> x; x = Range[] -> x; _ -> :ok end end)
169+
clause = clause(fn -> case something do x = Macro.Env[] -> x; x = Rec[] -> x; _ -> :ok end end)
168170
assert optimize_clause(clause) == { clause, [], nil }
169171
end
170172

171173
test "inside case with nested tuple" do
172-
clause = clause(fn -> case something do x = Range[first: { :foo, 2 }] -> x = x; Range[] = x -> x = x end end)
173-
assert optimize_clause(clause) == { clause, ["x@4": Range], { Range, nil } }
174+
clause = clause(fn -> case something do x = Rec[first: { :foo, 2 }] -> x = x; Rec[] = x -> x = x end end)
175+
assert optimize_clause(clause) == { clause, ["x@4": Rec], { Rec, nil } }
174176

175-
clause = clause(fn -> case something do x = Range[first: { :foo, 2 }] -> x = x; Range[first: { :foo, 2 }] = x -> x = x end end)
176-
assert optimize_clause(clause) == { clause, ["x@4": Range], { Range, [nil, { :foo, nil }, nil] } }
177+
clause = clause(fn -> case something do x = Rec[first: { :foo, 2 }] -> x = x; Rec[first: { :foo, 2 }] = x -> x = x end end)
178+
assert optimize_clause(clause) == { clause, ["x@4": Rec], { Rec, [nil, { :foo, nil }, nil] } }
177179
end
178180

179181
test "empty receive" do
@@ -188,15 +190,15 @@ defmodule Kernel.RecordRewriterTest do
188190
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; 2 -> x = Macro.Env[] end end)
189191
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
190192

191-
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; 2 -> x = Range[] end end)
193+
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; 2 -> x = Rec[] end end)
192194
assert optimize_clause(clause) == { clause, [x: nil], nil }
193195
end
194196

195197
test "inside receive with after" do
196198
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; after 2 -> x = Macro.Env[]; end end)
197199
assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
198200

199-
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; after 2 -> x = Range[]; end end)
201+
clause = clause(fn -> receive do 1 -> x = Macro.Env[]; after 2 -> x = Rec[]; end end)
200202
assert optimize_clause(clause) == { clause, [x: nil], nil }
201203
end
202204

@@ -215,54 +217,54 @@ defmodule Kernel.RecordRewriterTest do
215217

216218
test "getter call is rewriten" do
217219
{ clause, rewriten } =
218-
{ clause(fn(x = Range[]) -> x.first end), clause(fn(x = Range[]) -> :erlang.element(2, x) end) }
220+
{ clause(fn(x = Rec[]) -> x.first end), clause(fn(x = Rec[]) -> :erlang.element(2, x) end) }
219221

220-
assert optimize_clause(clause) == { rewriten, [x: Range], nil }
222+
assert optimize_clause(clause) == { rewriten, [x: Rec], nil }
221223
end
222224

223225
test "direct getter call is rewriten" do
224226
{ clause, rewriten } =
225-
{ clause(fn() -> Range[].first end), clause(fn() -> :erlang.element(2, Range[]) end) }
227+
{ clause(fn() -> Rec[].first end), clause(fn() -> :erlang.element(2, Rec[]) end) }
226228

227229
assert optimize_clause(clause) == { rewriten, [], nil }
228230
end
229231

230232
test "setter call is rewriten" do
231233
{ clause, rewriten } =
232-
{ clause(fn(x = Range[]) -> x.first(:first) end), clause(fn(x = Range[]) -> :erlang.setelement(2, x, :first) end) }
234+
{ clause(fn(x = Rec[]) -> x.first(:first) end), clause(fn(x = Rec[]) -> :erlang.setelement(2, x, :first) end) }
233235

234-
assert optimize_clause(clause) == { rewriten, [x: Range], { Range, nil } }
236+
assert optimize_clause(clause) == { rewriten, [x: Rec], { Rec, nil } }
235237
end
236238

237239
test "nested setter call is rewriten" do
238240
{ clause, rewriten } =
239-
{ clause(fn(x = Range[]) -> x.first(:first).last(:last) end), clause(fn(x = Range[]) -> :erlang.setelement(3, :erlang.setelement(2, x, :first), :last) end) }
241+
{ clause(fn(x = Rec[]) -> x.first(:first).last(:last) end), clause(fn(x = Rec[]) -> :erlang.setelement(3, :erlang.setelement(2, x, :first), :last) end) }
240242

241-
assert optimize_clause(clause) == { rewriten, [x: Range], { Range, nil } }
243+
assert optimize_clause(clause) == { rewriten, [x: Rec], { Rec, nil } }
242244
end
243245

244246
test "updater call is rewriten" do
245247
{ clause, rewriten } =
246-
{ clause(fn(x = Range[]) -> x.update_first(&(&1 + 1)) end), clause(fn(x = Range[]) -> Range.update_first(&(&1 + 1), x) end) }
247-
assert optimize_clause(clause) == { rewriten, [x: Range], { Range, nil } }
248+
{ clause(fn(x = Rec[]) -> x.update_first(&(&1 + 1)) end), clause(fn(x = Rec[]) -> Rec.update_first(&(&1 + 1), x) end) }
249+
assert optimize_clause(clause) == { rewriten, [x: Rec], { Rec, nil } }
248250
end
249251

250252
test "update call is rewriten" do
251253
{ clause, rewriten } =
252-
{ clause(fn(x = Range[]) -> x.update(first: 1) end), clause(fn(x = Range[]) -> Range.update([first: 1], x) end) }
253-
assert optimize_clause(clause) == { rewriten, [x: Range], { Range, nil } }
254+
{ clause(fn(x = Rec[]) -> x.update(first: 1) end), clause(fn(x = Rec[]) -> Rec.update([first: 1], x) end) }
255+
assert optimize_clause(clause) == { rewriten, [x: Rec], { Rec, nil } }
254256
end
255257

256258
test "fallback for unknown fields" do
257259
{ clause, rewriten } =
258-
{ clause(fn(x = Range[]) -> x.unknown(1, 2) end), clause(fn(x = Range[]) -> Range.unknown(1, 2, x) end) }
259-
assert optimize_clause(clause) == { rewriten, [x: Range], nil }
260+
{ clause(fn(x = Rec[]) -> x.unknown(1, 2) end), clause(fn(x = Rec[]) -> Rec.unknown(1, 2, x) end) }
261+
assert optimize_clause(clause) == { rewriten, [x: Rec], nil }
260262
end
261263

262264
test "fallback for rewriten fields" do
263265
{ clause, rewriten } =
264-
{ clause(fn(x = BadRange[]) -> x.first end), clause(fn(x = BadRange[]) -> BadRange.first(x) end) }
265-
assert optimize_clause(clause) == { rewriten, [x: BadRange], nil }
266+
{ clause(fn(x = BadRec[]) -> x.first end), clause(fn(x = BadRec[]) -> BadRec.first(x) end) }
267+
assert optimize_clause(clause) == { rewriten, [x: BadRec], nil }
266268
end
267269

268270
test "noop for not records fields" do
@@ -271,7 +273,7 @@ defmodule Kernel.RecordRewriterTest do
271273
end
272274

273275
test "noop for conflicting inference" do
274-
clause = clause(fn(x = Macro.Env[]) -> ^x = Range[]; x.first end)
276+
clause = clause(fn(x = Macro.Env[]) -> ^x = Rec[]; x.first end)
275277
assert optimize_clause(clause) == { clause, [x: nil], nil }
276278
end
277279
end

lib/elixir/test/elixir/protocol_test.exs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ defmodule ProtocolTest do
6464
assert nil? Sample.impl_for(self)
6565
assert nil? Sample.impl_for(hd(:erlang.ports))
6666
assert nil? Sample.impl_for(make_ref)
67-
assert nil? Sample.impl_for(Range[])
67+
assert nil? Sample.impl_for(Macro.Env[])
6868

6969
assert Sample.impl_for(Foo[]) ==
7070
Sample.ProtocolTest.Foo
@@ -73,10 +73,10 @@ defmodule ProtocolTest do
7373
end
7474

7575
test :protocol_priority_does_not_override_records do
76-
assert WithAny.impl_for(Foo[]) == WithAny.ProtocolTest.Foo
77-
assert WithAny.impl_for(Range[]) == WithAny.Tuple
78-
assert WithAny.impl_for({ :foo }) == WithAny.Tuple
79-
assert WithAny.impl_for({}) == WithAny.Tuple
76+
assert WithAny.impl_for(Foo[]) == WithAny.ProtocolTest.Foo
77+
assert WithAny.impl_for(Macro.Env[]) == WithAny.Tuple
78+
assert WithAny.impl_for({ :foo }) == WithAny.Tuple
79+
assert WithAny.impl_for({}) == WithAny.Tuple
8080
end
8181

8282
test :protocol_with_fallback do
@@ -214,7 +214,7 @@ defmodule Protocol.ConsolidationTest do
214214
assert nil? Sample.impl_for(self)
215215
assert nil? Sample.impl_for(hd(:erlang.ports))
216216
assert nil? Sample.impl_for(make_ref)
217-
assert nil? Sample.impl_for(Range[])
217+
assert nil? Sample.impl_for(Macro.Env[])
218218

219219
assert Sample.impl_for(Foo[]) ==
220220
Sample.Protocol.ConsolidationTest.Foo
@@ -223,10 +223,10 @@ defmodule Protocol.ConsolidationTest do
223223
end
224224

225225
test :consolidated_impl_for_prioritized do
226-
assert WithAny.impl_for(Foo[]) == WithAny.Protocol.ConsolidationTest.Foo
227-
assert WithAny.impl_for(Range[]) == WithAny.Tuple
228-
assert WithAny.impl_for({ :foo }) == WithAny.Tuple
229-
assert WithAny.impl_for({}) == WithAny.Tuple
226+
assert WithAny.impl_for(Foo[]) == WithAny.Protocol.ConsolidationTest.Foo
227+
assert WithAny.impl_for(Macro.Env[]) == WithAny.Tuple
228+
assert WithAny.impl_for({ :foo }) == WithAny.Tuple
229+
assert WithAny.impl_for({}) == WithAny.Tuple
230230
end
231231

232232
test :consolidated_fallback do

lib/elixir/test/elixir/range_test.exs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ defmodule RangeTest do
99
end
1010

1111
test :first do
12-
assert Range.new(first: 1, last: 3).first == 1
12+
assert Range.first(1..3) == 1
1313
end
1414

1515
test :last do
16-
assert Range.new(first: 1, last: 3).last == 3
16+
assert Range.last(1..3) == 3
1717
end
1818

1919
test :op do
@@ -45,8 +45,6 @@ defmodule RangeTest do
4545
test :inspect do
4646
assert inspect(1..3) == "1..3"
4747
assert inspect(3..1) == "3..1"
48-
49-
# False positive
5048
assert inspect({ Range, nil }) == "{Range, nil}"
5149
end
5250

lib/elixir/test/elixir/typespec_test.exs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Code.require_file "test_helper.exs", __DIR__
33
defmodule Typespec.TypeTest do
44
use ExUnit.Case, async: true
55

6+
defrecord Rec, [:first, :last]
7+
68
# This macro allows us to focus on the result of the
79
# definition and not on the hassles of handling test
810
# module
@@ -188,8 +190,7 @@ defmodule Typespec.TypeTest do
188190
189191
test "@type with a union" do
190192
spec = test_module do
191-
@type mytype :: integer | char_list
192-
| atom
193+
@type mytype :: integer | char_list | atom
193194
end
194195
assert {:mytype, {:type, _, :union, [{:type, _, :integer, []},
195196
{:remote_type, _, [{:atom, _, :elixir}, {:atom, _, :char_list}, []]},
@@ -198,10 +199,10 @@ defmodule Typespec.TypeTest do
198199
199200
test "@type with an access macro" do
200201
spec = test_module do
201-
@type mytype :: Range[first: integer]
202+
@type mytype :: Rec[first: integer]
202203
end
203204
assert {:mytype, {:type, _, :tuple,
204-
[{:atom, _, Range}, {:type, _, :integer, []}, {:type, _, :any, []}]}, []} = spec
205+
[{:atom, _, Rec}, {:type, _, :integer, []}, {:type, _, :any, []}]}, []} = spec
205206
end
206207
207208
test "@type with keywords" do

0 commit comments

Comments
 (0)