Making reliable distributed systems in the presence of sodware errors ,Joe Armstrong

#Erlang

pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

{P1,P2,...,Pn}={T1,T2,...,Tn}
{P,abcd}={123,abcd}.
[H|T]=[79,116].


{P,abc,123} when P == G,...
X > Y
X < Y
X =< Y
X >= Y
X == Y
X /= Y
X =:= Y
X =/= Y

{X,a,X,[B|X]}
%{X,a,F1,[B|F2]} when F1==X, F2==X

function

F(P11,...,P1N) when G11,...,G1N -> Body1;
F(P21,...,P2N) when G11,...,G1N -> Body2;
....
F(PK1, PK2, ..., PKN) -> BodyK.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

factorial(0) -> 1;
factorial(N) -> N * factorial(N-1).


member(H, [H|T]) -> true;
%member(H, [H1|_]) when H == H1 -> true;
member(H, [_|T] -> member(H, T);
member(H, []) -> false.

%%member(dog, [cat,man,dog,ape])


deposit(Who, Money) ->
Old = lookup(Who),
New = Old + Money,
insert(Who, New),
New.

%%deposit(joe, 25)

tail-recursive

fn(X1,Tails...)
fn(X2,Tails...)
fn(X3,Tails...)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

factorial(0) -> 1;
factorial(N) -> N * factorial(N-1).



%%tail
factorial(N) -> factorial_1(N, 1).
factorial_1(0, X) -> X;
factorial_1(N, X) -> factorial_1(N-1, N*X).


loop(Dict) ->
receive
{store, Key, Value} -> loop(dict:store(Key, Value, Dict));
{From, {get, Key}} -> From ! dict:fetch(Key, Dict), loop(Dict)
end.

case

case Expression of
Pattern1 -> Expr_seq1;
Pattern2 -> Expr_seq2;
...
end

if

if
    Guard1 ->
        Expr_seq1;
    Guard2 ->
        Expr_seq2;
    ...
end

Higher order functions

F=fun(I) -> ...I end
map(F, [H|T]) -> [F(H)|map(F, T)];
map(F, []) -> [].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
lists:map(fun(I) -> 2 *I end, [1,2,3,4]).


for(I, Max, F, Sum) when I < Max -> for(I+1, Max, F, Sum + F(I));
for(I, Max, F, Sum) -> Sum.
Sum0 = 0,Sum = for(0, Max, F, Sum0).
%curry
%f(X1)->f(X2)->...end end

Adder = fun(X) -> fun(Y) -> X + Y end end.
Adder10 = Adder(10).
Adder10(5).

% lambda
Fact = fun(X) ->
G = fun(0,F) -> 1;
(N, F) -> N*F(N-1,F)
end,
G(X, G)
end.
Fact(4)


X = fun foo/2
%X = fun(I, J) -> foo(I, J) end

list

[X || Qualifier1, Qualifier2, ...]
1
2
3
4
5
6
7
8
9
10

qsort([]) -> [];
qsort([Pivot|T]) ->
qsort([X||X<-T,X =< Pivot]) ++ [Pivot] ++ qsort([X||X<-T,X > Pivot]). % =<

perms([]) -> [[]];
perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].

% perms("123").
% ["123","132","213","231","312","321"]

binary

1
2
3
4
5
6
B1=list_to_binary([1,2,3]).
B2=list_to_binary([4,5,[6,7],[],[8,[9]],245]).
B3=concat_binary([B1,B2]).
split_binary(B3,6).
B = term_to_binary({hello,"joe"}).
binary_to_term(B).

bit

<Value:Size/TypeSpecifierList>
End-Sign-Type-Unit
big/little/native - signed/unsigned - integer/float/binary - 1..256

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
X=1,Y1=1,Y2=255,Y3=256,Z=1.
<<X,Y1,Z>>.
<<X,Y2,Z>>.
<<X,Y3,Z>>.
<<X,Y3:16,Z>>.
<<X,Y3:32,Z>>.
<<256:32>>.
<<256:32/big>>.
<<256:32/little>>.
<<1:1,2:7>>.
<<X:1,Y:7>> = <<130>>.
X.
Y.

-define(IP_VERSION, 4).
-define(IP_MIN_HDR_LEN, 5).
%...
DgramSize = size(Dgram),
case Dgram of
<<?IP_VERSION:4, HLen:4, SrvcType:8, TotLen:16,
ID:16, Flgs:3, FragOff:13,
TTL:8, Proto:8, HdrChkSum:16,
SrcIP:32,
DestIP:32, RestDgram/binary>> when HLen >= 5, 4*HLen =< DgramSize ->
OptsLen = 4*(HLen - ?IP_MIN_HDR_LEN),
<<Opts:OptsLen/binary,Data/binary>> = RestDgram,
%...

Records

-record(Name, {
Key1 = Default1,
Key2 = Default2,

}).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-record(person, {
firstName="",
lastName = "",
age}).


Person = #person{
firstName="Rip",
lastname="Van Winkle",
age=793
}


