@Include { myslides }
@OverheadTransparencies
@Title {
Validation using Erlang's type system
with Sheriff
}
@RunningTitle { sheriff }
@Author { Lo�c Hoguin }
@Institution { Dev:Extend }
@DateLine { Yes }
@InitialLanguage { English }
@PageOrientation { Landscape }
//
@Overhead
@Title { Good Erlang code }
@Begin
@CD @F @Box paint { lightyellow } { @Verbatim {
-type year() :: 1900..2011.
-type age() :: 0..111.
-spec main() -> no_return().
main() ->
Year = 1984,
Age = calculate_age(Year),
io:format("~b years old~n", [Age]),
main().
-spec calculate_age(year()) -> age().
calculate_age(Year) ->
2011 - Year.
} }
@End @Overhead
@Overhead
@Title { Dialyzer is awesome, isn't it? }
@Begin
@BulletList
@ListItem { Dialyzer statically checks function specifications }
@ListItem { All type errors will be reported by Dialyzer }
@ListItem { Okay, code isn't that good, it's just a dumb example }
@EndList
@End @Overhead
@Overhead
@Title { Good Erlang code receiving external data }
@Begin
@CD @F @Box paint { lightyellow } { @Verbatim {
-type year() :: 1900..2011.
-type age() :: 0..111.
-spec main() -> no_return().
main() ->
receive {year_of_birth, Year} ->
Age = calculate_age(Year),
io:format("~b years old~n", [Age])
end,
main().
-spec calculate_age(year()) -> age().
calculate_age(Year) ->
2011 - Year.
} }
@End @Overhead
@Overhead
@Title { We lost Dialyzer }
@Begin
@BulletList
@ListItem { Dialyzer doesn't help here }
@ListItem { @F Year could be any integer in this code!
@BulletList
@ListItem { Even year 3001 }
@ListItem { Or it could be a binary, a list, a tuple, a pid... }
@EndList
}
@ListItem { This value wouldn't match the type we defined }
@EndList
@End @Overhead
@Overhead
@Title { What is external data? }
@Begin
@BulletList
@ListItem { Anything that doesn't come directly from your process }
@ListItem { This includes:
@BulletList
@ListItem { Process messages }
@ListItem { Shared resources }
@ListItem { Sockets, files }
@ListItem { Return values from NIFs, ports, linked-in drivers }
@EndList
}
@ListItem { Real-world applications are all about external data! }
@End @Overhead
@Overhead
@Title { Why check external data? Just let it crash! }
@Begin
@BulletList
@ListItem { WHAT?! }
@ListItem { Are you sure it'll crash? }
@ListItem { Maybe it's going to crash an unrelated process
@BulletList
@ListItem { Like a central gen_server of your application }
@EndList
}
@ListItem { Maybe it's going to be stored in a database or a file }
@ListItem { Maybe it's going to be sent directly to connected clients }
@EndList
@End @Overhead
@Overhead
@Title { Really? }
@Begin
@BulletList
@ListItem { A person registering on your website today can't be born in 1492! }
@ListItem { Think about it, are you really crashing on this kind of data? }
@ListItem { Also think about XSS, SQL injection, and friends }
@EndList
@End @Overhead
@Overhead
@Title { Always validate external data }
@Begin
@BulletList
@ListItem { Either print a nice error message to the user
@BulletList
@ListItem { HTML forms, for example }
@EndList
}
@ListItem { Or crash as soon as possible
@BulletList
@ListItem { Don't crash anywhere! Crash on the system boundaries }
@ListItem { Don't let bad data crash your core processes }
@ListItem { Don't let external attacks or user error bring down your app }
}
@EndList
@End @Overhead
@Overhead
@Title { Data validation without Sheriff }
@Begin
@CD @F @Box paint { lightyellow } { @Verbatim {
-type year() :: 1900..2011.
-spec is_valid_year(year()) -> boolean().
is_valid_year(Y)
when is_integer(Y), Y >= 1900, Y =< 2011 ->
true;
is_valid_year(_Y) ->
false.
} }
@End @Overhead
@Overhead
@Title { All this has happened before... }
@Begin
@BulletList
@ListItem { I feel like I'm repeating myself there }
@EndList
@End @Overhead
@Overhead
@Title { And all this will happen again }
@Begin
@BulletList
@ListItem { I did write the same constraint twice }
@ListItem { Dialyzer already checks it for most of the program }
@ListItem { Why not use the @F year() type directly? }
@EndList
@End @Overhead
@Overhead
@Title { I can't type }
@Begin
@BulletList
@ListItem { But I can't use Erlang's types from runtime code! }
@EndList
@End @Overhead
@Overhead
@Title { Who do you call when you need help? }
@Begin
@CD 3.0 @Scale @IncludeGraphic badge.eps
@End @Overhead
@Overhead
@Title { I am the law }
@Begin
@BulletList
@ListItem { Sheriff is a runtime type checker }
@ListItem { It uses Erlang's type system for validation }
@ListItem { You don't need to duplicate constraints to validate data anymore! }
@ListItem { So just be lazy and validate all external data with a single LoC }
@EndList
@End @Overhead
@Overhead
@Title { Data validation with Sheriff }
@Begin
@CD @F @Box paint { lightyellow } { @Verbatim {
true = sheriff:check(Y, year()).
} }
@End @Overhead
@Overhead
@Title { Tuple validation with Sheriff }
@Begin
@CD @F @Box paint { lightyellow } { @Verbatim {
-type subject() :: 'I' | you | he.
-type verb() :: like | ignore | hate.
-type object() :: me | you | him.
-type grammar() :: {subject(), verb(), object()}.
%% ...
sheriff:check({you, like, me}, grammar()). %% true
sheriff:check({'I', love, you}, grammar()). %% false
} }
@End @Overhead
@Overhead
@Title { Recursive type validation with Sheriff }
@Begin
@CD @F @Box paint { lightyellow } { @Verbatim {
-type rtype() :: {leaf | rtype(), leaf | rtype()}.
%% ...
sheriff:check({leaf, {leaf, leaf}}, rtype()). %% true
sheriff:check({{flower, flower}, leaf}, rtype()). %% false
sheriff:check(<<"flower">>, rtype()). %% false
} }
@End @Overhead
@Overhead
@Title { Parameterized type validation with Sheriff }
@Begin
@CD @F @Box paint { lightyellow } { @Verbatim {
-type a() :: 0..65535.
-type b(T) :: undefined | T.
-type c() :: b(a()).
%% ...
sheriff:check(undefined, c()). %% true
sheriff:check(42, c()). %% true
sheriff:check(-1, c()). %% false
sheriff:check(1234567890, c()). %% false
sheriff:check(defined, c()). %% false
} }
@End @Overhead
@Overhead
@Title { Record validation with Sheriff }
@Begin
@CD @F @Box paint { lightyellow } { @Verbatim {
-record(packet, {
id :: 1 | 2 | 3,
num = 0 :: non_neg_integer(),
data = <<>> :: binary()
}).
-type packet() :: #packet{}.
%% ...
sheriff:check(#packet{id=1, data= <<0:32>>}, packet()). %% true
sheriff:check(#packet{id=undefined}, packet()). %% true
sheriff:check({packet, 2, 1, <<>>}, packet()). %% true
sheriff:check(#packet{id=0, num=7}, packet()). %% false
sheriff:check(#http_req{}, packet()). %% false
} }
@End @Overhead
@Overhead
@Title { Digging in }
@Begin
@BulletList
@ListItem { Sheriff is a parse_transform }
@ListItem { It first generates validation functions for all the types you defined }
@ListItem { It then replaces the @F @Verbatim { sheriff:check/2 } calls
with the proper validation calls }
@ListItem { It's fast and is only a compilation option away }
@EndList
@End @Overhead
@Overhead
@Title { Don't fall into lava }
@Begin
@BulletList
@ListItem { There are limitations }
@ListItem { Exported types can only work on modules that were compiled
using the sheriff parse_transform
@BulletList
@ListItem { Excluding basic types like @F { integer() } of course }
@EndList
}
@ListItem { It's only as good as Erlang's type system
@BulletList
@ListItem { It can't check element order in lists }
@ListItem { It can't check the content of binaries, only size }
@ListItem { This can probably be fixed later }
@EndList
}
@ListItem { Dialyzer will print out some warnings if analyzing from source }
@EndList
@End @Overhead
@Overhead
@Title { Sheriff got deputies }
@Begin
@BulletList
@ListItem { Sheriff, today, is only a proof of concept }
@ListItem { Code was written by two @I { Dev:Extend } interns
@BulletList
@ListItem { William Dang }
@ListItem { Hamza Mahmood }
@EndList
@ID 0.5 @Scale @IncludeGraphic wilza.eps
}
@ListItem { They only had one month to learn Erlang and do the project }
@ListItem { So there's probably many bugs! }
@EndList
@End @Overhead
@Overhead
@Title { To infinity, and beyond! }
@Begin
@BulletList
@ListItem { We'll cleanup the codebase }
@ListItem { We'll add PropEr tests }
@ListItem { We'll add a few missing features }
@ListItem { First release is planned for December 2011 }
@EndList
@End @Overhead
@Overhead
@Title { Wanted }
@Begin
@BulletList
@ListItem { You can help! }
@ListItem { Source code is already available on blue @Color {
"https://github.com/extend/sheriff" @ExternalLink { "https://github.com/extend/sheriff" } } }
@ListItem { Try it out }
@ListItem { Suggest improvements }
@ListItem { File bug reports
@BulletList
@ListItem { Wait for the code cleanup though }
@EndList
}
@EndList
@End @Overhead
@Overhead
@Title { Suit up! }
@Begin
@BulletList
@ListItem { We'll ping Kostis Sagonas and the red ties at some point }
@ListItem { They have experience, they can probably help }
@EndList
@End @Overhead
@Overhead
@Title { Fin }
@Begin
@BulletList
@ListItem { Any questions? }
@EndList
@End @Overhead