Skip to content

Commit 5009ad0

Browse files
committed
infix feature description
1 parent 75cf7e1 commit 5009ad0

File tree

3 files changed

+135
-6
lines changed

3 files changed

+135
-6
lines changed

doc/features.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Features overview
22

3+
* [Infix functions](#infix-functions)
34
* [Option type](#option-type)
45
* [Either type](#either-type)
56
* [Foldable](#foldable)
@@ -14,6 +15,12 @@
1415
<!-- * [Type safe cast](#type-safe-cast) -->
1516
<!-- * [Topological](#topological) -->
1617

18+
## Infix functions
19+
20+
In Erlang, we apply functions using prefix notation - name is followed by its arguments in brackets `plus(1, 2)`. Humans are more used to infix operators than prefix or postfix. If a function takes two arguments, we have the option of using it in infix form, where we place it between its first and second arguments `1 /plus/ 2`. To apply a function using infix notation, we enclose its name in slash characters (/). This allows us to use functions as infix operators.
21+
22+
See details about [infix functions](infix.md).
23+
1724

1825
## Option type
1926

doc/infix.md

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Infix functions
2+
3+
In Erlang, we apply functions using prefix notation - name is followed by its arguments in brackets `plus(1, 2)`. Humans are more used to infix operators than prefix or postfix. If a function takes two arguments, we have the option of using it in infix form, where we place it between its first and second arguments `1 /plus/ 2`.
4+
5+
The `parse_transform` feature implements a syntax sugar for *infix*, which is compiled into valid Erlang code using corresponding function at compile-time. To apply a function using infix notation, we enclose its name in slash characters (/). This allows us to use any arity 2 functions as infix operators.
6+
7+
```erlang
8+
-compile({parse_transform, infix}).
9+
10+
1 /plus/ 1.
11+
12+
F /lists:map/ [1, 2, 3].
13+
```
14+
Infix notation does not change a function's behavior, it is purely a syntactic convenience that help readability in a specific situation.
15+
16+
## Operators
17+
18+
The parse transform allows to define custom operator -- a function of arity 2. Just put remote (mod:fun) or local functions enclosed in slashes. The usage of variables as operators is not supported due to confusion with legitimate Erlang expression.
19+
20+
```erlang
21+
%% infix notation
22+
1 /plus/ 1
23+
F /lists:map/ [1, 2, 3].
24+
25+
%% transformed to function calls
26+
plus(1, 1).
27+
lists:map(F, [1, 2, 3])/
28+
```
29+
30+
Example of math operator for tuples
31+
32+
```erlang
33+
example() ->
34+
{1,2,3} /add/ {4,5,6}, %% {5,7,9}
35+
1 /add/ {4,5,6}, %% {5,6,7}
36+
{1,2,3} /add/ 1. %% {2,3,4}
37+
38+
add(X, Y)
39+
when is_tuple(X), is_tuple(Y) ->
40+
list_to_tuple([
41+
A + B || {A, B} <- lists:zip(tuple_to_list(X), tuple_to_list(Y))
42+
]);
43+
44+
add(X, Y)
45+
when is_integer(X), is_tuple(Y) ->
46+
list_to_tuple([
47+
A + X || A <- tuple_to_list(Y)
48+
]);
49+
50+
add(X, Y)
51+
when is_tuple(X), is_integer(Y) ->
52+
list_to_tuple([
53+
A + Y || A <- tuple_to_list(X)
54+
]).
55+
```
56+
57+
User defined operators do not/cloud not/will not introduce any kind of polymorphism or overloading that Erlang does not already implement.
58+
59+
## Partial application
60+
61+
The infix notation supports a partial application, just replace left/right operand with unbound variable _
62+
63+
```erlang
64+
%% partial application
65+
_ /plus/ 1
66+
1 /plus/ _
67+
68+
%% transformed to
69+
fun(X) -> plus(X, 1) end.
70+
fun(X) -> plus(1, X) end.
71+
72+
%% e.g. infix notation to increment elements in array
73+
1 /erlang:'+'/ _ /lists:map/ [1,2,3,4].
74+
```
75+
76+
## Monoid
77+
78+
The infix parse transform allows to define monoid -- an algebraic structure with a single associative binary operation and an identity element. Erlang module with following "behavior" defines a monoid class.
79+
80+
```erlang
81+
%% an identity element
82+
-spec empty() -> _.
83+
84+
%% associative binary operation
85+
-spec append(_, _) -> _.
86+
```
87+
88+
Use module name enclosed in stars to refer it.
89+
90+
```erlang
91+
%% infix notation
92+
1 *plus* 1.
93+
94+
%% translated to function call.
95+
plus:append(1, 1).
96+
```
97+
98+
One of the practical application of monoids -- folding over various data structures. So far, everyone is familiar with folds over lists, but lists aren't the only foldable data structure. We can abstract and define fold over almost any data structure with help of monoids.
99+
100+
```erlang
101+
plus /fold/ [1, 2, 3].
102+
103+
fold(Monoid, []) ->
104+
Monoid:empty();
105+
fold(Monoid, [H | T]) ->
106+
Monoid:append(H, fold(Monoid, T)).
107+
```
108+
109+
## Known limitations
110+
111+
1. **Import** is not supported yet.
112+
113+
```
114+
-import(erlang, ['+'/2,]).
115+
116+
two() ->
117+
1 /'+'/ 1.
118+
```
119+
120+
## References
121+
122+
[1] http://erlang.org/pipermail/erlang-questions/2004-March/011929.html

src/infix.erl

+6-6
Original file line numberDiff line numberDiff line change
@@ -702,27 +702,27 @@ type_list([]) -> [].
702702
%%%------------------------------------------------------------------
703703

704704
hook_infix({op, Line, '*', {op, _, '*', {var, _, '_'}, {atom, _, _} = Op}, R0}) ->
705-
lpartial(Op, Line, uuid(), expr(R0));
705+
lpartial({remote, Line, Op, {atom, Line, append}}, Line, uuid(), expr(R0));
706706

707707
hook_infix({op, Line, '*', {op, _, '*', L0, {atom, _, _} = Op}, {var, _, '_'}}) ->
708-
rpartial(Op, Line, expr(L0), uuid());
708+
rpartial({remote, Line, Op, {atom, Line, append}}, Line, expr(L0), uuid());
709709

710710
hook_infix({op, Line, '*', {op, _, '*', L0, {atom, _, _} = Op}, R0}) ->
711711
L1 = expr(L0),
712712
R1 = expr(R0),
713-
{call, Line, Op, [L1, R1]};
713+
{call, Line, {remote, Line, Op, {atom, Line, append}}, [L1, R1]};
714714

715715

716716
hook_infix({op, Line, '/', {op, _, '/', {var, _, '_'}, {atom, _, _} = Op}, R0}) ->
717-
lpartial({remote, Line, Op, {atom, Line, append}}, Line, uuid(), expr(R0));
717+
lpartial(Op, Line, uuid(), expr(R0));
718718

719719
hook_infix({op, Line, '/', {op, _, '/', L0, {atom, _, _} = Op}, {var, _, '_'}}) ->
720-
rpartial({remote, Line, Op, {atom, Line, append}}, Line, expr(L0), uuid());
720+
rpartial(Op, Line, expr(L0), uuid());
721721

722722
hook_infix({op, Line, '/', {op, _, '/', L0, {atom, _, _} = Op}, R0}) ->
723723
L1 = expr(L0),
724724
R1 = expr(R0),
725-
{call, Line, {remote, Line, Op, {atom, Line, append}}, [L1, R1]};
725+
{call, Line, Op, [L1, R1]};
726726

727727

728728

0 commit comments

Comments
 (0)