birthday(X=#person{age=N}) -> X#person{age=N+1}.

Macros

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-define(Constant, Replacement).
-define(Func(Var1, Var2,.., Var), Replacement).

-define(macro1(X, Y), {a, X, Y}).
-define(start, {).
-define(stop, }).


foo(A) ->
?macro1(A+10, b)
%{a,A+10,b}.
foo(A) ->
?start,a,?stop. %{...}

?FILE
?MODULE
?LINE

Include files

1
2
3
4

-include(Filename).
-include_lib(Name).
-include_lib("kernel/include/file.hrl").

module

1
2
3
4
5
6
7
-module(math).
-export([areas/1]). %% math:areas(...)
-import(lists, [map/2]). %% lists:map

areas(L) -> lists:sum( map( fun(I) -> area(I) end, L)).
area({square, X}) -> X*X;
area({rectangle,X,Y}) -> X*Y.
1
2
3
erl
c(math).
math : areas([{rectangle, 12, 4}, {square, 6}]).

erlang:
‘!’/2 ‘*’/2
‘+’/1 ‘+’/2
‘++’/2 ‘-‘/1
‘-‘/2 ‘–’/2
‘/‘/2 ‘/=’/2
‘<’/2 ‘=/=’/2
‘=:=’/2 ‘=<’/2
‘==’/2 ‘>’/2
‘>=’/2 ‘and’/2
‘band’/2 ‘bnot’/1
‘bor’/2 ‘bsl’/2
‘bsr’/2 ‘bxor’/2
‘div’/2 ‘not’/1
‘or’/2 ‘rem’/2
‘xor’/2 abs/1
adler32/1 adler32/2
adler32_combine/3 alloc_info/1
alloc_sizes/1 append/2
append_element/2 apply/2
apply/3 atom_to_binary/2
atom_to_list/1 binary_part/2
binary_part/3 binary_to_atom/2
binary_to_existing_atom/2 binary_to_float/1
binary_to_integer/1 binary_to_integer/2
binary_to_list/1 binary_to_list/3
binary_to_term/1 binary_to_term/2
bit_size/1 bitsize/1
bitstring_to_list/1 bump_reductions/1
byte_size/1 call_on_load_function/1
cancel_timer/1 cancel_timer/2
ceil/1 check_old_code/1
check_process_code/2 check_process_code/3
convert_time_unit/3 crasher/6
crc32/1 crc32/2
crc32_combine/3 date/0
decode_packet/3 delay_trap/2
delete_element/2 delete_module/1
demonitor/1 demonitor/2
disconnect_node/1 display/1
display_nl/0 display_string/1
dist_ctrl_get_data/1 dist_ctrl_get_data_notification/1
dist_ctrl_input_handler/2 dist_ctrl_put_data/2
dist_get_stat/1 dmonitor_node/3
dt_append_vm_tag_data/1 dt_get_tag/0
dt_get_tag_data/0 dt_prepend_vm_tag_data/1
dt_put_tag/1 dt_restore_tag/1
dt_spread_tag/1 element/2
erase/0 erase/1
error/1 error/2
exit/1 exit/2
exit_signal/2 external_size/1
external_size/2 finish_after_on_load/2
finish_loading/1 float/1
float_to_binary/1 float_to_binary/2
float_to_list/1 float_to_list/2
floor/1 format_cpu_topology/1
fun_info/1 fun_info/2
fun_info_mfa/1 fun_to_list/1
function_exported/3 garbage_collect/0
garbage_collect/1 garbage_collect/2
garbage_collect_message_area/0 gather_gc_info_result/1
get/0 get/1
get_cookie/0 get_keys/0
get_keys/1 get_module_info/1
get_module_info/2 get_stacktrace/0
group_leader/0 group_leader/2
halt/0 halt/1
halt/2 has_prepared_code_on_load/1
hd/1 hibernate/3
insert_element/3 integer_to_binary/1
integer_to_binary/2 integer_to_list/1
integer_to_list/2 iolist_size/1
iolist_to_binary/1 iolist_to_iovec/1
is_alive/0 is_atom/1
is_binary/1 is_bitstring/1
is_boolean/1 is_builtin/3
is_float/1 is_function/1
is_function/2 is_integer/1
is_list/1 is_map/1
is_map_key/2 is_number/1
is_pid/1 is_port/1
is_process_alive/1 is_record/2
is_record/3 is_reference/1
is_tuple/1 length/1
link/1 list_to_atom/1
list_to_binary/1 list_to_bitstring/1
list_to_existing_atom/1 list_to_float/1
list_to_integer/1 list_to_integer/2
list_to_pid/1 list_to_port/1
list_to_ref/1 list_to_tuple/1
load_module/2 load_nif/2
loaded/0 localtime/0
localtime_to_universaltime/1 localtime_to_universaltime/2
make_fun/3 make_ref/0
make_tuple/2 make_tuple/3
map_get/2 map_size/1
match_spec_test/3 max/2
md5/1 md5_final/1
md5_init/0 md5_update/2
memory/0 memory/1
min/2 module_info/0
module_info/1 module_loaded/1
monitor/2 monitor_node/2
monitor_node/3 monotonic_time/0
monotonic_time/1 nif_error/1
nif_error/2 node/0
node/1 nodes/0
nodes/1 now/0
open_port/2 phash/2
phash2/1 phash2/2
pid_to_list/1 port_call/2
port_call/3 port_close/1
port_command/2 port_command/3
port_connect/2 port_control/3
port_get_data/1 port_info/1
port_info/2 port_set_data/2
port_to_list/1 ports/0
posixtime_to_universaltime/1 pre_loaded/0
prepare_loading/2 process_display/2
process_flag/2 process_flag/3
process_info/1 process_info/2
processes/0 purge_module/1
put/2 raise/3
read_timer/1 read_timer/2
ref_to_list/1 register/2
registered/0 resume_process/1
round/1 self/0
send/2 send/3
send_after/3 send_after/4
send_nosuspend/2 send_nosuspend/3
seq_trace/2 seq_trace_info/1
seq_trace_print/1 seq_trace_print/2
set_cookie/2 set_cpu_topology/1
setelement/3 setnode/2
setnode/3 size/1
spawn/1 spawn/2
spawn/3 spawn/4
spawn_link/1 spawn_link/2
spawn_link/3 spawn_link/4
spawn_monitor/1 spawn_monitor/3
spawn_opt/1 spawn_opt/2
spawn_opt/3 spawn_opt/4
spawn_opt/5 split_binary/2
start_timer/3 start_timer/4
statistics/1 subtract/2
suspend_process/1 suspend_process/2
system_flag/2 system_info/1
system_monitor/0 system_monitor/1
system_monitor/2 system_profile/0
system_profile/2 system_time/0
system_time/1 term_to_binary/1
term_to_binary/2 throw/1
time/0 time_offset/0
time_offset/1 timestamp/0
tl/1 trace/3
trace_delivered/1 trace_info/2
trace_pattern/2 trace_pattern/3
trunc/1 tuple_size/1
tuple_to_list/1 unique_integer/0
unique_integer/1 universaltime/0
universaltime_to_localtime/1 universaltime_to_posixtime/1
unlink/1 unregister/1
whereis/1 yield/0

application application_controller
application_master atomics
beam_lib binary
c code
code_server counters
dict edlin
edlin_expand epp
erl_abstract_code erl_anno
erl_distribution erl_eval
erl_lint erl_parse
erl_prim_loader erl_scan
erl_signal_handler erl_tracer
erlang error_handler
error_logger erts_code_purger
erts_dirty_process_signal_handler erts_internal
erts_literal_area_collector ets
file file_io_server
file_server filename
gb_sets gb_trees
gen gen_event
gen_server global
global_group group
group_history heart
hipe_unified_loader inet
inet_config inet_db
inet_gethost_native inet_parse
inet_udp init
io io_lib
io_lib_format io_lib_pretty
kernel kernel_config
kernel_refc lists
logger logger_backend
logger_config logger_filters
logger_formatter logger_h_common
logger_handler_watcher logger_server
logger_simple_h logger_std_h
logger_sup maps
net_kernel orddict
ordsets os
otp_ring0 persistent_term
prim_buffer prim_eval
prim_file prim_inet
prim_zip proc_lib
proplists queue
ram_file raw_file_io
raw_file_io_raw rpc
sets shell
standard_error string
supervisor supervisor_bridge
timer unicode
unicode_util user_drv
user_sup zlib

lists:
all/2 any/2 append/1 append/2 concat/1
delete/2 droplast/1 dropwhile/2 duplicate/2 filter/2
filtermap/2 flatlength/1 flatmap/2 flatten/1 flatten/2
foldl/3 foldr/3 foreach/2 join/2 keydelete/3
keyfind/3 keymap/3 keymember/3 keymerge/3 keyreplace/4
keysearch/3 keysort/2 keystore/4 keytake/3 last/1
map/2 mapfoldl/3 mapfoldr/3 max/1 member/2
merge/1 merge/2 merge/3 merge3/3 min/1
module_info/0 module_info/1 nth/2 nthtail/2 partition/2
prefix/2 reverse/1 reverse/2 rkeymerge/3 rmerge/2
rmerge/3 rmerge3/3 rukeymerge/3 rumerge/2 rumerge/3
rumerge3/3 search/2 seq/2 seq/3 sort/1
sort/2 split/2 splitwith/2 sublist/2 sublist/3
subtract/2 suffix/2 sum/1 takewhile/2 ukeymerge/3
ukeysort/2 umerge/1 umerge/2 umerge/3 umerge3/3
unzip/1 unzip3/1 usort/1 usort/2 zf/2
zip/2 zip3/3 zipwith/3 zipwith3/4

string:
casefold/1 centre/2 centre/3 chars/2
chars/3 chomp/1 chr/2 concat/2
copies/2 cspan/2 equal/2 equal/3
equal/4 find/2 find/3 is_empty/1
join/2 left/2 left/3 len/1
length/1 lexemes/2 list_to_float/1 list_to_integer/1
lowercase/1 module_info/0 module_info/1 next_codepoint/1
next_grapheme/1 nth_lexeme/3 pad/2 pad/3
pad/4 prefix/2 rchr/2 replace/3
replace/4 reverse/1 right/2 right/3
rstr/2 slice/2 slice/3 span/2
split/2 split/3 str/2 strip/1
strip/2 strip/3 sub_string/2 sub_string/3
sub_word/2 sub_word/3 substr/2 substr/3
take/2 take/3 take/4 titlecase/1
to_float/1 to_graphemes/1 to_integer/1 to_lower/1
to_upper/1 tokens/2 trim/1 trim/2
trim/3 uppercase/1 words/1 words/2

file:
advise/4 allocate/3 altname/1
change_group/2 change_mode/2 change_owner/2
change_owner/3 change_time/2 change_time/3
close/1 consult/1 copy/2
copy/3 copy_opened/3 datasync/1
del_dir/1 delete/1 eval/1
eval/2 format_error/1 get_cwd/0
get_cwd/1 ipread_s32bu_p32bu/3 ipread_s32bu_p32bu_int/3
list_dir/1 list_dir_all/1 make_dir/1
make_link/2 make_symlink/2 module_info/0
module_info/1 native_name_encoding/0 open/2
path_consult/2 path_eval/2 path_eval/3
path_open/3 path_script/2 path_script/3
pid2name/1 position/2 pread/2
pread/3 pwrite/2 pwrite/3
raw_read_file_info/1 raw_write_file_info/2 read/2
read_file/1 read_file_info/1 read_file_info/2
read_line/1 read_link/1 read_link_all/1
read_link_info/1 read_link_info/2 rename/2
script/1 script/2 sendfile/2
sendfile/5 set_cwd/1 sync/1
truncate/1 write/2 write_file/2
write_file/3 write_file_info/2 write_file_info/3

sets:
add_element/2 del_element/2 filter/2 fold/3
from_list/1 intersection/1 intersection/2 is_disjoint/2
is_element/2 is_empty/1 is_set/1 is_subset/2
module_info/0 module_info/1 new/0 size/1
subtract/2 to_list/1 union/1 union/2

maps:
filter/2 find/2 fold/3 from_list/1 get/2
get/3 is_key/2 iterator/1 keys/1 map/2
merge/2 module_info/0 module_info/1 new/0 next/1
put/3 remove/2 size/1 take/2 to_list/1
update/3 update_with/3 update_with/4 values/1 with/2

binary:
at/2 bin_to_list/1 bin_to_list/2
bin_to_list/3 compile_pattern/1 copy/1
copy/2 decode_unsigned/1 decode_unsigned/2
encode_unsigned/1 encode_unsigned/2 first/1
last/1 list_to_bin/1 longest_common_prefix/1
longest_common_suffix/1 match/2 match/3
matches/2 matches/3 module_info/0
module_info/1 part/2 part/3
referenced_byte_size/1 replace/3 replace/4
split/2 split/3

ordsets:
add_element/2 del_element/2 filter/2 fold/3
from_list/1 intersection/1 intersection/2 is_disjoint/2
is_element/2 is_empty/1 is_set/1 is_subset/2
module_info/0 module_info/1 new/0 size/1
subtract/2 to_list/1 union/1 union/2

orddict:
append/3 append_list/3 erase/2 fetch/2
fetch_keys/1 filter/2 find/2 fold/3
from_list/1 is_empty/1 is_key/2 map/2
merge/3 module_info/0 module_info/1 new/0
size/1 store/3 take/2 to_list/1
update/3 update/4 update_counter/3

os:
cmd/1 cmd/2 find_executable/1 find_executable/2
get_env_var/1 getenv/0 getenv/1 getenv/2
getpid/0 list_env_vars/0 module_info/0 module_info/1
perf_counter/0 perf_counter/1 putenv/2 set_env_var/2
set_signal/2 system_time/0 system_time/1 timestamp/0
type/0 unset_env_var/1 unsetenv/1 version/0

io:
columns/0 columns/1 format/1 format/2
format/3 fread/2 fread/3 fwrite/1
fwrite/2 fwrite/3 get_chars/2 get_chars/3
get_line/1 get_line/2 get_password/0 get_password/1
getopts/0 getopts/1 module_info/0 module_info/1
nl/0 nl/1 parse_erl_exprs/1 parse_erl_exprs/2
parse_erl_exprs/3 parse_erl_exprs/4 parse_erl_form/1 parse_erl_form/2
parse_erl_form/3 parse_erl_form/4 printable_range/0 put_chars/1
put_chars/2 read/1 read/2 read/3
read/4 request/1 request/2 requests/1
requests/2 rows/0 rows/1 scan_erl_exprs/1
scan_erl_exprs/2 scan_erl_exprs/3 scan_erl_exprs/4 scan_erl_form/1
scan_erl_form/2 scan_erl_form/3 scan_erl_form/4 setopts/1
setopts/2 write/1 write/2

Concurrent

Pid = spawn(F)
Pid ! Msg

receive
Msg1 [when Guard1] ->
Expr_seq1;
Msg2 [when Guard2] ->
Expr_seq2;

MsgN [when GuardN] ->
Expr_seqN;

[; after TimeOutTime ->
Timeout_Expr_seq]
end

register

register(Name, Pid)

err

Val = (catch Expr)% {’EXIT’, _}
(catch F) % {’EXIT’,P}.
throw(Q)
exit(P)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Y = (catch 1/0).

sqrt(X) when X < 0 ->
exit({sqrt,X});
sqrt(X) ->
...


h(cat) -> exit(dog);
h(N)-> 10*N.

g(X) ->
case (catch h(X)) of
{’EXIT’, _} -> 10;
Val -> Val
end.
in process A
B = spawn_link(fun() -> ... end), % {’EXIT’,Why} -> {’EXIT’,P,Why}

ERROR HANDLING

Ref = erlang:monitor(process, B) % {’DOWN’, Ref, process, B, Why} -> A
1
2
3
4
5
6
7
8
9
10
11

start() -> spawn(fun go/0).
go() ->
process_flag(trap_exit, true),
loop().

loop() ->
receive
{’EXIT’,P,Why} -> % kill
... handle the error ...
end

Distributed

PORTS

spawn(Fun,Node)
monitor(Node)
P ! {Con, Command}
{Port,{data,D}}

Command
{command,Data} %
close % {P,closed}
{connect,Pid1} % {Port,connected}

DYNAMIC CODE CHANGE

tail-calls -> no old code to return -> all old code for a module can safely be deleted.

1
2
3
4
5
6
7
8
-module(m).  %% 1.load/reload m
...
loop(Data, F) ->
receive
{From, Q} ->
{Reply, Data1} = F(Q, Data),
m:loop(data1, F) %% 2.loop m
end.
1
2
3
4
5
6
7
8
-module(m).  %% load/reload m
...
loop(Data, F) ->
receive
{From, Q} ->
{Reply, Data1} = F(Q, Data),
loop(data1, F)
end.

A type notation

+type file:open(fileName(), read | write) ->
{ok, fileHandle()}
| {error, string()}.
+type file:read_line(fileHandle()) ->
{ok, string()}
| eof.
+type file:close(fileHandle()) ->
true.
+deftype fileName() = [int()]
+deftype string() = [int()].
+deftype fileHandle() = pid().

int()— is the integer type.
atom() — is the atom type.
pid() — is the Pid type.
ref() — is the reference type.
float() — is the Erlang float type.
port() — is the port type.
bin() — is the binary type.

List, tuple and alternation types are defined recursively:

{X1,X2,…,Xn} {T1,T2,…,Tn}
[X1,X2,…,Xn] [T]
T1|T2

+deftype name1() = name2() = … = Type.
+deftype bool()= true | false.
+deftype weekday() = monday|tuesday|wednesday|thursday|friday.
+deftype weekend() = saturday() | sunday().
+deftype day()= weekday() | weekend().

+type fn(T1, T2, …, Tn) -> T.

+deftype string() = [int()].
+deftype day()= number() = int().
+deftype town()= street() = string().
+type factorial(int()) -> int().
+type day2int(day())-> int().
+type address(person()) -> {town(), street(), number()}.

+type fun(T1, T2, …, Tn) -> T end
+type map(fun(X) -> Y end, [X]) -> [Y].

PROGRAMMING TECHNIQUES

server –reply–> clients
server <-query– clients

{State1, Reply} = F(Query, State)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

-module(server1).
-export([start/3, stop/1, rpc/2]).

start(Name, F, State) ->
register(Name, spawn(fun() -> loop(Name, F, State) end)).
stop(Name) ->Name ! stop.

rpc(Name, Query) ->
Name ! {self(), Query}, %c->s
receive
{Name, Reply} -> Reply
end.

loop(Name, F, State) ->
receive
stop -> void;
{Pid, Query} -> % s->s
{Reply, State1} = F(Query, State),
Pid ! {Name, Reply},
loop(Name, F, State1) %% tail
end.

vshlr1:start().
vshlr1:find(“joe”).
vshlr1:i_am_at(“joe”, “sics”).
vshlr1:find(“joe”).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-module(vshlr1).
-export([start/0, stop/0, handle_event/2,i_am_at/2, find/1]).

-import(server1, [start/3, stop/1, rpc/2]).
-import(dict,[new/0, store/3, find/2]).

start() -> start(vshlr, fun handle_event/2, new()).
stop() -> stop(vshlr).

i_am_at(Who, Where) ->rpc(vshlr, {i_am_at, Who, Where}).

find(Who) ->rpc(vshlr, {find, Who}).

handle_event({i_am_at, Who, Where}, Dict) -> {ok, store(Who, Where, Dict)};

handle_event({find, Who}, Dict) -> {find(Who, Dict), Dict}.

A simple server with error recovery.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

-module(server2).
-export([start/3, stop/1, rpc/2]).
start(Name, F, State) ->
register(Name, spawn(fun() -> loop(Name,F,State) end)).
stop(Name) -> Name ! stop.
rpc(Name, Query) ->Name ! {self(), Query},

receive
{Name, crash} -> exit(rpc);
{Name, ok, Reply} -> Reply
end.

loop(Name, F, State) ->
receive
stop -> void;
{From, Query} ->
case (catch F(Query, State)) of
{’EXIT’, Why} ->
log_error(Name, Query, Why),
From ! {Name, crash},
loop(Name, F, State);
{Reply, State1} ->
From ! {Name, ok, Reply},
loop(Name, F, State1)
end
end.
log_error(Name, Query, Why) ->io:format("Server ~p query ~p caused exception ~p~n",[Name, Query, Why]).

nodejs graphql

DataLoader

https://www.npmjs.com/package/dataloader
https://github.com/graphql/dataloader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
var DataLoader = require('dataloader')

const say=x=>y=>console.log(x,y)
superagent=require('superagent')
get=u=>superagent.get(u).then(x=>({id:u,content:x.text}))
gets=us=>Promise.all(us.map(get))
const {promisify} = require('util');
const each=(f)=>(o={})=> Object.entries(o).forEach(f)
const promisifyAll=(c)=>{
let f=([k,v])=>c.__proto__["_"+k]=promisify(v).bind(c)
each(f)(c.__proto__)
return c
}
conn=()=>{
var redis = require('redis');
var client = redis.createClient();
promisifyAll(client)
return client
}


const test_db=async ()=>{
const users=[
{ id: 2, name: 'San Francisco' },
{ id: 9, name: 'Chicago' },
{ id: 1, name: 'New York' },
{ id: 'A', name: 'N' },
{ id: 'B', name: 'NYork' },
]
cdns=Array(100).fill(0).map((x,i)=>({id:i,name:"ccc"+i}))
stories=Array(100).fill(0).map((x,i)=>({id:i,name:"sss"+i}))
const nulls=xs=>xs.map(x=>({id:x,name:""}))
genUsers=(authToken, ids)=> authToken !== 0 ? nulls(ids) : users.filter(x=>ids.includes(x.id))
genCdnUrls=(authToken, ids)=> authToken !== 0 ? nulls(ids) : cdns.filter(x=>ids.includes(x.id))
genStories=(authToken, ids)=> authToken !== 0 ? nulls(ids): stories.filter(x=>ids.includes(x.id))

createLoaders=(authToken)=>({
user: new DataLoader(async ids => genUsers(authToken, ids),{ cache: true}),
cdn: new DataLoader(async rawUrls => genCdnUrls(authToken, rawUrls)),
stories: new DataLoader(async keys => genStories(authToken, keys)),
})

authToken=0
loaders = createLoaders(authToken)
let {user,cdn,stories:s}=loaders
{
u = await user.load(2);
let {id,name}=u
user.prime(name, u);
user.prime(id , u);
c=await cdn.load(id)
s=await s.load(id)
console.log({u,c,s})
}

uuu=async ()=>{
a=await user.load(1)
user.clear(1).prime(1,{id:1,name:"zzz"})
a1=user.prime(1)._promiseCache.get(1)
b=await user.loadMany([1,2,9])
c=await user.loadMany([9,1,2])
console.log(a,a1,b,c)
//user.clearAll()
}

}

const test_http=async ()=>{
u='https://www.douban.com/group/blabla/discussion?start='
douban = new DataLoader((ids)=>gets(ids.map(x=>u+x)));
a=await douban.load(1)
b=await douban.loadMany([1,2,9])
console.log(a,b)
}

const test_redis=async()=>{
c=conn()
redisLoader = new DataLoader(client._mget);
a=await redisLoader.load('foo')
let [x,y]=await redisLoader.loadMany(["foo","foo"])
let t=await redisLoader.loadMany(["foo","foo1"])
console.log({a,x,y,t})
//{ a: 'zzz', x: 'zzz', y: 'zzz', t: [ 'zzz', null ] }
//redisLoader._promiseCache.get('foo')
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

var { makeExecutableSchema } = require('graphql-tools');
var typeDefs = [`
type Query {
hello: String
}

schema {
query: Query
}`];

var resolvers = {
Query: {
hello(root) {
return 'world';
}
}
};

var schema = makeExecutableSchema({typeDefs, resolvers});

sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var DataLoader = require('dataloader');
var sqlite3 = require('sqlite3');
var db = new sqlite3.Database('./db.sql');

// Dispatch a WHERE-IN query, ensuring response has rows in correct order.
var userLoader = new DataLoader(ids => {
var params = ids.map(id => '?' ).join();
var query = `SELECT * FROM users WHERE id IN (${params})`;
return queryLoader.load([query, ids]).then(
rows => ids.map(
id => rows.find(row => row.id === id) || new Error(`Row not found: ${id}`)
)
);
});

// Parallelize all queries, but do not cache.
var queryLoader = new DataLoader(queries => new Promise(resolve => {
var waitingOn = queries.length;
var results = [];
db.parallelize(() => {
queries.forEach((query, index) => {
db.all.apply(db, query.concat((error, result) => {
results[index] = error || result;
if (--waitingOn === 0) {
resolve(results);
}
}));
});
});
}), { cache: false });

// Usage

var promise1 = userLoader.load('1234');
var promise2 = userLoader.load('5678');

Promise.all([ promise1, promise2 ]).then(([ user1, user2]) => {
console.log(user1, user2);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const test_couch=async ()=>{
var DataLoader = require('dataloader');
var nano = require('nano');
var couch = nano('http://localhost:5984');
var userDB = couch.use('users');
var userLoader = new DataLoader(keys => new Promise((resolve, reject) => {
userDB.fetch({ keys: keys }, (error, docs) => {
if (error) {
return reject(error);
}
resolve(docs.rows.map(row => row.error ? new Error(row.error) : row.doc));
});
}));
var promise1 = userLoader.load('8fce1902834ac6458e9886fa7f89c0ef');
var promise2 = userLoader.load('00a271787f89c0ef2e10e88a0c00048b');
Promise.all([ promise1, promise2 ]).then(([ user1, user2]) => {
console.log(user1, user2);
});
}

callback hell to promise

[f1,f2,f3,f4,f5,] -> [r1,r2,r3,r4,r5,]

1
2
3
4
const run_parallel =(fs=[])=>fs.map(x=>x())

//async tasks
const run_parallel_p =(fs=[])=>Promise.all(fs.map(x=>x()))

f1->f2->f3->f4->f5 -> r

1
2
3
4
5
6
7
8
9

const repeat=(x,n)=>Array(n).fill(x)
const run_sequential=(fs=[],result)=>fs.reduce((acc,f)=>f(acc),result)
const run_repeat=async (f,n=1,result)=>run_sequential(repeat(f,n),result)

//async tasks
const run_sequential_p1=async ([h,...t],result)=> h ? run_sequential(t,await h(result)) : result
const run_sequential_p2=async(fs,result)=>fs.reduce(async (acc,f)=>f(await acc),result)
const run_repeat_p=async (f,n=1,result)=>run_sequential_p2(repeat(f,n),result)

f1->f2->f3->…fn

cond ? next() : return
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//f=(ctx,next)=>{...;next()}
//compose([f1,f2,...,fn])
const compose=([h,...t])=>ctx=>{
if (!h) { return ; }
h(ctx,()=>compose(t)(ctx))
}


//f=async(ctx,next)=>{await g(ctx),await next()}
//compose([f1,f2,...,fn])
const compose1=([h,...t])=>async ctx=>{
if (!h) { return ; }
await h(ctx,()=>compose1(t)(ctx))
}

const test_compose=()=>{
f1=(ctx,next)=>{
ctx.user_name="ccc"
next()
}
f2=(ctx,next)=>{
token= ctx.user_name=="ccc" ? 2 : 0
ctx.token=token
next()
}
f3=(ctx,next)=>{
token=ctx.token
ctx.auth= token==2 ? true : false
if (ctx.auth){
next()
}
}
f4=(ctx,next)=>{
get_age=(x)=>123
ctx.age=get_age(ctx.user_name)
next()
}

ctx={}
f=compose([f1,f2,f3,f4])
f(ctx)
console.log(ctx)
}


const test_compose1=async()=>{
add=async(x)=>x+10
superagent=require('superagent')
get=u=>superagent.get(u).then(x=>x.header)
u='http://www.baidu.com'
baidu=()=>get(u)

f1=async(ctx,next)=>{
ctx.a=await add(10)
ctx.b1=await baidu()
await next()
}

f2=async(ctx,next)=>{
console.log('2222222222222222222222',ctx.b1)
ctx.b=await add(ctx.a)
ctx.b2=await baidu()
await next()
}
f3=async(ctx,next)=>{
console.log('3333333333333333333333',ctx.b2)
ctx.c=await add(ctx.b)
ctx.b3=await baidu()
await next()
}
f4=async(ctx,next)=>{
console.log('44444444444444444444',ctx.b3)
ctx.d=await add(ctx.c)
ctx.b4=await baidu()
await next()
}

ctx={}
f=compose1([f1,f2,f3,f4])
await f(ctx)
console.log(ctx)
// { a: 20, b: 30, c: 40, d: 50 }
}

callback hell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//https://api.jquery.com/jQuery.get/
const callback_hell=()=>{
fn=(...a,cb)
$.get("1",
x1=>$.get(x1,
x2=>$.get(x2,
x3=>$.get(x3,
(x4)=>$.get(x4,
x5=>$.get(x5,x6=>{
//...
})
)
)
)
)
)
}

test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

const get=async(u)=>fetch(u).then(x=>x.text())
const gets=(u=[1,2,3,4,5])=>run_parallel(u.map(get))

const test_run_seq=async()=>{
add=async (x)=>x+10
r0=await run_sequential_p1([get,get,get,get],"1")
r1=await run_sequential_p2([get,get,get,get],"1")
r2=await run_repeat_p(get,5,"1")
r3=await run_sequential_p1([add,add,add],10)
console.log(r3)

}

const test_async=async()=>{
let u0="1"
let r1=await get(u0)
let r2=await get(r1)
let r3=await get(r2)
let r4=await get(r3)
}


const test_then=()=>{
get("1")
.then(get)
.then(get)
.then(get)
.then(get)
.then(console.log)
}

const test_rxjs=()=>{
get1()
.map(get)
.map(get)
.map(get)
.map(get)
.mergeAll()
.subscribe(console.log);
}

callback -> async/await

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

const each=(f)=>(o={})=> Object.entries(o).forEach(f)
const {promisify} = require('util');
//https://github.com/browserify/node-util

const promisify0=(fn,t)=>(...arg)=>new Promise((f1,f2)=>fn.call(t,...arg,(err,d)=>err?f2(err):f1(d))) //fn(...,cb=(err,d)=>(...)) //redis..
const promisify1=(fn)=>(...arg)=>new Promise((f1,f2)=>fn(...arg,(err,d)=>err?f2(err):f1(d))) //fn(...,cb=(err,d)=>(...))
const promisify2=(fn)=>(...arg)=>new Promise((f1,f2)=>fn(...arg,f1)) //fn(...,cb)
const promisify3=(fn)=>(...arg)=>new Promise((f1,f2)=>fn(f1,...arg,)) //fn(cb,...)
const promisifyAll=(c)=>{
let f=([k,v])=>c.__proto__["_"+k]=promisify(v).bind(c)
each(f)(c.__proto__)
return c
}
const promisifyAll1=(c)=>{
let f=([k,v])=>c.__proto__["_"+k]=promisify0(v).bind(c)
each(f)(c.__proto__)
return c
}

const promisifyAll2=(c)=>{
let p=c.constructor;
let o=p.prototype
let f=([k,v])=>o["_"+k]=promisify0(v,c)
each(f)(o)
return c
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

const test_promisify1=async()=>{
// callback
add=(x,y,f)=>f(null,x+y)
add(1,2,console.log)

add1=promisify1(add)
r=await add1(1,3)
console.log(r)

}

const test_promisify_no_err=async()=>{
add_ok=(x,y,f)=>f(x,y)
add_ok1=promisify2(add_ok)
r=await add_ok1(1,2)
console.log(r)
}

const test_promisify2=async()=>{
sleep0=(f,n)=>setTimeout(f,n*1000)
sleep_p0=promisify2(sleep1)

console.log(1)
await sleep_p0(3)
console.log(2)
}

const test_promisify3=async()=>{
sleep0=(f,n)=>setTimeout(f,n*1000)
sleep_p1=promisify3(sleep0)

console.log(1)
await sleep_p1(3)
console.log(2)
}

const test_promisify_all=async ()=>{
const redis = require("redis");
const conn=()=>{
let client = redis.createClient();
promisifyAll(client)
//promisifyAll1(client)
//promisifyAll2(client)
return client
}
c=conn()
r1=await c._set('foo',"ddd")
r2=await c._get('foo')
console.log(r1,r2)
}

nodejs mongodb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
const mongodb = require('mongodb')
const mongoClient = mongodb.MongoClient

const conn=async ({table,db,db_url,}=process.env)=>{
const mc = await mongoClient.connect(db_url,{useNewUrlParser: true})
return mc.db(db).collection(table)
}

const test=async ()=>{
c=await conn()
r=await c._findAndModify()
r=await c.aggregate()
r=await c.bulkWrite()
r=await c.collectionName()
r=await c.count()
r=await c.countDocuments()
r=await c.createIndex()
r=await c.createIndexes()
r=await c.dbName()
r=await c.deleteMany()
r=await c.deleteOne()
r=await c.distinct()
r=await c.drop()
r=await c.dropAllIndexes()
r=await c.dropIndex()
r=await c.dropIndexes()
r=await c.ensureIndex()
r=await c.estimatedDocumentCount()
r=await c.find()
r=await c.findAndModify()
r=await c.findAndRemove()
r=await c.findOne()
r=await c.findOneAndDelete()
r=await c.findOneAndReplace()
r=await c.findOneAndUpdate()
r=await c.geoHaystackSearch()
r=await c.getLogger()
r=await c.group()
r=await c.hint()
r=await c.indexExists()
r=await c.indexInformation()
r=await c.indexes()
r=await c.initializeOrderedBulkOp()
r=await c.initializeUnorderedBulkOp()
r=await c.insert()
r=await c.insertMany()
r=await c.insertOne()
r=await c.isCapped()
r=await c.listIndexes()
r=await c.mapReduce()
r=await c.namespace()
r=await c.options()
r=await c.parallelCollectionScan()
r=await c.reIndex()
r=await c.readConcern()
r=await c.remove()
r=await c.removeMany()
r=await c.removeOne()
r=await c.rename()
r=await c.replaceOne()
r=await c.save()
r=await c.stats()
r=await c.update()
r=await c.updateMany()
r=await c.updateOne()
r=await c.watch()
r=await c.writeConcern()
}

My New Post

A First Level Header

A Second Level Header

Now is the time for all good men to come to
the aid of their country. This is just a
regular paragraph.

The quick brown fox jumped over the lazy
dog’s back.

Header 3

This is a blockquote.

This is the second paragraph in the blockquote.

This is an H2 in a blockquote

Some of these words are emphasized.
Some of these words are emphasized also.

Use two asterisks for strong emphasis.
Or, if you prefer, use two underscores instead.

Lists

Unordered (bulleted) lists use asterisks, pluses, and hyphens (*, +, and -) as list markers. These three markers are interchangable; this:

  • Candy.
  • Gum.
  • Booze.

this:

  • Candy.
  • Gum.
  • Booze.

and this:

  • Candy.
  • Gum.
  • Booze.
  1. Red
  2. Green
  3. Blue
  • A list item.

    With multiple paragraphs.

  • Another item in the list.

example link.

I get 10 times more traffic from Google than from
Yahoo or MSN.

I start my morning with a cup of coffee and
The New York Times.

alt text

alt text

I strongly recommend against using any <blink> tags.

I wish SmartyPants used named entities like &mdash;
instead of decimal-encoded entites like &#8212;.

If you want your page to validate under XHTML 1.0 Strict,
you’ve got to put paragraph tags in your blockquotes:

<blockquote>
    <p>For example.</p>
</blockquote>
1
rm -rf /
1
const add=(a,b)=>a+b
1
2
def sum(a,b):
return a+b
1
2
3
def sum(x,y):
x+y
end
1
2
3
function add($a,$b){
return $a+$b;
}
1
2
3
4
5
6
7
8
9
10
11
12

use ferris_says::say; // from the previous step
use std::io::{stdout, BufWriter};

fn main() {
let stdout = stdout();
let out = b"Hello fellow Rustaceans!";
let width = 24;

let mut writer = BufWriter::new(stdout.lock());
say(out, width, &mut writer).unwrap();
}
1
2
3
primes = filterPrime [2..]
where filterPrime (p:xs) =
p : filterPrime [x | x <- xs, x `mod` p /= 0]
1
2
3
4
5
6

-module(tut).
-export([double/1]).

double(X) ->
2 * X.
1
2
3
4
5
6
7
8
9
10
11
current_process = self()

# Spawn an Elixir process (not an operating system one!)
spawn_link(fn ->
send(current_process, {:msg, "hello world"})
end)

# Block until the message is received
receive do
{:msg, contents} -> IO.puts(contents)
end

This is an H1

This is an H2

This is an H1

This is an H2

This is an H3

This is an H4

This is an H5
This is an H6

This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.

Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.

This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.

Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.

This is the first level of quoting.

This is nested blockquote.

Back to the first level.

Blockquotes can contain other Markdown elements, including headers, lists, and code blocks:

This is a header.

  1. This is the first list item.
  2. This is the second list item.

Here’s some example code:

return shell_exec("echo $input | $markdown_script");
  • Red
  • Green
  • Blue
  • Red
  • Green
  • Blue
  • Red
  • Green
  • Blue
  1. Bird
  2. McHale
  3. Parish
  4. Bird
  5. McHale
  6. Parish
  7. Bird
  8. McHale
  9. Parish
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
    viverra nec, fringilla in, laoreet vitae, risus.
  • Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
    Suspendisse id sem consectetuer libero luctus adipiscing.
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
    viverra nec, fringilla in, laoreet vitae, risus.
  • Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
    Suspendisse id sem consectetuer libero luctus adipiscing.
  • Bird
  • Magic

But this:

  • Bird

  • Magic

will turn into:

  • Bird

  • Magic

List items may consist of multiple paragraphs. Each subsequent paragraph in a list item must be indented by either 4 spaces or one tab:

  1. This is a list item with two paragraphs. Lorem ipsum dolor
    sit amet, consectetuer adipiscing elit. Aliquam hendrerit
    mi posuere lectus.

    Vestibulum enim wisi, viverra nec, fringilla in, laoreet
    vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
    sit amet velit.

  2. Suspendisse id sem consectetuer libero luctus adipiscing.

It looks nice if you indent every line of the subsequent paragraphs, but here again, Markdown will allow you to be lazy:

  • This is a list item with two paragraphs.

    This is the second paragraph in the list item. You’re
    only required to indent the first line. Lorem ipsum dolor
    sit amet, consectetuer adipiscing elit.

  • Another item in the same list.

To put a blockquote within a list item, the blockquote’s > delimiters need to be indented:

  • A list item with a blockquote:

    This is a blockquote
    inside a list item.

To put a code block within a list item, the code block needs to be indented twice — 8 spaces or two tabs:

  • A list item with a code block:

    <code goes here>

It’s worth noting that it’s possible to trigger an ordered list by accident, by writing something like this:

  1. What a great season.

In other words, a number-period-space sequence at the beginning of a line. To avoid this, you can backslash-escape the period:

1986. What a great season.

Code Blocks

Pre-formatted code blocks are used for writing about programming or markup source code. Rather than forming normal paragraphs, the lines of a code block are interpreted literally. Markdown wraps a code block in both

 and  tags.

To produce a code block in Markdown, simply indent every line of the block by at least 4 spaces or 1 tab. For example, given this input:

This is a normal paragraph:

This is a code block.

Markdown will generate:

This is a normal paragraph:

This is a code block.

One level of indentation — 4 spaces or 1 tab — is removed from each line of the code block. For example, this:

Here is an example of AppleScript:

tell application "Foo"
    beep
end tell

will turn into:

Here is an example of AppleScript:

tell application "Foo"
    beep
end tell

A code block continues until it reaches a line that is not indented (or the end of the article).

Within a code block, ampersands (&) and angle brackets (< and >) are automatically converted into HTML entities. This makes it very easy to include example HTML source code using Markdown — just paste it and indent it, and Markdown will handle the hassle of encoding the ampersands and angle brackets. For example, this:

<div class="footer">
    &copy; 2004 Foo Corporation
</div>

will turn into:

<div class="footer">
    &copy; 2004 Foo Corporation
</div>

Regular Markdown syntax is not processed within code blocks. E.g., asterisks are just literal asterisks within a code block. This means it’s also easy to use Markdown to write about Markdown’s own syntax.
Horizontal Rules

You can produce a horizontal rule tag (


) by placing three or more hyphens, asterisks, or underscores on a line by themselves. If you wish, you may use spaces between the hyphens or asterisks. Each of the following lines will produce a horizontal rule:






Span Elements
Links

Markdown supports two style of links: inline and reference.

In both styles, the link text is delimited by [square brackets].

To create an inline link, use a set of regular parentheses immediately after the link text’s closing square bracket. Inside the parentheses, put the URL where you want the link to point, along with an optional title for the link, surrounded in quotes. For example:

This is an example inline link.

This link has no title attribute.

Will produce:

This is an example inline link.

This link has no title attribute.

If you’re referring to a local resource on the same server, you can use relative paths:

See my About page for details.

Reference-style links use a second set of square brackets, inside which you place a label of your choosing to identify the link:

This is an example reference-style link.

You can optionally use a space to separate the sets of brackets:

This is [an example] id reference-style link.

Then, anywhere in the document, you define your link label like this, on a line by itself:

That is:

Square brackets containing the link identifier (optionally indented from the left margin using up to three spaces);
followed by a colon;
followed by one or more spaces (or tabs);
followed by the URL for the link;
optionally followed by a title attribute for the link, enclosed in double or single quotes, or enclosed in parentheses.

The following three link definitions are equivalent:

Note: There is a known bug in Markdown.pl 1.0.1 which prevents single quotes from being used to delimit link titles.

The link URL may, optionally, be surrounded by angle brackets:

You can put the title attribute on the next line and use extra spaces or tabs for padding, which tends to look better with longer URLs:

Link definitions are only used for creating links during Markdown processing, and are stripped from your document in the HTML output.

Link definition names may consist of letters, numbers, spaces, and punctuation — but they are not case sensitive. E.g. these two links:

[link text][a]
[link text][A]

are equivalent.

The implicit link name shortcut allows you to omit the name of the link, in which case the link text itself is used as the name. Just use an empty set of square brackets — e.g., to link the word “Google” to the google.com web site, you could simply write:

Google

And then define the link:

Because link names may contain spaces, this shortcut even works for multiple words in the link text:

Visit Daring Fireball for more information.

And then define the link:

Link definitions can be placed anywhere in your Markdown document. I tend to put them immediately after each paragraph in which they’re used, but if you want, you can put them all at the end of your document, sort of like footnotes.

Here’s an example of reference links in action:

I get 10 times more traffic from Google 1 than from
Yahoo 2 or MSN 3.

Using the implicit link name shortcut, you could instead write:

I get 10 times more traffic from Google than from
Yahoo or MSN.

Both of the above examples will produce the following HTML output:

I get 10 times more traffic from Google than from Yahoo or MSN.

For comparison, here is the same paragraph written using Markdown’s inline link style:

I get 10 times more traffic from Google
than from Yahoo or
MSN.

The point of reference-style links is not that they’re easier to write. The point is that with reference-style links, your document source is vastly more readable. Compare the above examples: using reference-style links, the paragraph itself is only 81 characters long; with inline-style links, it’s 176 characters; and as raw HTML, it’s 234 characters. In the raw HTML, there’s more markup than there is text.

With Markdown’s reference-style links, a source document much more closely resembles the final output, as rendered in a browser. By allowing you to move the markup-related metadata out of the paragraph, you can add links without interrupting the narrative flow of your prose.
Emphasis

Markdown treats asterisks (*) and underscores (_) as indicators of emphasis. Text wrapped with one * or _ will be wrapped with an HTML tag; double *’s or _’s will be wrapped with an HTML tag. E.g., this input:

single asterisks

single underscores

double asterisks

double underscores

will produce:

single asterisks

single underscores

double asterisks

double underscores

You can use whichever style you prefer; the lone restriction is that the same character must be used to open and close an emphasis span.

Emphasis can be used in the middle of a word:

unfriggingbelievable

But if you surround an * or _ with spaces, it’ll be treated as a literal asterisk or underscore.

To produce a literal asterisk or underscore at a position where it would otherwise be used as an emphasis delimiter, you can backslash escape it:

*this text is surrounded by literal asterisks*

Code

To indicate a span of code, wrap it with backtick quotes (`). Unlike a pre-formatted code block, a code span indicates code within a normal paragraph. For example:

Use the printf() function.

will produce:

Use the printf() function.

To include a literal backtick character within a code span, you can use multiple backticks as the opening and closing delimiters:

There is a literal backtick (`) here.

which will produce this:

There is a literal backtick (`) here.

The backtick delimiters surrounding a code span may include spaces — one after the opening, one before the closing. This allows you to place literal backtick characters at the beginning or end of a code span:

A single backtick in a code span: `

A backtick-delimited string in a code span: `foo`

will produce:

A single backtick in a code span: `

A backtick-delimited string in a code span: `foo`

With a code span, ampersands and angle brackets are encoded as HTML entities automatically, which makes it easy to include example HTML tags. Markdown will turn this:

Please don't use any <blink> tags.

into:

Please don't use any <blink> tags.

You can write this:

&#8212; is the decimal-encoded equivalent of &mdash;.

to produce:

&#8212; is the decimal-encoded equivalent of &mdash;.

Images

Admittedly, it’s fairly difficult to devise a “natural” syntax for placing images into a plain text document format.

Markdown uses an image syntax that is intended to resemble the syntax for links, allowing for two styles: inline and reference.

Inline image syntax looks like this:

Alt text

Alt text

That is:

An exclamation mark: !;
followed by a set of square brackets, containing the alt attribute text for the image;
followed by a set of parentheses, containing the URL or path to the image, and an optional title attribute enclosed in double or single quotes.

Reference-style image syntax looks like this:

Alt text

Where “id” is the name of a defined image reference. Image references are defined using syntax identical to link references:

As of this writing, Markdown has no syntax for specifying the dimensions of an image; if this is important to you, you can simply use regular HTML tags.
Miscellaneous
Automatic Links

Markdown supports a shortcut style for creating “automatic” links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:

http://example.com/

Markdown will turn this into:

http://example.com/

Automatic links for email addresses work similarly, except that Markdown will also perform a bit of randomized decimal and hex entity-encoding to help obscure your address from address-harvesting spambots. For example, Markdown will turn this:

address@example.com

into something like this:

address@exa
mple.com

which will render in a browser as a clickable link to “address@example.com”.

(This sort of entity-encoding trick will indeed fool many, if not most, address-harvesting bots, but it definitely won’t fool all of them. It’s better than nothing, but an address published in this way will probably eventually start receiving spam.)
Backslash Escapes

Markdown allows you to use backslash escapes to generate literal characters which would otherwise have special meaning in Markdown’s formatting syntax. For example, if you wanted to surround a word with literal asterisks (instead of an HTML tag), you can use backslashes before the asterisks, like this:

*literal asterisks*

Markdown provides backslash escapes for the following characters:

\ backslash
` backtick

  • asterisk
    _ underscore
    {} curly braces
    [] square brackets
    () parentheses

    hash mark

  • plus sign
  • minus sign (hyphen)
    . dot
    ! exclamation mark
    md javascriptview raw
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    const cp=x=>JSON.parse(JSON.stringify(x))
    const head=(o=[])=>Object.keys(o[0])
    const sep=(o=[])=>Object.keys(o[0]).fill("-")
    const chunk=(c=[],cols=4)=>c.map((x,y,z)=>y%cols==0 ? z.slice(y,y+cols):null).filter(x=>x)
    const arr2md=(c=[],cols=4)=>{
    let d=chunk(c,cols)
    return [head(d),sep(d),...d]
    .map(x=>x.join('|'))
    .join('\n')
    }
    const json2md=(o=[],)=>{
    d=o.map(x=>Object.values(x))
    return [head(o),sep(o),...d].map(x=>x.join("|")).join("\n")
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    name | 价格 | 数量
    -|-|-
    香蕉 | $1 | 5 |
    苹果 | $1 | 6 |
    草莓 | $1 | 7 |


    name | 111 | 222 | 333 | 444
    - | :-: | :-: | :-: | -:
    aaa | bbb | ccc | ddd | eee|
    fff | ggg| hhh | iii | 000|


    name | 111 | 222 | 333 | 444
    :-: | :-: | :-: | :-: | :-:
    aaa | bbb | ccc | ddd | eee|
    fff | ggg| hhh | iii | 000|
name 价格 数量
香蕉 $1 5
苹果 $1 6
草莓 $1 7
name 111 222 333 444
aaa bbb ccc ddd eee
fff ggg hhh iii 000
name 111 222 333 444
aaa bbb ccc ddd eee
fff ggg hhh iii 000
a b c d

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque hendrerit lacus ut purus iaculis feugiat. Sed nec tempor elit, quis aliquam neque. Curabitur sed diam eget dolor fermentum semper at eu lorem.

NEW: DevDocs now comes with syntax highlighting. http://devdocs.io

Every interaction is both precious and an opportunity to delight.

Do not just seek happiness for yourself. Seek happiness for all. Through kindness. Through mercy.

David LevithanWide Awake
1
alert('Hello World!');
1
[rectangle setX: 10 y: 10 width: 20 height: 20];
Array.map
1
array.map(callback[, thisArg])
_.compactUnderscore.js
1
2
_.compact([0, 1, false, 2, '', 3]);
=> [1, 2, 3]

pullquote

raw

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Read More