Skip to content

Commit 370c895

Browse files
authored
Merge pull request #63 from esl/update-typed-gen-server-demo
Update TypedServer demo
2 parents 68ab657 + bedf719 commit 370c895

File tree

9 files changed

+123
-74
lines changed

9 files changed

+123
-74
lines changed
Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
defmodule TypedGenServer.Stage1.Server do
2-
use GenServer
3-
use GradualizerEx.TypeAnnotation
2+
# use GenServer
3+
use Gradient.TypeAnnotation
44

55
## Start IEx with:
66
## iex -S mix run --no-start
77
##
8+
## Start Gradient:
9+
## Application.ensure_all_started(:gradient)
10+
##
811
## Then use the following to recheck the file on any change:
9-
## recompile(); GradualizerEx.type_check_file(:code.which( TypedGenServer.Stage1.Server ), [:infer])
12+
## recompile(); Gradient.type_check_file(:code.which( TypedGenServer.Stage1.Server ), [:infer])
1013

1114
## Try switching between the definitions and see what happens
1215
@type message :: Contract.Echo.req() | Contract.Hello.req()
13-
#@type message :: Contract.Echo.req()
14-
#@type message :: {:echo_req, String.t()} | {:hello, String.t()}
16+
# @type message :: Contract.Echo.req()
17+
# @type message :: {:echo_req, String.t()} | {:hello, String.t()}
1518

1619
@type state :: map()
1720

@@ -20,19 +23,18 @@ defmodule TypedGenServer.Stage1.Server do
2023
end
2124

2225
@spec echo(pid(), String.t()) :: String.t()
23-
# @spec echo(pid(), String.t()) :: {:echo_req, String.t()}
2426
def echo(pid, message) do
25-
case annotate_type( GenServer.call(pid, {:echo_req, message}), Contract.Echo.res() ) do
26-
#case call_echo(pid, message) do
27+
case annotate_type(GenServer.call(pid, {:echo_req, message}), Contract.Echo.res()) do
28+
# case call_echo(pid, message) do
2729
## Try changing the pattern or the returned response
2830
{:echo_res, response} -> response
2931
end
3032
end
3133

32-
#@spec call_echo(pid(), String.t()) :: Contract.Echo.res()
33-
#defp call_echo(pid, message) do
34+
# @spec call_echo(pid(), String.t()) :: Contract.Echo.res()
35+
# defp call_echo(pid, message) do
3436
# GenServer.call(pid, {:echo_req, message})
35-
#end
37+
# end
3638

3739
@spec hello(pid(), String.t()) :: :ok
3840
def hello(pid, name) do
@@ -41,27 +43,25 @@ defmodule TypedGenServer.Stage1.Server do
4143
end
4244
end
4345

44-
@impl true
46+
# @impl true
4547
def init(state) do
4648
{:ok, state}
4749
end
4850

49-
@impl true
50-
def handle_call(m, from, state) do
51-
{:noreply, handle(m, from, state)}
52-
end
51+
@type called(a) :: {:noreply, state()}
52+
| {:reply, a, state()}
5353

54-
@spec handle(message(), any, any) :: state()
54+
# @impl true
55+
@spec handle_call(message(), GenServer.from(), state())
56+
:: called(Contract.Echo.res() | Contract.Hello.res())
5557
## Try breaking the pattern match, e.g. by changing 'echo_req'
56-
def handle({:echo_req, payload}, from, state) do
57-
GenServer.reply(from, {:echo_res, payload})
58-
state
58+
def handle_call({:echo_req, payload}, _from, state) do
59+
{:reply, {:echo_res, payload}, state}
5960
end
6061

6162
## Try commenting out the following clause
62-
def handle({:hello, name}, from, state) do
63+
def handle_call({:hello, name}, _from, state) do
6364
IO.puts("Hello, #{name}!")
64-
GenServer.reply(from, :ok)
65-
state
65+
{:reply, :ok, state}
6666
end
6767
end

examples/typed_gen_server/lib/typed_gen_server/stage2.ex

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
defmodule TypedGenServer.Stage2.Server do
2-
use GenServer
3-
use GradualizerEx.TypeAnnotation
2+
# use GenServer
3+
use Gradient.TypeAnnotation
44
alias Stage2.TypedServer
55

