├── rebar ├── rebar.config ├── src ├── pa.app.src ├── pa_pt.erl └── pa.erl ├── test └── pa_SUITE.erl ├── README.md └── LICENSE /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erszcz/pa/HEAD/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {deps, 2 | [{proper, ".*", {git, "https://github.com/manopapad/proper.git", "v1.2"}}]}. 3 | -------------------------------------------------------------------------------- /src/pa.app.src: -------------------------------------------------------------------------------- 1 | {application, pa, 2 | [ 3 | {description, "Partial application for Erlang functions"}, 4 | {vsn, git}, 5 | {modules, [ 6 | pa 7 | ]}, 8 | {registered, []}, 9 | {applications, [ 10 | kernel, 11 | stdlib 12 | ]}, 13 | {env, []} 14 | ]}. 15 | -------------------------------------------------------------------------------- /src/pa_pt.erl: -------------------------------------------------------------------------------- 1 | -module(pa_pt). 2 | 3 | -export([parse_transform/2]). 4 | 5 | -ifndef(max_partial_arity). 6 | -define(max_partial_arity, 15). 7 | -endif. 8 | 9 | -define(FUNCTION, bind). 10 | 11 | parse_transform(Forms, _Options) -> 12 | module(Forms, []). 13 | 14 | module([{eof, _}=Eof], Acc) -> 15 | lists:reverse(Acc) ++ functions(?max_partial_arity) ++ [Eof]; 16 | module([{attribute, _, module, _}=Module|Tail], Acc) -> 17 | module(Tail, [exports(?max_partial_arity), Module | Acc]); 18 | module([Head|Tail], Acc) -> 19 | module(Tail, [Head|Acc]). 20 | 21 | exports(MaxArity) -> 22 | {attribute, 0, export, 23 | [{?FUNCTION, AppliedArgs+1} || AppliedArgs <- lists:seq(0, MaxArity)]}. 24 | 25 | functions(MaxArity) -> 26 | [function(AppliedArgs, MaxArity) || AppliedArgs <- lists:seq(0, MaxArity)]. 27 | 28 | function(AppliedArgs, MaxArity) -> 29 | Clauses = [clause(FunArity, AppliedArgs) 30 | || FunArity <- lists:seq(AppliedArgs, MaxArity)], 31 | AllClauses = Clauses ++ [badarg_clause(AppliedArgs)], 32 | {function, 0, ?FUNCTION, AppliedArgs+1, AllClauses}. 33 | 34 | badarg_clause(AppliedArgs) -> 35 | {clause, 0, 36 | [{var, 0, '_'} || _ <- lists:seq(0, AppliedArgs)], 37 | [], 38 | [{call, 0, {atom, 0, error}, [{atom, 0, badarg}]}]}. 39 | 40 | clause(FunArity, AppliedArgs) -> 41 | {clause, 0, 42 | clause_header(AppliedArgs), 43 | clause_guard(FunArity), 44 | clause_body(FunArity, AppliedArgs)}. 45 | 46 | clause_header(AppliedArgs) -> 47 | [{var, 0, 'Fun'} | args(1, AppliedArgs)]. 48 | 49 | clause_guard(FunArity) -> 50 | [[{call, 0, 51 | {atom, 0, 'is_function'}, 52 | [{var, 0, 'Fun'}, {integer, 0, FunArity}]}]]. 53 | 54 | clause_body(FunArity, AppliedArgs) -> 55 | [{'fun', 0, 56 | {clauses, 57 | [{clause, 0, 58 | args(AppliedArgs+1, FunArity), 59 | [], 60 | [{call, 0, 61 | {var, 0, 'Fun'}, 62 | args(1, FunArity)}]}]}}]. 63 | 64 | args(From, To) -> 65 | [{var, 0, arg(N)} || N <- lists:seq(From, To)]. 66 | 67 | arg(N) -> 68 | list_to_atom("A" ++ integer_to_list(N)). 69 | -------------------------------------------------------------------------------- /test/pa_SUITE.erl: -------------------------------------------------------------------------------- 1 | -module(pa_SUITE). 2 | -compile([export_all]). 3 | 4 | -include_lib("common_test/include/ct.hrl"). 5 | -include_lib("proper/include/proper.hrl"). 6 | -include_lib("eunit/include/eunit.hrl"). 7 | 8 | -define(eq(Expected, Actual), ?assertEqual(Expected, Actual)). 9 | 10 | all() -> 11 | [bind, 12 | invalid_bind]. 13 | 14 | init_per_suite(Config) -> 15 | Config. 16 | 17 | end_per_suite(_Config) -> 18 | ok. 19 | 20 | init_per_testcase(_TestCase, Config) -> 21 | Config. 22 | 23 | end_per_testcase(_TestCase, _Config) -> 24 | ok. 25 | 26 | %% 27 | %% Tests 28 | %% 29 | 30 | %% Partial application whose result is an N-ary function. 31 | bind(Config) -> 32 | property(bind, ?FORALL({{Arity, Fun}, Args}, fun_args(), 33 | is_applied_properly(Arity, Fun, Args))). 34 | 35 | %% Error when the input function arity or number of args make it impossible 36 | %% to get an N-ary function. 37 | invalid_bind(Config) -> 38 | property(invalid_bind, ?FORALL({{_, Fun}, Args}, invalid_fun_args(), 39 | is_not_applied(Fun, Args))). 40 | 41 | %% 42 | %% Generators 43 | %% 44 | 45 | -define(MAX_FUN_ARITY, 10). 46 | 47 | %% Partially applying Fun to Args should give a fun of arity N. 48 | fun_args() -> 49 | ?SUCHTHAT({{Arity, _Fun}, Args}, {function(), args()}, 50 | Arity - length(Args) >= 0). 51 | 52 | invalid_fun_args() -> 53 | ?SUCHTHAT({{Arity, _Fun}, Args}, {function(), args()}, 54 | Arity - length(Args) < 0). 55 | 56 | function() -> 57 | ?LET(Arity, fun_arity(), function(Arity)). 58 | 59 | fun_arity() -> integer(0, ?MAX_FUN_ARITY). 60 | 61 | function(0) -> {0, fun () -> 0 end}; 62 | function(1) -> {1, fun (_) -> 1 end}; 63 | function(2) -> {2, fun (_,_) -> 2 end}; 64 | function(3) -> {3, fun (_,_,_) -> 3 end}; 65 | function(4) -> {4, fun (_,_,_,_) -> 4 end}; 66 | function(5) -> {5, fun (_,_,_,_,_) -> 5 end}; 67 | function(6) -> {6, fun (_,_,_,_,_,_) -> 6 end}; 68 | function(7) -> {7, fun (_,_,_,_,_,_,_) -> 7 end}; 69 | function(8) -> {8, fun (_,_,_,_,_,_,_,_) -> 8 end}; 70 | function(9) -> {9, fun (_,_,_,_,_,_,_,_,_) -> 9 end}; 71 | function(10) -> {10, fun (_,_,_,_,_,_,_,_,_,_) -> 10 end}. 72 | 73 | args() -> 74 | ?LET(Arity, fun_arity(), lists:seq(1, Arity)). 75 | 76 | %% 77 | %% Helpers 78 | %% 79 | 80 | property(Name, Prop) -> 81 | Props = proper:conjunction([{Name, Prop}]), 82 | ?assert(proper:quickcheck(Props, [verbose, long_result, 83 | {numtests, 100}, 84 | {constraint_tries, 200}])). 85 | 86 | is_not_applied(Fun, Args) -> 87 | try 88 | apply(pa, bind, [Fun|Args]), 89 | false 90 | catch error:badarg -> 91 | true 92 | end. 93 | 94 | is_applied_properly(Arity, Fun, Args) -> 95 | Result = apply(pa, bind, [Fun|Args]), 96 | RemainingArity = Arity - length(Args), 97 | RemainingArgs = lists:seq(length(Args)+1, Arity), 98 | is_function(Result, RemainingArity) andalso 99 | apply(Result, RemainingArgs) =:= Arity. 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Partial application of Erlang functions 2 | 3 | ## Trivial example 4 | 5 | An example says more than a thousand words: 6 | 7 | ```erlang 8 | > Times2 = pa:bind(fun erlang:'*'/2, 2). 9 | #Fun 10 | > lists:map(Times2, [1,2,3]). 11 | [2,4,6] 12 | ``` 13 | 14 | ## Real world example 15 | 16 | Take [an example from MongooseIM (actually a legacy leftover from ejabberd)][1] - 17 | this _inline fun_ is 55 lines long! 18 | 19 | [1]: https://github.com/esl/MongooseIM/blob/4211e4ca6e11d60cccfc0da16f08ba0c0d8e4a94/apps/ejabberd/src/mod_muc_room.erl#L2225-L2281 20 | 21 | The fun code has problems with indentation, since the body of the 22 | inline fun should be indented more than the outer code and using `case`s 23 | and `if`s increases the indentation level even more. 24 | 25 | This fun is complex and not easily traceable, because we don't know its real 26 | name (at least without referring to `erlc -S`) - in case of a fun this 27 | long, its logic might not be obvious, so tracing and seeing the return 28 | value might be really helpful. 29 | 30 | A more general example of the same problem looks like this: 31 | 32 | ```erlang 33 | do_something_on_a_list_of_items(ListOfItems) -> 34 | SomeVar1 = get_some_var1(), 35 | SomeVar2 = get_some_var2(), 36 | SomeVar3 = get_some_var3(), 37 | lists:map(fun (Elem) -> 38 | %% very long closure using variables 39 | %% SomeVar1, SomeVar2, SomeVar3 closed over 40 | %% from the outer environment 41 | end, ListOfItems). 42 | ``` 43 | 44 | I hope you're convinced that refactoring is necessary. 45 | 46 | ```erlang 47 | do_something_on_a_list_of_items(ListOfItems) -> 48 | SomeVar1 = get_some_var1(), 49 | SomeVar2 = get_some_var2(), 50 | SomeVar3 = get_some_var3(), 51 | lists:map(mk_step(SomeVar1, SomeVar2, SomeVar3), ListOfItems). 52 | 53 | mk_step(SomeVar1, SomeVar2, SomeVar3) -> 54 | fun (Elem) -> 55 | do_something_with_one_item(SomeVar1, SomeVar2, SomeVar3, Elem) 56 | end. 57 | 58 | do_something_with_one_item(SomeVar1, SomeVar2, SomeVar3, Elem) -> 59 | %% still a long function using variables 60 | %% SomeVar1, SomeVar2, SomeVar3 61 | %% passed in as arguments 62 | ... 63 | ``` 64 | 65 | The end result is functionally the same, 66 | but `do_something_with_one_item/4` is perfectly traceable and doesn't suffer 67 | from _pathological indentosis_. 68 | 69 | However, `mk_step/3` from above is just boilerplate. 70 | The same could be achieved without proliferation of similar `mk_sth/3` 71 | and `mk_sth_else/5` functions throughout the codebase with function partial 72 | application. Which Erlang lacks! 73 | 74 | Thankfully, this library fixes this nuisance by providing a quite 75 | convenient (as far as the syntax permits) implementation: 76 | 77 | ```erlang 78 | do_something_on_a_list_of_items(ListOfItems) -> 79 | SomeVar1 = get_some_var1(), 80 | SomeVar2 = get_some_var2(), 81 | SomeVar3 = get_some_var3(), 82 | lists:map(pa:bind(fun do_something_with_one_item/4, 83 | SomeVar1, SomeVar2, SomeVar3), ListOfItems). 84 | 85 | do_something_with_one_item(SomeVar1, SomeVar2, SomeVar3, Elem) -> 86 | %% still a long function using variables 87 | %% SomeVar1, SomeVar2, SomeVar3 88 | %% passed in as arguments 89 | ... 90 | ``` 91 | 92 | ## There is more than that! 93 | 94 | Of course, `map/2` and `foreach/2` require unary functions, 95 | but `foldl/3` a binary one and you might come up with a number of 96 | functions with **even wilder** requirements! 97 | 98 | You can define the maximum supported arity by defining the value of the 99 | ```max_partial_arity``` macro during compilation. 100 | 101 | Defining it in your ```rebar.config``` file is a pretty convenient way to do so. 102 | 103 | The default value is **15**. 104 | -------------------------------------------------------------------------------- /src/pa.erl: -------------------------------------------------------------------------------- 1 | -module(pa). 2 | 3 | -compile({parse_transform, pa_pt}). 4 | 5 | % Parse transform generates set of functions bind/1-(N+1) 6 | % that will be exported from this module. 7 | % 8 | % Value of N is set via macro 'max_partial_arity'. 9 | % Default value is 15. 10 | % 11 | % The generated set of functions is equivalent to the following example 12 | % (for N=3) accompanied by the corresponding AST that should be helpful 13 | % to understand the parse transform. 14 | % 15 | %bind(Fun) when is_function(Fun, 0) -> 16 | % fun() -> Fun() end; 17 | %bind(Fun) when is_function(Fun, 1) -> 18 | % fun(A1) -> Fun(A1) end; 19 | %bind(Fun) when is_function(Fun, 2) -> 20 | % fun(A1, A2) -> Fun(A1, A2) end; 21 | %bind(Fun) when is_function(Fun, 3) -> 22 | % fun(A1, A2, A3) -> Fun(A1, A2, A3) end; 23 | %bind(_) -> 24 | % error(badarg). 25 | % 26 | %bind(Fun, A1) when is_function(Fun, 1) -> 27 | % fun() -> Fun(A1) end; 28 | %bind(Fun, A1) when is_function(Fun, 2) -> 29 | % fun(A2) -> Fun(A1, A2) end; 30 | %bind(Fun, A1) when is_function(Fun, 3) -> 31 | % fun(A2, A3) -> Fun(A1, A2, A3) end; 32 | %bind(_, _) -> 33 | % error(badarg). 34 | % 35 | %bind(Fun, A1, A2) when is_function(Fun, 2) -> 36 | % fun() -> Fun(A1, A2) end; 37 | %bind(Fun, A1, A2) when is_function(Fun, 3) -> 38 | % fun(A3) -> Fun(A1, A2, A3) end; 39 | %bind(_, _, _) -> 40 | % error(badarg). 41 | % 42 | %bind(Fun, A1, A2, A3) when is_function(Fun, 3) -> 43 | % fun() -> Fun(A1, A2, A3) end; 44 | %bind(_, _, _, _) -> 45 | % error(badarg). 46 | % 47 | %[{attribute,1,file,{"src/pa.erl",1}}, 48 | % {attribute,1,module,pa}, 49 | % {function,5,bind,1, 50 | % [{clause,5, 51 | % [{var,5,'Fun'}], 52 | % [[{call,5,{atom,5,is_function},[{var,5,'Fun'},{integer,5,0}]}]], 53 | % [{'fun',6, 54 | % {clauses,[{clause,6,[],[],[{call,6,{var,6,'Fun'},[]}]}]}}]}, 55 | % {clause,7, 56 | % [{var,7,'Fun'}], 57 | % [[{call,7,{atom,7,is_function},[{var,7,'Fun'},{integer,7,1}]}]], 58 | % [{'fun',8, 59 | % {clauses, 60 | % [{clause,8, 61 | % [{var,8,'A1'}], 62 | % [], 63 | % [{call,8,{var,8,'Fun'},[{var,8,'A1'}]}]}]}}]}, 64 | % {clause,9, 65 | % [{var,9,'Fun'}], 66 | % [[{call,9,{atom,9,is_function},[{var,9,'Fun'},{integer,9,2}]}]], 67 | % [{'fun',10, 68 | % {clauses, 69 | % [{clause,10, 70 | % [{var,10,'A1'},{var,10,'A2'}], 71 | % [], 72 | % [{call,10, 73 | % {var,10,'Fun'}, 74 | % [{var,10,'A1'},{var,10,'A2'}]}]}]}}]}, 75 | % {clause,11, 76 | % [{var,11,'Fun'}], 77 | % [[{call,11,{atom,11,is_function},[{var,11,'Fun'},{integer,11,3}]}]], 78 | % [{'fun',12, 79 | % {clauses, 80 | % [{clause,12, 81 | % [{var,12,'A1'},{var,12,'A2'},{var,12,'A3'}], 82 | % [], 83 | % [{call,12, 84 | % {var,12,'Fun'}, 85 | % [{var,12,'A1'}, 86 | % {var,12,'A2'}, 87 | % {var,12,'A3'}]}]}]}}]}, 88 | % {clause,13, 89 | % [{var,13,'_'}], 90 | % [], 91 | % [{call,14,{atom,14,error},[{atom,14,badarg}]}]}]}, 92 | % {function,16,bind,2, 93 | % [{clause,16, 94 | % [{var,16,'Fun'},{var,16,'A1'}], 95 | % [[{call,16,{atom,16,is_function},[{var,16,'Fun'},{integer,16,1}]}]], 96 | % [{'fun',17, 97 | % {clauses, 98 | % [{clause,17,[],[], 99 | % [{call,17,{var,17,'Fun'},[{var,17,'A1'}]}]}]}}]}, 100 | % {clause,18, 101 | % [{var,18,'Fun'},{var,18,'A1'}], 102 | % [[{call,18,{atom,18,is_function},[{var,18,'Fun'},{integer,18,2}]}]], 103 | % [{'fun',19, 104 | % {clauses, 105 | % [{clause,19, 106 | % [{var,19,'A2'}], 107 | % [], 108 | % [{call,19, 109 | % {var,19,'Fun'}, 110 | % [{var,19,'A1'},{var,19,'A2'}]}]}]}}]}, 111 | % {clause,20, 112 | % [{var,20,'Fun'},{var,20,'A1'}], 113 | % [[{call,20,{atom,20,is_function},[{var,20,'Fun'},{integer,20,3}]}]], 114 | % [{'fun',21, 115 | % {clauses, 116 | % [{clause,21, 117 | % [{var,21,'A2'},{var,21,'A3'}], 118 | % [], 119 | % [{call,21, 120 | % {var,21,'Fun'}, 121 | % [{var,21,'A1'}, 122 | % {var,21,'A2'}, 123 | % {var,21,'A3'}]}]}]}}]}, 124 | % {clause,22, 125 | % [{var,22,'_'},{var,22,'_'}], 126 | % [], 127 | % [{call,23,{atom,23,error},[{atom,23,badarg}]}]}]}, 128 | % {function,25,bind,3, 129 | % [{clause,25, 130 | % [{var,25,'Fun'},{var,25,'A1'},{var,25,'A2'}], 131 | % [[{call,25,{atom,25,is_function},[{var,25,'Fun'},{integer,25,2}]}]], 132 | % [{'fun',26, 133 | % {clauses, 134 | % [{clause,26,[],[], 135 | % [{call,26, 136 | % {var,26,'Fun'}, 137 | % [{var,26,'A1'},{var,26,'A2'}]}]}]}}]}, 138 | % {clause,27, 139 | % [{var,27,'Fun'},{var,27,'A1'},{var,27,'A2'}], 140 | % [[{call,27,{atom,27,is_function},[{var,27,'Fun'},{integer,27,3}]}]], 141 | % [{'fun',28, 142 | % {clauses, 143 | % [{clause,28, 144 | % [{var,28,'A3'}], 145 | % [], 146 | % [{call,28, 147 | % {var,28,'Fun'}, 148 | % [{var,28,'A1'}, 149 | % {var,28,'A2'}, 150 | % {var,28,'A3'}]}]}]}}]}, 151 | % {clause,29, 152 | % [{var,29,'_'},{var,29,'_'},{var,29,'_'}], 153 | % [], 154 | % [{call,30,{atom,30,error},[{atom,30,badarg}]}]}]}, 155 | % {function,32,bind,4, 156 | % [{clause,32, 157 | % [{var,32,'Fun'},{var,32,'A1'},{var,32,'A2'},{var,32,'A3'}], 158 | % [[{call,32,{atom,32,is_function},[{var,32,'Fun'},{integer,32,3}]}]], 159 | % [{'fun',33, 160 | % {clauses, 161 | % [{clause,33,[],[], 162 | % [{call,33, 163 | % {var,33,'Fun'}, 164 | % [{var,33,'A1'}, 165 | % {var,33,'A2'}, 166 | % {var,33,'A3'}]}]}]}}]}, 167 | % {clause,34, 168 | % [{var,34,'_'},{var,34,'_'},{var,34,'_'},{var,34,'_'}], 169 | % [], 170 | % [{call,35,{atom,35,error},[{atom,35,badarg}]}]}]}, 171 | % {eof,36}] 172 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------