66
## Start IEx with:
77
## iex -S mix run --no-start
88
##
9+
## Start Gradient:
10+
## Application.ensure_all_started(:gradient)
11+
##
912
## Then use the following to recheck the file on any change:
10-
## recompile(); GradualizerEx.type_check_file(:code.which( TypedGenServer.Stage2.Server ), [:infer])
13+
## recompile(); Gradient.type_check_file(:code.which( TypedGenServer.Stage2.Server ), [:infer])
1114

1215
@opaque t :: pid()
1316

1417
## Try switching between the definitions and see what happens
1518
@type message :: Contract.Echo.req() | Contract.Hello.req()
16-
#@type message :: Contract.Echo.req()
17-
#@type message :: {:echo_req, String.t()} | {:hello, String.t()}
19+
# @type message :: Contract.Echo.req()
20+
# @type message :: {:echo_req, String.t()} | {:hello, String.t()}
1821

1922
@type state :: map()
2023

@@ -24,10 +27,9 @@ defmodule TypedGenServer.Stage2.Server do
2427
end
2528

2629
@spec echo(t(), String.t()) :: String.t()
27-
# @spec echo(t(), String.t()) :: {:echo_req, String.t()}
2830
def echo(pid, message) do
29-
case annotate_type( GenServer.call(pid, {:echo_req, message}), Contract.Echo.res() ) do
30-
#case call_echo(pid, message) do
31+
case annotate_type(GenServer.call(pid, {:echo_req, message}), Contract.Echo.res()) do
32+
# case call_echo(pid, message) do
3133
## Try changing the pattern or the returned response
3234
{:echo_res, response} -> response
3335
end
@@ -46,12 +48,12 @@ defmodule TypedGenServer.Stage2.Server do
4648
end
4749
end
4850

49-
@impl true
51+
# @impl true
5052
def init(state) do
5153
{:ok, state}
5254
end
5355

54-
@impl true
56+
# @impl true
5557
def handle_call(m, from, state) do
5658
{:noreply, handle(m, from, state)}
5759
end
@@ -62,8 +64,8 @@ defmodule TypedGenServer.Stage2.Server do
6264
## This could register {:echo_req, payload} <-> {:echo_res, payload} mapping
6365
## and response type at compile time to generate call_echo() automatically.
6466
## Thanks Robert!
65-
#TypedServer.reply( from, {:echo_res, payload}, Contract.Echo.res() )
66-
GenServer.reply( from, {:echo_res, payload} )
67+
# TypedServer.reply( from, {:echo_res, payload}, Contract.Echo.res() )
68+
GenServer.reply(from, {:echo_res, payload})
6769
state
6870
end
6971

@@ -78,15 +80,26 @@ end
7880
defmodule Test.TypedGenServer.Stage2.Server do
7981
alias TypedGenServer.Stage2.Server
8082

83+
## Run with:
84+
## recompile(); Test.TypedGenServer.Stage2.Server.test()
85+
##
8186
## Typecheck with:
82-
## recompile(); GradualizerEx.type_check_file(:code.which( Test.TypedGenServer.Stage2.Server ), [:infer])
87+
## recompile(); Gradient.type_check_file(:code.which( Test.TypedGenServer.Stage2.Server ), [:infer])
88+
## recompile(); Gradient.type_check_file(:code.which( Test.TypedGenServer.Stage2.Server ), [:infer, ex_check: false])
8389

8490
@spec test :: any()
8591
def test do
8692
{:ok, srv} = Server.start_link()
87-
pid = self()
93+
94+
pid =
95+
spawn(fn ->
96+
receive do
97+
:unlikely -> :ok
98+
end
99+
end)
100+
88101
"payload" = Server.echo(srv, "payload")
89102
## This won't typecheck, since Server.echo only accepts Server.t(), that is our Server pids
90-
#"payload" = Server.echo(pid, "payload")
103+
# "payload" = Server.echo(pid, "payload")
91104
end
92105
end

examples/typed_gen_server/lib/typed_gen_server/stage3.ex

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
defmodule Stage3.TypedServer do
2-
32
## This doesn't play well with:
43
## {:ok, srv} = MultiServer.start_link()
54
## Due to:
@@ -18,21 +17,21 @@ end
1817

1918
defmodule TypedGenServer.Stage3.Server do
2019
use GenServer
21-
use GradualizerEx.TypeAnnotation
20+
use Gradient.TypeAnnotation
2221
alias Stage3.TypedServer
2322

2423
## Start IEx with:
2524
## iex -S mix run --no-start
2625
##
2726
## Then use the following to recheck the file on any change:
28-
## recompile(); GradualizerEx.type_check_file(:code.which( TypedGenServer.Stage3.Server ), [:infer])
27+
## recompile(); Gradient.type_check_file(:code.which( TypedGenServer.Stage3.Server ), [:infer])
2928

3029
@opaque t :: {__MODULE__, pid()}
3130

3231
## Try switching between the definitions and see what happens
3332
@type message :: Contract.Echo.req() | Contract.Hello.req()
34-
#@type message :: Contract.Echo.req()
35-
#@type message :: {:echo_req, String.t()} | {:hello, String.t()}
33+
# @type message :: Contract.Echo.req()
34+
# @type message :: {:echo_req, String.t()} | {:hello, String.t()}
3635

3736
@type state :: map()
3837

@@ -44,17 +43,17 @@ defmodule TypedGenServer.Stage3.Server do
4443
@spec echo(t(), String.t()) :: String.t()
4544
# @spec echo(t(), String.t()) :: {:echo_req, String.t()}
4645
def echo(_server = {__MODULE__, _pid}, message) do
47-
case annotate_type( GenServer.call(_pid, {:echo_req, message}), Contract.Echo.res() ) do
48-
#case call_echo(_server, message) do
46+
case annotate_type(GenServer.call(_pid, {:echo_req, message}), Contract.Echo.res()) do
47+
# case call_echo(_server, message) do
4948
## Try changing the pattern or the returned response
5049
{:echo_res, response} -> response
5150
end
5251
end
5352

54-
#@spec call_echo(t(), String.t()) :: Contract.Echo.res()
55-
#defp call_echo({__MODULE__, pid}, message) do
53+
# @spec call_echo(t(), String.t()) :: Contract.Echo.res()
54+
# defp call_echo({__MODULE__, pid}, message) do
5655
# GenServer.call(pid, {:echo_req, message})
57-
#end
56+
# end
5857

5958
@spec hello(t(), String.t()) :: :ok
6059
def hello({__MODULE__, pid}, name) do
@@ -92,14 +91,14 @@ defmodule Test.TypedGenServer.Stage3.Server do
9291
alias TypedGenServer.Stage3.Server
9392

9493
## Typecheck with:
95-
## recompile(); GradualizerEx.type_check_file(:code.which( Test.TypedGenServer.Stage3.Server ), [:infer])
94+
## recompile(); Gradient.type_check_file(:code.which( Test.TypedGenServer.Stage3.Server ), [:infer])
9695

9796
@spec test :: any()
9897
def test do
9998
{:ok, srv} = Server.start_link()
10099
pid = self()
101100
"payload" = Server.echo(srv, "payload")
102101
## This won't typecheck, since Server.echo only accepts Server.t(), that is Server pids
103-
#"payload" = Server.echo(pid, "payload")
102+
# "payload" = Server.echo(pid, "payload")
104103
end
105104
end

examples/typed_gen_server/lib/typed_gen_server/stage4.ex

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
defmodule TypedGenServer.Stage4.Server do
2-
use GenServer
2+
# use GenServer
33
use Gradient.TypeAnnotation
44
use Gradient.TypedServer
55
alias Gradient.TypedServer
66

77
## Start IEx with:
88
## iex -S mix run --no-start
99
##
10+
## Start Gradient:
11+
## Application.ensure_all_started(:gradient)
12+
##
1013
## Then use the following to recheck the file on any change:
1114
## recompile(); Gradient.type_check_file(:code.which( TypedGenServer.Stage4.Server ), [:infer])
15+
## recompile(); Gradient.type_check_file(:code.which( TypedGenServer.Stage4.Server ), [:infer, ex_check: false])
1216

1317
@opaque t :: pid()
1418

@@ -27,7 +31,7 @@ defmodule TypedGenServer.Stage4.Server do
2731
@spec echo(t(), String.t()) :: String.t()
2832
# @spec echo(t(), String.t()) :: {:echo_req, String.t()}
2933
def echo(pid, message) do
30-
#case annotate_type(GenServer.call(pid, {:echo_req, message}), Contract.Echo.res()) do
34+
# case annotate_type(GenServer.call(pid, {:echo_req, message}), Contract.Echo.res()) do
3135
case call_echo_req(pid, message) do
3236
## Try changing the pattern or the returned response
3337
{:echo_res, response} -> response
@@ -38,22 +42,22 @@ defmodule TypedGenServer.Stage4.Server do
3842
## thanks to using TypedServer.reply/3 instead of GenServer.reply/2.
3943
## We don't have to define it!
4044
## TODO: use the correct type instead of any as the second param!
41-
#@spec call_echo_req(t(), any) :: Contract.Echo.res()
42-
#defp call_echo_req(pid, message) do
45+
# @spec call_echo_req(t(), any) :: Contract.Echo.res()
46+
# defp call_echo_req(pid, message) do
4347
# GenServer.call(pid, {:echo_req, message})
44-
#end
48+
# end
4549

4650
@spec hello(t(), String.t()) :: :ok
4751
def hello(pid, name) do
4852
GenServer.call(pid, {:hello, name})
4953
end
5054

51-
@impl true
55+
# @impl true
5256
def init(state) do
5357
{:ok, state}
5458
end
5559

56-
@impl true
60+
# @impl true
5761
def handle_call(m, from, state) do
5862
{:noreply, handle(m, from, state)}
5963
end
@@ -64,11 +68,11 @@ defmodule TypedGenServer.Stage4.Server do
6468
## TypedServer.reply/3 registers a {:echo_req, payload} <-> Contract.Echo.res() mapping
6569
## and generates call_echo_req() at compile time.
6670
## Thanks for the idea, @rvirding!
67-
TypedServer.reply( from, {:echo_res, payload}, Contract.Echo.res() )
71+
TypedServer.reply(from, {:echo_res, payload}, Contract.Echo.res())
6872
## This will not typecheck - awesome!
69-
#TypedServer.reply( from, {:invalid_tag, payload}, Contract.Echo.res() )
73+
# TypedServer.reply( from, {:invalid_tag, payload}, Contract.Echo.res() )
7074
## And this is the well known untyped equivalent.
71-
#GenServer.reply(from, {:echo_res, payload})
75+
# GenServer.reply(from, {:echo_res, payload})
7276
state
7377
end
7478

@@ -85,13 +89,21 @@ defmodule Test.TypedGenServer.Stage4.Server do
8589

8690
## Typecheck with:
8791
## recompile(); Gradient.type_check_file(:code.which( Test.TypedGenServer.Stage4.Server ), [:infer])
92+
## recompile(); Gradient.type_check_file(:code.which( Test.TypedGenServer.Stage4.Server ), [:infer, ex_check: false])
8893

8994
@spec test :: any()
9095
def test do
9196
{:ok, srv} = Server.start_link()
92-
pid = self()
97+
98+
pid =
99+
spawn(fn ->
100+
receive do
101+
:unlikely -> :ok
102+
end
103+
end)
104+
93105
"payload" = Server.echo(srv, "payload")
94106
## This won't typecheck, since Server.echo only accepts Server.t(), that is our Server pids
95-
#"payload" = Server.echo(pid, "payload")
107+
# "payload" = Server.echo(pid, "payload")
96108
end
97109
end

examples/typed_gen_server/mix.exs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,14 @@ defmodule TypedGenServer.MixProject do
2626
{:gradient, path: "../../"},
2727
{:dialyxir, "~> 1.1", only: [:dev, :test], runtime: false},
2828
{:gradualizer,
29-
github: "erszcz/Gradualizer", ref: "typed-gen-server", manager: :rebar3, override: true},
30-
{:gradualizer_ex, github: "erszcz/gradualizer-ex", branch: "rs/wip"}
29+
github: "erszcz/Gradualizer", ref: "typed-gen-server", manager: :rebar3, override: true}
3130
]
3231
end
3332

3433
defp dialyzer do
3534
[
3635
plt_add_deps: :app_tree,
37-
#ignore_warnings: "dialyzer.ignore-warnings",
36+
# ignore_warnings: "dialyzer.ignore-warnings",
3837
flags: ~w(
3938
error_handling
4039
race_conditions
@@ -43,5 +42,4 @@ defmodule TypedGenServer.MixProject do
4342
)a
4443
]
4544
end
46-
4745
end

examples/typed_gen_server/mix.lock

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
%{
22
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
33
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
4-
"gradualizer": {:git, "https://github.com/erszcz/Gradualizer.git", "bd54184dae19d5117534c6e6885fa5abd1fe5da4", [ref: "typed-gen-server"]},
5-
"gradualizer_ex": {:git, "https://github.com/erszcz/gradualizer-ex.git", "dc64f484c83a5ce286696fb79756577a1b9853f5", [branch: "rs/wip"]},
4+
"gradualizer": {:git, "https://github.com/erszcz/Gradualizer.git", "764ddc6b4cc007008ea68eb9ec7a3f3adc2f1951", [ref: "typed-gen-server"]},
65
}

0 commit comments

Comments
 (0)