From 3e526cb5da1693c3a6ec30caeb1b8cab2eab360b Mon Sep 17 00:00:00 2001 From: Crossy147 Date: Fri, 19 Feb 2016 17:56:09 +0100 Subject: [PATCH 1/5] issue #335 Monad pattern introduced --- monad/index.md | 29 +++++++ monad/pom.xml | 43 ++++++++++ .../src/main/java/com/iluwatar/monad/App.java | 19 +++++ .../src/main/java/com/iluwatar/monad/Sex.java | 5 ++ .../main/java/com/iluwatar/monad/User.java | 39 +++++++++ .../java/com/iluwatar/monad/Validator.java | 81 +++++++++++++++++++ .../test/java/com/iluwatar/monad/AppTest.java | 13 +++ .../java/com/iluwatar/monad/MonadTest.java | 42 ++++++++++ pom.xml | 1 + 9 files changed, 272 insertions(+) create mode 100644 monad/index.md create mode 100644 monad/pom.xml create mode 100644 monad/src/main/java/com/iluwatar/monad/App.java create mode 100644 monad/src/main/java/com/iluwatar/monad/Sex.java create mode 100644 monad/src/main/java/com/iluwatar/monad/User.java create mode 100644 monad/src/main/java/com/iluwatar/monad/Validator.java create mode 100644 monad/src/test/java/com/iluwatar/monad/AppTest.java create mode 100644 monad/src/test/java/com/iluwatar/monad/MonadTest.java diff --git a/monad/index.md b/monad/index.md new file mode 100644 index 000000000..a11360a2e --- /dev/null +++ b/monad/index.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Monad +folder: monad +permalink: /patterns/monad/ +categories: Presentation Tier +tags: + - Java + - Difficulty-Advanced +--- + +## Intent + +Monad pattern based on monad from linear algebra represents the way of chaining operations +together step by step. Binding functions can be described as passing one's output to another's input +basing on the 'same type' contract. + +![alt text](./etc/monad.png "Monad") + +## Applicability + +Use the Monad in any of the following situations + +* when you want to chain operations easily +* when you want to apply each function regardless of the result of any of them + +## Credits +* [Design Pattern Reloaded by Remi Forax](https://youtu.be/-k2X7guaArU) +* [Brian Beckman: Don't fear the Monad](https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads) diff --git a/monad/pom.xml b/monad/pom.xml new file mode 100644 index 000000000..ec5a14a8c --- /dev/null +++ b/monad/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.11.0-SNAPSHOT + + monad + + + junit + junit + test + + + + diff --git a/monad/src/main/java/com/iluwatar/monad/App.java b/monad/src/main/java/com/iluwatar/monad/App.java new file mode 100644 index 000000000..2ab376201 --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/App.java @@ -0,0 +1,19 @@ +package com.iluwatar.monad; + +import java.util.Objects; + +public class App { + + /** + * Program entry point. + * + * @param args @param args command line args + */ + public static void main(String[] args) { + User user = new User("user", 24, Sex.FEMALE, "foobar.com"); + System.out.println(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null") + .validate(User::getName, name -> !name.isEmpty(), "name is empty") + .validate(User::getEmail, email -> !email.contains("@"), "email doesn't containt '@'") + .validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get().toString()); + } +} diff --git a/monad/src/main/java/com/iluwatar/monad/Sex.java b/monad/src/main/java/com/iluwatar/monad/Sex.java new file mode 100644 index 000000000..8b7e43f3c --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/Sex.java @@ -0,0 +1,5 @@ +package com.iluwatar.monad; + +public enum Sex { + MALE, FEMALE +} diff --git a/monad/src/main/java/com/iluwatar/monad/User.java b/monad/src/main/java/com/iluwatar/monad/User.java new file mode 100644 index 000000000..de1be5a45 --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/User.java @@ -0,0 +1,39 @@ +package com.iluwatar.monad; + +public class User { + + private String name; + private int age; + private Sex sex; + private String email; + + /** + * + * @param name - name + * @param age - age + * @param sex - sex + * @param email - email + */ + public User(String name, int age, Sex sex, String email) { + this.name = name; + this.age = age; + this.sex = sex; + this.email = email; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public Sex getSex() { + return sex; + } + + public String getEmail() { + return email; + } +} diff --git a/monad/src/main/java/com/iluwatar/monad/Validator.java b/monad/src/main/java/com/iluwatar/monad/Validator.java new file mode 100644 index 000000000..7cc84298d --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/Validator.java @@ -0,0 +1,81 @@ +package com.iluwatar.monad; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Class representing Monad design pattern. Monad is a way of chaining operations on the + * given object together step by step. In Validator each step results in either success or + * failure indicator, giving a way of receiving each of them easily and finally getting + * validated object or list of exceptions. + * @param Placeholder for an object. + */ +public class Validator { + private final T t; + private final List exceptions = new ArrayList<>(); + + /** + * @param t object to be validated + */ + private Validator(T t) { + this.t = t; + } + + /** + * Creates validator against given object + * + * @param t object to be validated + * @param object's type + * @return new instance of a validator + */ + public static Validator of(T t) { + return new Validator<>(Objects.requireNonNull(t)); + } + + /** + * @param validation one argument boolean-valued function that + * represents one step of validation. Adds exception to main validation exception + * list when single step validation ends with failure. + * @param message error message when object is invalid + * @return this + */ + public Validator validate(Predicate validation, String message) { + if (!validation.test(t)) { + exceptions.add(new IllegalStateException(message)); + } + return this; + } + + /** + * Extension for the {@link Validator#validate(Function, Predicate, String)} method, + * dedicated for objects, that need to be projected before requested validation. + * @param projection function that gets an objects, and returns projection representing + * element to be validated. + * @param validation see {@link Validator#validate(Function, Predicate, String)} + * @param message see {@link Validator#validate(Function, Predicate, String)} + * @param see {@link Validator#validate(Function, Predicate, String)} + * @return this + */ + public Validator validate(Function projection, Predicate validation, + String message) { + return validate(projection.andThen(validation::test)::apply, message); + } + + /** + * To receive validated object. + * + * @return object that was validated + * @throws IllegalStateException when any validation step results with failure + */ + public T get() throws IllegalStateException { + if (exceptions.isEmpty()) { + return t; + } + IllegalStateException e = new IllegalStateException(); + exceptions.forEach(e::addSuppressed); + throw e; + } +} diff --git a/monad/src/test/java/com/iluwatar/monad/AppTest.java b/monad/src/test/java/com/iluwatar/monad/AppTest.java new file mode 100644 index 000000000..5d23b41a6 --- /dev/null +++ b/monad/src/test/java/com/iluwatar/monad/AppTest.java @@ -0,0 +1,13 @@ +package com.iluwatar.monad; + +import org.junit.Test; + +public class AppTest { + + @Test + public void testMain() { + String[] args = {}; + App.main(args); + } + +} diff --git a/monad/src/test/java/com/iluwatar/monad/MonadTest.java b/monad/src/test/java/com/iluwatar/monad/MonadTest.java new file mode 100644 index 000000000..5ef2ecc69 --- /dev/null +++ b/monad/src/test/java/com/iluwatar/monad/MonadTest.java @@ -0,0 +1,42 @@ +package com.iluwatar.monad; + + +import junit.framework.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Objects; + +public class MonadTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testForInvalidName() { + thrown.expect(IllegalStateException.class); + User tom = new User(null, 21, Sex.MALE, "tom@foo.bar"); + Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null").get(); + } + + @Test + public void testForInvalidAge() { + thrown.expect(IllegalStateException.class); + User tom = new User("John", 17, Sex.MALE, "john@qwe.bar"); + Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null") + .validate(User::getAge, age -> age > 21, "user is underaged") + .get(); + } + + @Test + public void testForValid() { + User tom = new User("Sarah", 42, Sex.FEMALE, "sarah@det.org"); + User validated = Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null") + .validate(User::getAge, age -> age > 21, "user is underaged") + .validate(User::getSex, sex -> sex == Sex.FEMALE, "user is not female") + .validate(User::getEmail, email -> email.contains("@"), "email does not contain @ sign") + .get(); + Assert.assertSame(validated, tom); + } +} diff --git a/pom.xml b/pom.xml index c15ca20cf..0b587fd06 100644 --- a/pom.xml +++ b/pom.xml @@ -121,6 +121,7 @@ event-driven-architecture feature-toggle value-object + monad From 72e08365b83911dac55713bfe93a5c7b0a0ea208 Mon Sep 17 00:00:00 2001 From: Crossy147 Date: Fri, 19 Feb 2016 19:08:35 +0100 Subject: [PATCH 2/5] issue #335 documentation improvements --- monad/etc/monad.png | Bin 0 -> 32962 bytes monad/etc/monad.ucls | 54 ++++++++++++++++++ monad/index.md | 10 +++- .../main/java/com/iluwatar/monad/User.java | 9 ++- .../java/com/iluwatar/monad/Validator.java | 12 +++- 5 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 monad/etc/monad.png create mode 100644 monad/etc/monad.ucls diff --git a/monad/etc/monad.png b/monad/etc/monad.png new file mode 100644 index 0000000000000000000000000000000000000000..f82e7a3e45ccd594685571d1e9ed30299fcef93f GIT binary patch literal 32962 zcmb@uWmJ~m*FQ*iOE-vww9-gR2uOE#2uMkHcS$NOAxL*hH&RM>NOw2Px%K;t|1;0L zm|5d17mLMxu5+EU_owz9rXVMYjzWwA1qFrvM(VW^6co%D6cn^G5)AkWotAPc6qIk+ zo7W;Lu4#K-sGjJOw?ZAPi5@hH_Av-lG=a4&R4SIQLkn=ey~nHG!AWk6c&)NZEc$E} zpXzz2H2rg`XJ6dyi`4ZeZtC8SH7bjy4m`B-b~evddC1AVHj{)PS0ObG+mjn3$<**1PR<+1` zUjvTV%0iSbUL-#IL%qgTCrWx42Uuy5pn&Lvl4<&wS<$qd`L*Hnkum)%<`S6GQ~BWF zmbq%nd)l`<&`)U5u6E1TLPrL|t!7T1b=5+R-LbXAxjIa$wA9q?%_+mxE|d1`#uMwrDw!1ngjuKD7-yVeg(Mln`fBR9FQc1TIR)jj#$&o0ar42v$}I9l-gchJpK zNOa=J@tF-g%+YkYOkWP^{tA=PP&_3&^j4;Lpom;^Zp z#hRUIX?xZ-%)K!_sKiWBA}>_P-wKPxVn-Pz$41f6hn}n>NMjIx$hFR8?is^hC8Z5t z>oxYcus+95!hhWKX1^f}`IFKW$GpHvTGO zTv@fI=*A(U(_&2{qM5+Yt?)ccL=y4_Cc!=E%(dV6fuGO)Q>wr+nMDtif?T)NTWzi9 z9IMsyx=YZD51IHQ3E@^JHFR#Faxb#<^|DBCh6$w<}Imb$SS8r7!Sk_14wUkbe=q{cFi>JCBcx3;!ye z*OC4<{v+gJDF;U#A_E(p(iPaz$SosRxuXmy2Wm*@`A4&!LT*|20qwBjgWctUVaZg! z?E;@kU&MGYjq#xmL1AETOzWuoCuw3aHZK*GKO3XoSTGIt=F8WnFh?w;Ko}KkZecct zf2s+bKUW_*RwRNFm#QBRqMz_6LIbq_m@p_&SeuksNVAmScZsEDRNz-h;b25aej^}B zz%rzM6HW^drnCr{hibQ=G*!k6bxc8#OBc8v?upea3a)!BDAPYv2*l2DEkW9WYk*u% z2gPg$Q{~hStW7rLz^^6#9Z?H>_U;2{2!V5fmlDl9GvK{Lud?o=e!dUQi~Fhbvz)Lm z+I0p!d;p`Kv?vDutUAbmv|t_`mjnUBMC3iq4sj|T`8zePPQ2ip=3QiajCJVqlh2P5 z8ng5ABL}Z8*2eZ2dykv0P44chuS+SM@fkBiLKqu^y*e{j#&Tp)Q5|1DT&=O_uB(^o z^tZQ55aX})#c|S`wE4cjDRSSRZ3%mV=)ayI-RW_8QA$I%z0Dr(5RSr zBo%lx^y=|umj?s8(3LR~bK<#n9Bwcbvkk+`kCHK^ip^`%iwj-6xNU`1bnTAj`LaSM zorWdHvEJLGd*7?t+^%zK3kvMFK65X+CYmo*JcVK3pKaJ}2uv|&(ic2li~0KI7hxao z&ZLuQB~Im@zV|f;$Jqj>RX#ai^$S67+RA0-*AV~F_*2G><6fD!m5O>p;Dz&nV@FVUe zYYdc~ht&wqorxmkiDqXT3Yn)Hq~zqK>3nXLY&ci9F4Z+P)=MqovOjRyH)EOK9W9gB zb9}6^7(@Etmj{RLzMi;2pX<1(rtjU5A-?$Cquz8ddC})_Xn7fmj;;yZI@HgS{`@Z9 za2B+mU5ot#QgbH0Drz7m!HBntYQ0+fIn+WzJ7Xw(dH{l3uR zmE~2>7}{DqZPd((ARGOAxSvO7x{a>cE|bL!ltexcZn^GGjuuo2m}-Tf`fY4X+??;i zJQE`>pdI|O+lprgPgA5jcf6jQ%8uH{6H@wR(S4WmWz};Y>)C3-xk`(Ry%nBfwV@wF zCD1Sh{p$KQxExE@y;M60?#CUdkBG1+O~_WEeOfi|ex&oC)GaAJIz6FDgROUA72tEn zv1XLh)Yfjx@P53f3%^X3&XVI+z`=E*++88PJT%wPR1FD9;jyRW;a(+RB4oC}*R6TK z02e}74mt@ElMatsELxqz>}n}8ZAgF#cT58pa_oyQ<=87xvY^6s^Cp#El`FPGtWSFi=<*)Dv} zXTm%T45TZ1-}#U}H^}MCM}o%d%SMm1YM7{juQ?3u;LE{jH7n$O2anOt#j`o8j7tdn zXbZ2*63C8sd%Y>Z(^ZTbqTA$-JQ79u`)EZKlib95fsevaqtj{EtnY#K=^!tmfjRcb z)xeKWVtL3&rqF**Ndc@T<|d?12i{&D-VCy48fAP=A#~qXda-(Ws2_aweqk2nvsXi# z3@Mq-Zwtmkh(znWXiz((zB@fx&4Qt~fPR#(3N zDw_@JEIkz4OmqZH{#T|Bo5%A5)yx9#bb@pE+BE4IH(F+k`zvlWxUd-(B+Y3a9SGW{uuRv*gTf60P>7gocKZ3wp#l19=o|gs@&7qtcFrg=b7mDK;;L&Facp zb_Cr{D1DhrZ}#g{N> z9&+=qt8)1M(;{!>;pq9{lFuEKi{O20Z;wr8&8r;WyG0Mr$Mo~V%Ujng#LU33s*s2i$JtrNfVP- zG#Wncr$faef6kE zXFv24gQ3v;RhUbA&21~|+Zg0gEl|PDIcS^-Rl(jCNomtkFuBhoj-u1_hc~F}{2{ZVS(t?&Fc^RZuo|t=w znXtF#x^n#_F8}Q=>X%2g+4^u46lGY69tdwsfVM|Q+#rraQ#5Uo+x)y5vbvJ zc_Ev@L`I+C;=8D^+p@oYW%O*#)JHt7QUkaBOIwmT2Jtu<@ee|@m*(!_{*|j(aL*VS z2`CP4j@HpoPNBd16IYLhfH*@l$`U7G4)AT+@-_iOKKNqM6fJEWcvaI}#XbN!mF zXW%Y=9D=HPeN}B{8z>HHmvV1RFLev3!C_(9Qo+jSyRD(FqY=cM)@*O?k24a^ zL7FLHmR^RV$JwpxZ(pYzgs!L#){f9P9E@Pl%g*Aes$lh5lQMl?&==gIvffb?weDoUE|rAyTa-zt;RsiX+%Cg&Y9q8?1{ZNLA($~2hf9gy$5E{6o%chE ze}5qs5wMa%4i~O@pB2dFNoo9hIF%bmEC!vPv1ms*A8kWMI+-)^oOm4qaMdueY# z4-r-_eYPzhC1ttgLMydM{TJLS>AL0&nhN1X3l6Q{YM*YCpwUPj`Kvx*C{LratroV~ zwmI9@_9PVcVJeRS+{RR%kdHi07bcI>mrbwXzt@_ud509U4y#{gds;VqTJfFfxa&Xq zVz6BOu-7*@2&ob`25#d1pCRvI+%FP3?|g>`(F0MBOGf<0C~6?FIfs;jHx=YD-%>+Th^~f#&I;`KIKScM~S2BqvwL1Vf)WRqnjM5&cF$ zgF|opC?){lG&R;{ffR&QH#B^Drm}UqS$DDh>&;9s)}{jKjy>$N|Nccbvb)ofunJ?? zCMPRYFFr56mHv3+wcghC&8cjk$I?%qD4vSE+xgfY;IV8Sfgd$ldXM$B`MW?2Ma=rM z+&t!}P{K}J_sauuMa4C^D2J=7YKbgRiw@?mN!2Z9s~>_;-}rAtthGM2_yG9&-NU2q z-9(C41F2Ji_7nq^;o?G5NR6D=&9;$B{+oFB5X>?o^>Ni_GXt1Js}YP}s?kZzP(R14 zS|-Lo6XA~E*G$b)64+yyX5(-R2MpsP=(;I#{Y+AVP`rG9fXZS>w`1D!Wcn*AekHSz z3B8|B!-os;!&+t7*{A* z;6@k0A>4SL4sE3K3w6v;_{?{I2#vScF3in6n$q{t)G^mp{aEq}RpJRz0ztl;ni$pIyxw3!}I;6e*jdkp3F@xp*QYvlD1fZ1lM=)!{ltejzO z^+ZWGxKEF(aKAdzLrUYV%ClV>eiczHD&(Hfk4xM|%HiA-ncd{v1$&;QpS!<^F=CwA2YcPEe@YOhr)W(WEnYxN`j|!CV)TlUZj#7Vba< z2|bm?=%Mpg(5eGXxvDt60l;ebU@ADxbP0a@V9YHkoL@+;< z6)YkV?bDr9-^i~wh{_D=*IIX+Z68wF0oLt4oXzyVuf|so69Z#wstlFzpg(?>Lg>Nh zx0DZk2q?hSBBLb&K6l5O0SGmLK8$BT^mz43Mh!6ql3P_F!`Q) z8rC5g%+5IgLjbNu}fQudg#&9+OXl_p_J4H=3pF zJetS~VZYh6f2k?G{(E1AQj!1HR22&e&L)C?_!iQBrlS6{@$Nztz%L!?YNbjEtdB6O?=<7>ccTz%)h4c*iUt~G~W)o`e z%0=k;(sQ8h;lz^A;~Xr=e6_tGAVGOfQbNVqlHiG6nBw>ZKu zY@2U>$v1gkG!2`uUw4+11q4prM`lF&M{bA)ps`%s0;-uZ6@=7QZ8fIPLI=i%0EFmXxgW2@gon?;fyFZMxz+0(Hcs z)!WO*`=Q@-koFFceM=S~V)!%Uq%}fcoj+H7K9VW4Gh5?j+=~D?t8qXnM60#G`OuNh zpNWWwc(!$nBBR;h@XYlx(`WJ5H&tUgI{wk1m*%y;h_BonAbfao5+6VbdUH@);^{Dr z)@9IV{o!+p)HuaHF{%e=(c|~q7&tpOf6`1G9d-HjGwI|}&4e+`h>XleS&5oeZ+@}1 zL>ttS@Lec|xmGIIgJZ`}6SZT_$(T!m*;j8FTj?=2uMVxCN&Pok@!sZ}n{T(`!DKgg znk@KEUs>#pv<`Qq&&_C7urWn8HKD=)LZ?$7OJkwtUaG-f2*|XCG>|!x90n5FK~Jb+ zvw)WC?vb;d8YUz18%%a-Y~UPK&Qt!%C7Z$q#RM(uFF?D(b@aRcJd2;{Q;MS$BhizW zd7#Z`{0>I13Uf^HuM8VouM&y0DZVm&36nSNX{S?ecv0l4PuS2D(Z=hLKO*_!PC7&Y z=+}SO=)DH(GCXDJeSe-0-q_RFOdp4X`MA(BRiF^n(VZSkZSQC$=XK3k{#rC!!DlA+ zqyO>gC5#UAHm~Zu9^`;LDnm>3o$O3&FF)W{niTdZu`-F&R z2(>2igfP&j5=J_PRtiS=+;BAMegFLMKo+rZ*B_7Qb-Qn7fr`>UIH=UjA33uizD)Y+ z8DxR0?O+rCEYRzf%VsL~h|Og2ip!FXnCszU_fRVLxHQ8$sR8C)A*|i`JL1rY>K8zi zeK$AxmNY=@H=ZMgGw;e$nwVDq{#@tGJ4{M;_JqI7XyoZ#=`30V74t?WDG@&$mEa=~ zugzCLW5Z*lWmqKZ**l;X+N>uhVkWzQ>U%hBJ(OGq!jtE9A|cTB;go~>8=>!eBq!5Z z^#v>BGv)j@{3H#st-!i9-|m67##bOvAx`zItw z0U%iSo(v~rPPmh|cYiKKK}DixCoOH9MYkEKbl@ds@3tnoEFLe^GFyYzhmCEl@#Feb zJHWpY+^)^vCWzg)KrHzn4&&`jhC!;)-Z9!ZTSJyD6Mh*w*4V_e%WJ|LQfAxqO?9Yk z+0N%09WMaLh6V#{n%oLo?ihvqA%~YI?{-#JW`Telgxc$m|H?g~Fw>E;-2Gey!Vqy@ z3FykFY4i?DqZ7M^fphzVOiBWDo@tM&4o4CsTNBDnD6aQuQ8)%9z!gwAe});aT5E~; zr6}n9Oyeafw8(q(?amNMaEB;f_3?OcRVe1n?imu^5OjNAtzaGXjpf|FXkjEl*3i}t zeSEStJ0WxNb|gITV{08X=7-=_o2OVv-2weTAzPa)5tGj}BVZU5dQ3CGWJZBEEBJ}n zC|>EOUYZLSMfnkMCjPILi)0US2(;XLfe=XP(JSA7K6^>R)K($srWF+oQ_@csf9&#chLI5Dw}+ z-@RqGjSl7Q~y z3vn0#j8@5dXKS5qPX^Eb9Jo}7CQa{)+z2fy9)$dSt{@>uu%eS1Gf12qErO3Q*mq=h zB^39ofz0R!IV^)4>JUV!o9 zc{9*JthOxlCm&z~!%=>z)V|*pI*P#5OT{K?5yFKgC7vdI(EACl(3vF6Tx}3K(duBw zn>Q=3bo@mAsNbC&3sBjj9VUr3tBc)xx|(okw$WF*7WZ-f8>#H+{B=e#0szE2I)tAZ zR-6PW%?&n@Ystq~oJbU31ol z%S!Vvo<)EGy-{cfN_zqX>Ljpivgv)_93r`%((k?gdM{1TdUwKb0Oe5@CiYmP;bkI! zzB|=DI|~DB$B#ilyr>j>xXiZ&B_Hf}#)Hj6u-a7GdMQoN zy-cd_^BsADWru(S84v9@0^yl9q1Z(g4K4}PIv2fKAV3i^-Uqhg>f1_V$!;g9K<0F^gbF#qQ9!G0a18U`@XE!0dS@SYbFLp5>6y7M&e^5kki1Sxz(P}D z^+sm-+|a4TN65H4{7n?el$ckk&YP8I7WUB+QSW??M0`~0-;&flKDZ!Kk7T@kEd$&u zOumz&6-CPkPp`QOudgp&sO5+k2cr^1FVx%DX3Yrw`j3IBgP67k^eHro#lhj>o%1Pu zGk%J2-jK84oDQU>#!2xfqgdT|2>`y9k;F#Bh*Uu{^~j|2$pH@6=wvlWoj1TLB@0b) zUfJ{7YWYcc$VriPAda&OGH-8H3JEs`C|@+R!A9Otw%kB|d+o2(hDb;A0~or|pk$h1 z+I@+RuC+@7mHy=N5DAkmRngfht;q%RDb6zkrQrdE&NGCq=jJ z$-Nyw`TVHXMTSMf|6P~gMK%ul%J@Ox`l;mPco&8^;Y zFTNir9RY3ZYS6=qC7)+9eaJ-!5eR}PfAEHsW*7-SQ4su`^KoJvj$L*VeQQZw%^A0= zOLlwl9xurN7#xv~Tk75zz(XN^a$}pPVo*77VT)uy!ZdX%{AnKj3YX2y8|m%v&=7|x ztA*|q&w?)8aNJ8T!H+BIe0!Teh@g8lf&YtfAw^;FtEa?R5)&T-6O)|xXX|KWrS_T> zZ*Nx^Z1=AZpi{*X{$Ag0y~-E^eR1S7=a2sW;mJvdyQ?}A%Mi%(i&(o~9O`fE?{BZJ zqRCutjkSIdk8nI{G}PMI(NYHusijY>R(x3xsZF}&Oo{Br90G=?*X@PfkDqWu9*d5f z(XT(%+ZI4og)NhoXmM@?Enied1*I@gPAC^}J&&QyBIW2s(vo81OerQAwe>WS6p@k| z1;?ozZga%eG)*k#6OPyq-rSx*9?8Nslf?a{D~e557V-CmRICDRdp-c`!2zzYOpumN zW^lT~2d=aUa_(SK#4!uJbms@$=q&wK7T_kVeNiy>d5i{aAaA17xYtO#?){fm2!wT& z-Y%DatJ`6Y*EUgPx!3-5b%kN2AFFL;XF{3x@#=i)`%zXUk!f8|oI^A`szj9#hA$|S zs--%}X9~@7@u|ortzNfS2TaWj#hCg0KM{5|Nq^$x=5JmdPXhs(pKmsUry^Q%M@#Ja z2I)|$koF0~9CaYS%X>Vc>xWpX7H(Ve*M7@3;E*DHZmme=w7ObwnZsIaa$#pgOoP4Y zLb>*NWQ9+J(+8^eMXi{cARM?*hss2?s?jJKAt{UL^U8#i9>}vUXK70 zH3X?9CP3N3BfcrMmeAE+1JM2U{_@@^!dDZO4{ag9WUmZG^JRto+8zKjUrcb3WV(2I znNz!X28<6(sK) z%eokOD?&GVszGYq;bNaRFP1l_p!JkSi+uy%86FI*uK51pr^(C|0ypeFD^NkZtZI$H zT1k!u3ZU%t(bieBpHEl#S<5k6NO{`XwTH&H%(u;Tm%`glWQml-#%BHeP3r!jgvT+$ zW|w2HKPtyHCOKhWHLa)o>oi>^C9#NINdRN?OT`!y$on zZm5G^7bnE%zjFEqs)xIgyM`sIo&(FXomau2$@xHmby{Cd;MjxlCy5*-673$s841OV zBBjL!vUOXBg+l~Y5h%%73d4qdZFE~9{mfPer9w zfhjNIXiX7;N5>Gpvhgx7l1;t>PqYT&=z+asgj!u+HmEX}$DPIuHY~Kq5c7Qln}dA= z{WzqNSQ^<_+XJ0@dzj53#`4@k!vt9$E-^^~{}!5fks5&}>ApJHwdwcjIj)E*U@Qhm z)e_LfG-=SN#!3GD^g^o72lQ9CVVJE_@+;5f(q1sR?X;Yd7**w93$I!Ya#cO5VPSEk z!8(d2x&Dh(H?zw1vzSk|(p&?u&tq**#%7tuW(cJLlLF`A+3~22-D;QBsxiYk4wxah zeH6YD>#LWHR@Lw~~kk}~Vp#r_K5 zl162+1CVWIbJ{V`iB2CFj%G~;FmRFw4kkMiK>RG4dZ4O^cD$0k+LFh55{Udfb;fh) zDDo)uDfWmR2TTaSPH5nTvHC08TgXOvxglR8dLE zs+`c~GcY;Hjr~sXjt#vPLSCXkcWO#&7+n_u6k^ZEj>iGmA4f+9aBVM@l&DLJzg0a# z&s3Ur@`mgYztc^F{jo8~I)(bBdmLm3N|bvGooys*9TYFt5%l~VH*rK#S#+fl7*UY6 zUPFLBA>3bv@I<9nkpb1qC`6nbCE^t!z7rc7bY@K_HiKMFrehzAIZJ{gkmffIiP8kq zmk$nM#$2Di>31w&9Y#5uup| zN5+RYp-O&Wf9x@l$u`TPgNK(mK(|Sm(A>1m`!AbE{|ViL zMH#MXBWkjH76=BBgo0=ioDB&090?Si*+o|_2FVC0G*lL%LsSueE&!tYe%wuWc1}!A zzF->-VZLJR*V8c?Zg>8^O7MJFw|+d}9{_#K6SCaUJ7TIfCvmDL=z06PVi#~zSwx^x z&ez+w&CXJ@v!4OguQTM*sOVUxk{~(7v0S&TE1B^Z_&t!38MduJg0aJV!A#iGC1?_5R?7IZV^h-BITd8Vw& zu{oR`g5#UBRYwd(I{fCfCX;?wdtzCb={*q?!CccR4Pc%#-cr59*w$heXv^K4v*o@7 z4){wdDCUr7Ml1S03%iF)?5wOZ$19SQDKO7Jv2FH!5~S!qVq7P6+=%o|1wtA^uWf5y zk_jt|Kw3_*%Zd^vt_l~9Y2F7ONzXf3*0TIn4%XIrCI$u_E`Exil~%=PjuFtxQwd-qP@83c2$zc6#97-A4}6u{q%@P=@(>K8ek zZitkJvz_FychFVW9_-bc!%Fjn2LnOj6E4aprf3YdK^a_av3pa$p z5#u6l&E2Fr!O;MBZdwD}CrXPnZfjIR;WrnnIF1-CyLe)nhaGw3z-%i?m?i$1m33ff z*zOsNh)9rv0G{yYqZ*`h2Ludd2q*&Z2m*KjKXQQu1u+p;q;d_lhj!=9Xrj5c_@agY z6<16Cbu|y9=_I@A^7&NCUgl}1U6LecPgn-o?s11!`Og`fP51Z>qlUvrX~w@cFE=e z>6~d0Xhhi8y%d>Y1a`XLKO>OmOkgo=629o-7WB?veHtsQKuSBviw4aUkbz!cqB#D& zzKcEw98TC((rNn&uHU@8$YtwdcG53vj=zgl}>zghz309G>8lowm;A32m!^n zH?_u^Ae5rs-~hzqpWQzB8^3db1n_P`VSpC_eTSTYTkIHHijp2)|yyMH!iT=LO^mAThG3WeN^qprXDX`x2L>InvIHXkcsWwblb|kDiy7 zW20Ti+Nx9b=cFOpV}Pt6Ock@BMx;eP=l1|K$w3&O!xd+$#QC@sum2Ag#%+* z7k~&f^mJ)rW&v+R8(<;Tf}Iy}8e#DKRo2U6U_;NSb7#Ebbq0b!rR7-~5W;#y8kR|Q z1xdQ5d&2d(o+?Pxi4IaPG$m)GrWU#)zERY|W6VL=Vc&!7+_QDk5pluwtue3kbTYv^ zyU?texBGvq=6?%bAe5>1m)@;5gG{tcQY1ldu?|tkMRBs|#auoqaIPwCQ+GO=-JXJ9 zbv^D$w2r85X^^8#5gfqY#L~{Vy?hy~+dOFX;VgIYdkuFs6HZEnDCHl=jU_VWKZYBe zQUsNi8`|q^x4h(Esuf?p$dX)vuz*r$X?u)Vxm%I3!WeT>&ASf5q`DnQv}( z-|}ehrB@=utpUXymRpUD7GNDBq9XVqSIG(>9{XebSx(?X^UuNUZyM~S+LP!B#LnG` zQLi-_Xg8Mu81-vH2M!wo5Mmp>y;`$o4&ao1Vz+=ma&_&*V`mUlkHtJ3mzt{CRJ-=w zV;jk3YZQkGIv)~Wl+c8N1FG)~Y}-)kCUB0*t382Zvlu9tRnKhTLm$fxy#+jKZ9$&#c7yMEu;O^Vy+CfW!!z+(@z7q)YBGkn z7T2UuVK!RG3wW&i65?nOjD7*tq}cip0fghKD#BclD-G=ey|ucBhg;3A5u_-iDPl~C z6t&#l(YzbFdEC#8Gq8ICH8VgE1A}bVu_F+g^WC(odt(OwAfm!A%~VofVDXl&N#wpPfKe zf%W^xd4y9?(a>Py=XW{i1BoCw?<*>z$d$8gK>h_K(VCLZS`#z9XZQcI8VMu>sx{I| zx5Gt&$l3ry%7|(_I{8xWmqj?lH}AD(m2%sqiZp+D?@bHi<~c>=GK+p@H@=}3a9~Fe zqfB9vO>(p#qA20ab?51&%VgRb?>_}T2LbE*5adQrw@qLpV@b)PbticJ^(=871bXc0 zxztecBTE6Z%r{sc06yJfCv`)2-*EIMQNy~QZK+b;36KQrkO{*)A6L zPRqH;62nd(fuzyQWc}!41OdEw8?sq{WFQk)G%dB(FK=z(p-EYQ*#+s0>ZAmLf+Q-8Qi?ILcrFP(E8f>7il~BmISWX}$w`QDm1CpV@ z?f19w{Z=Ivd*9$FDSB%)@eMSAeh$6=w0_@`Kb;?wZAQ&toYO`tQ}SS`6^%$&Cyl)a zG9U7bI!&B7_qF@)pQcL4a8LY@9WcZqF?(!l-kAyASEu%Z>~GRO!Tp&fmuz@^e0oXA z`qi*LttZ4?MSJFDITWnn=T)W~z&3TCZ z#9oL0V$xunovI^4)o-dTb6A(&!+h1l3-~1aMooC4L$pXhBk&YPr4e@695+Vo*A^o| zP|66T4(5ROc-S%*d;~^|Qn_vE`|SS*RQbCv6(&ZcLgaH*<{pxg+80~mv()9JU|Y7u z#9`Vw4E@tOvDRP>MEwh236XMs!_3F`H)C%Hb`Xdp2;Z#0gWP3j$;ozomfJT!w@}x} zh^UE!g$Q6?J<&%Bp%?jQC%iv5{RSp+1Fy6!+C(RBt{dRR>flJwS@d%N{eCP1$due!o?ZLx6T1x&uQlc-j<&?f01D668k@FKp2%E7R zW`Ay)Ex%XZCuSIj*!cw`m}D_bz#jjPiN@4`3(W{ExrQm0L2} z$y-g?lo23I&&nn;Y?yWISJtfqGnuc&`SPNbE+Rx##1k|50rYTo3v|o6bBe2O#Im7Kv?YpgC=Anh zk?(|(NidCr4~7##xw&@t$ko26L?`DFt&hV-!TF=jo+?|G@O}z@;53V|Oexx*M z?Y;ysMeDHWXwCn^?mHyH(ooXtYdRohcRy0ZbP|45I0VBJs z=W7A|xCS2V|1`Y!@^lRDZ#2c9e=XP^Y)lg+`R8vZ47XG&)JW5~!`BsjK;-pPXe==K zmezU2^9RzFei&^2BSAH%q%iC`-G?Ju!h*4cf?X155T**N6!n+^AN?Pc3Xu>*p#gM_ zM1W8Xh^mqe1%4<2L8vGY6IcNX{4mE45W1j0hVy@LbT&_KJY6q{TIw9XBYXl}9FS~F za<6#a91n7|c^F*LXEFINh&yNjo%x2B*(?@B{}(7^x4J5cw;;W8-cV^xHYor!c?BO5c0ca zQrWUBwE8cuPhxAVgg~r{;e3!if`@tN z$w%aL-6z*1^#^n^%d@#TZHiZfy19Q>iHTA|*9%g5ncj^W%}v!;yW0@^*WAg=3a>^q zXF4Q2j+W2;V?;z%X%RsUG8(YT8P#NQdchnWghxfYU9X!-nccOpoZr5|QfcF@*JB@6 zEK-XCcHnejKcA=SFAU5p( z{L;;f$DEB~BA@YQqe(CQbf-@K2zXEDgMjPy=`0g$3dFCpLu z=zxJQF?VUm6{EV6YxA@uHMu!Ex~OysD>v%rO{_mi)Th>xIbR{=WF{F_^KJ7Jv&+L-nW@+4C4;mo1KW*A87y+05oEhVA%2> z=q)9|GFVy`*WJ@&^edm2m$$Hk62 z;H8!Uh*Jty2iWmyu;xHi2oN$}XB+mrF_?v}g*LE8CDwiF4*PpwU)Hvulk^uYy` z4keQu`%Z)7reyh3pqWd&*!D_V_=L|8N8XUDk1YrQt0$c-+7GN$hvT9M5=JhKEi(Kt z)P|-eq|Xa3O)fsQw8p?>HVWW03AF{X;V~Y&t<~h-j9`2e@q`(PdXlbwj$#UKB~yJ! zXkZ-{cgcstuLFjk({dEno))!L=E#E?wk`<31#V9k!QU;52Fa7AE--orrYFq|>AB?a z-a&Ti4gD>ZbtUC+wiZL0W1wfPb=$=o{*f*LNhUBwnaK2S3*zLIF>4Vt6n4xWp7z*_ ziwhHDv7L~AWG!i^GIMV!E>?EBz3+a@A|ViG04AlSemKv2pKa;yPSkkP3{#^k0rnK8y!srB>IG8d=d-Yb z=+m}R<+QH$Ag$SCyDB3kN!+3azaPohL!|$HKt`M?i%`LYJ)LJ9saXf|@iQWN=7;Xq9W@8AI zwpU3gkcjTF%4e>Y4RTpevP0YZ-+a&dB;O|c`;Q1$Nx1Effe>k^hD);YQ`Cg9v#8Pi zd_0;=V0s3~&8Z)8G$Y+lI{!V!JxGqn7V1UQ`~vKe2YC^^T~n>iLKizv7hX3)1h<-X zc|G150xDUrc6tiob1sbl1Sf*BeRC+M6kf-zdV5=m*O7+orc+aZTV9;o^j7Np0rCFt z@wiK>59mbL-V7Ut9>=K~%#6_u@`7R9mp~(ijI01(Rx}(jI>8Ya9GZqPleGVwneqSW z{HRre8JWq#H5DLP1R%nro+YtnPJfkezhOcXLNnu{OS;Jhh7S9S_5=1FF#FCfM3YL; z_G%f7WrIErA~HZ)ZbKkMF&UXKo6M>UJBrtYGz zgef&IcY7!=;(%&l>0)A1+~Ty0r4`cg2k}-=3WGQZ_DI?L`u2`}QAK)uwMrQR){1t2 z>^l%=^WVVc&?gEB&t{hD;l5>QYLN(H4`{Zd+TQ-PS6*&#v6RHLNq=!qW0yj?$<}q% z_q@DxtoKa%2QS=^5eDgtVXC6o8(zt5{5P>AZ+nP3Xi)*+NbKP4{a+Jt3HfM$T_yaU zl;gi1@0-q!AY=A4K>iZ$&BVY=F35WvCHleY|G7-7?CT|f|C3vs?-bgJ)3^Wnl{jJr z0@^PTjgdC7I7XOrgQOUrmhOb(#m9Do?_PKZv#ZeTzT>xd1IyeNOD5Qi0zJ(G7{3k@ z{x^{Tt-U;c?{hG>h{mDeZ-&e!-8KQToUX>FCzX49#nTZ~a&pd|@PfW-5mP>otTECTg})jd(ghD~!=kv=2BN>C<4c+E8cxq~?4!TKU=b0AiqJfdn zAk|{P{J}R=F#QmU_k8q5*kOH;Rrqj5(~_7+UXEE_`4i}pBwQc#Dos2UcY8dt38R>+ z1F9AFl3@N9{H#tEJ{J2w@pC4?&*k5T^XRQqr(o}A^AG`kUM0CbvvmIM&N(*L%@RdK zs%gWZvMc?w8xIf%6~+NO476I1{jp4+unoPQlx)QLEAa}+UxQ)dB(<7ruzVXHgEhhX z5656+`!{68CMGq%2Xq3U$+lGqzsBlCD~QgD*5=r>L8u zG*I=}t&>oJxrzeu2nlrJaTH|a)nJX!b%2o;P~~ITDV9T&*pvZ8gd4jUFuvxuZ$vrB z%SL9X&5cZaJ6C6GE>d-P`ae>dBO3$yo)xt~;7bu}z0?z$hWFkEO~$Q85mARMgB7%W z-~*i}&{|l)$tr>G>4*h@3zuA9W!SHo9{SWHA=pHVD34^Q`9%5@%&KJyr1Ds&=#OOV zr*f4%g0SXH6Wu<%J%!~$?5b1jnILXcUO_;E8|Mc^{w~~?0s;uF%@~p~-ak?!cPfIn zb||aK{uvori|P6Yf^$EyLHq}T$3q}E?2Hh<>9-xNcli^k2=9^~ZVT(dfY)a1zDOZX zxGfXLl3TCKTx~hg%it5TkLaw@)?k~1E}M!cEhk4sBoe~iom8~WGPw;XLdAH zR5K8-Bw*<_O$#}+E9A|pF8l90Xk&dkcl3fUu+y?L&q@%?^( z<9S}c*X!|Tr*qEzx$o<~#`}6-?~?)4|EElf3VI%NsaLl1Wi^TT@iVIq_x~w+^&kA5 zs(9t2c(mul@pOu=^YJnB3KkKqlV}$SIWif!CwlX8+dEIUrK{3P)Z=9XY|MF94aV=p z$-gd7zK)<9n+G=FwV5rnCa7*gBygSzD2z7zm zeeH$Gj|_U;wViPUyr;yOH3r&VQ&)?osdK571eMEcwp9;2Wmm}AjB55n$I)G*JvNB- zHR8mJ@F)8E_YJB@@-!+XE6i`24w1BDN!c>0iEB?VbBLC+LH0t*6SyPS1-jRrwu6~j0nm~GyQ}HFGf>g$%CZ~(2nWM$lGByH>!iY(X8HV*6dGrpoX zP3b|LAkWo>cj~j7+liZ0yOc!2ekD)}Il8#y1YP3#pp-;FfP@a<^cKsg62Rcc4Q{?12x zJK^yp(8Z}`T*`c^%vqZ5_%O_+1X`uIF#HoEMYg@+cUeEd?zItI-p?Z2#*BM!AI8GkpKFf|=gyV@OO` z@In*hZdHt)A3Y;}A!gL%IHsz>^VEl-To1%XUk?49j$P>&=P`3eM4ZewUh!Owpp?RA zd#AZOvIxmi1Z`OBM{y=3UaarQS2zefo~eqo89R=tT6JZy)2#`1}~3oJBv ztnmn}wb-V=%8+Wf-$w{rISmpKiPlBycf>7PTXW>G;{Qh9Cw#G*WqSlOr$0`e&>?d> zaCNt|eD~r--E0?53u~Ovx=#P_@T>CEj1a3LGqi`0M8qLmfO|MOm&~yvfV*n{w#?C* zujVInm9;Xj&tGTmnAsYn0018Bd=nO&)~UJ?iaLi44bq_OZD^^T^rIRo1bjTpETYw; zHvWFgU7D*>futV+3`G59kxPdcIWy6Oj`a)`l^+%^0>9=?=+Jd#L@f$oN&Z|UJ6RMo zs=&`utxSf0JA=hM=8hGwXL9CcHh=TSJDA-JLleK(Dp^cAcc~GDhuO|#X`KESFZ>=- zYtgWN_?UVh`qURl>V6rbHqjoxA`GYig2p?2E;Ofu!CrMDKC96~R{>Fb`iB}HGN>~I z9RJq6x94Qf)nkHr{S{4KQdW#AQhxyHFZ$U%4E+ofi7rr~D`3`3=Ly@2_mx{XaRd+b zrpLQpsi|sxZ}vYo8t?|#K3w|s^8C@F9lJyR%UW9B#-6tXXG?$YJ5!xXNy0^-l<2%> zT0;bwhLYTU=eRnQP8|WYHoZv;QuEB5eEO^NF6J4P$goeR+h|Q)v?A8(4PgL=Y5 z^8FpMM=9?F9r8gs$UFaa8Yo%yhupRxPSszvL^3JohbP%b2#U^E5DSrg4ZzD<=$tDl8ZmnD~oq=)KaZ zi8)gZMYtPGQWhW_n*htR9R@ z_}@#6SjTX2C^0@Zp8rTGRh|goyPhK7--pfA$}rSES!)53yKaE!h%&Zp&dgOS-oaLT zitms6D{W7a-$@6)O++Y3k|Wp2$y31Ll9PxaBjWH9oT+eHiCewfiAx9oTHTEBIB^}G z|L=4^MgDO7jAAkzeY%+v6J7p=Jo3L39b&p|OA}4m&@VTxd`@`msBo)1?KDQ?2qYmK zo$#}@=$R_@JfJEPJJQAmg~h&Z#?SH3sXxhn;-&=6={u*m9uQbQ1j;tRFOUg9=O^#} zxc$1ho`P?uRjHJ=TOBr_FVC}4K91;w!|6@O0^R$HikndR5>ta*qp89u3c(`~M~PvY zx@^Iugt)kzjkPtaqQbU?O4G;8AD9G$iK{K!W#fXJT<yO4kZtoQn{4(0>;+KqU8HtgQ$e}zUttFXku-OG!|JfhXx^&fo7 z*{RP$r}>A=oVi*-oK3UwCv}>H8V~d`G1w!EAEER^m}tWx!lrQzy&MXvC&|p=as4qB z_7)AG`&hYcN=Ja7r&YC8By{ta*3%H*UK?nlBGeOQO{C)0W*22$$}Zdr{r_adJtcb z>N%Nunmvy&Hp;?823loMrkI)4(c+Kz0 zd)(hV3!ry*l$Y3cFatNCi-7+IEeRJ*IM9e?mVH$rUc(7*FPbiA`vEku({1c9)*ZL?|# zTB|g8q)*D`%a!{zF;o1YmGc@Ow6sKz+xD}fnx?_y16-H*GME}Kf7s5(VX_f z0blF&#<4!hlV%Dn5_+AxAcg}F`f-)KocAD^T5Ub6Xv2n! z(WIFFn53p=4JP+toZ%0!BHmuQe}XWc-CS!IC#q9X4YP^esUM!2h4q_4f_43k!|ziGm0K-_J&5k#o+Z z$~DXu{aPsf1QMuf%d56P`e(Xqs#$1hp=JPT7?1`5kNNRqf%#(-&_Jy7dc~Qtaj-u^ zrPgM>zxz2})TLrWyAqV5h8@RyFjTGat`bYFB&hUqj)QoB9okIi{+7tM&oUL@cM{0V zRW#V9f2@3~ve!Dp87r6x@_xhRww`>pCQhJhs_Iqjln?la+LN-fGLcfZRJRjjaPZ z-&c|ImFTA|#eZ_^(1@bRdN7I+r{%!tv!dY^@uP@S*Ju3Zjg z|DWF97FJMBQ^XK%bZGO2X2Ez2Z9^>hGQ}zc8cOeA}egOwGj zcW(m|aa~2a+js44h}-9C18w}F%?xey4_wHfG;8I2xz_{NbZfA>NkRESe6)wp$%elX zU;5G%5{AQB*g018A(UxZTqVPVk9dCuT3Q8FTkC)2YaN7Mx@fY!>Y%y_9H*A=ZNQJR zEQLlGRKH>Bpog$&2d2$(19*RbR4x$U?{8i_hb6TFeJyPebvUgSr5lxP@5H#po&n+iHN+(ni)eHt+>{5)X@3gyixnny;|#R zXyF9-MpY9!eAn(SNRQ$@LZG0_Hm&1jW=){Y(}w!@Z(j=|I2U6&W`R&@P1TYJO z2GIZ*zVY+xd9V&Dm~53%QBlttVeUJE1DoQJxOg83yFkn_jvj~DIWdV*(Aq{dK(~5l zb@rkOcMc)m>V4>^u}ovlFQnIbVuy^us1Dr&P-liod6*IKFY)q0&^=O5e0x(}?QSJB zU@~JyqeTlACXfEjUw{zuf8h*SeBmvH4h6)o6Hq${(twz$>HV#v1pGf`DK5CW4&F^S z#Xh*QcX0qD1=i36Sw~=!0z3q$fN2&zV0l`TARd_$UE7dk2cCl^Iy~Ia*mwx+Tv6Hd57Eot-dg&hZ=yMpWoZdVgBi(%IT#2 zt?A<~ZZz2FgF0;7hufSQ?&N{qw4x8ypgf57$q$T*YSJFNV!yVUH}nCMV*NG=cNP0z zdIWEcsrj2fu((6B9H!1SHKRd-=JKLEXoK4?ewpjNw{cp;SK;BQuaCxd107MY|N>1t7@l=vIJL+2eBl0Gp#?c?Re zq;=}-*-7B(fcCm%e=~@+06!)q;{?vLWsztPPfyRc=985Ndse}hWd7w}KRhE^7%WsN zA%z(wSROdjmpP%`-FMvG-4o4(Mc$MFubq~bmPw-@22p>E)`tG$Lk*i=(ks2Yt?mB|ny71iG6(A>oxcK+Lz|)!{Dkmzo zrYNketXy1NU~fnyf9{3JM~2vkoheF5FpjNN=J;t~V0(G;Bah`5AyC&@8pt=R^&yPC z;l%!u-)$F;ryfo}ird1*!695L&-%yLYASx`tRIh$A+%iz$I4BFFh8qOY&deqms!_ax)fs3;0)r-{ZO_`2RS zwUyM&ikDYR2Ma7bu<`Xqi;9X0NA?dEhH&oOF+4y#=oe8F`*2fOh?MQ-?Z!rW#hbz> zZc11;Xui>;Bh%(kAoj93B22-gAi`uVeL4tvT&sRcb8Ub!xJ@VuP-Qc)Hs<3a2V zJBmo%+mKbw?4T-Zofgrt#pv^~=$K5jPcf0&4iIiE2J&+GCuWL-NlBsP-XzSoWeN9IQ?K6~jzbhJrR&@dY zm21?hw}2p|lAr3>4I(fgFfj~7VPR2~JltB2ycN!>^*$p5O8R#q@r`Y51^M}*O$Kn% z0s;axUze9{*4YT=3gIs|H^FOG-MmoAH&Em4St$rl!^%C(|PelUjUi zY(e>i)B^LfvrPjdt{blt5)!7mgnoZ0lb6Vu)2DUx^+hFX+S|XcFAR1^hJ+ZHn9RUB zrlyn?6-D?*M@QiX&(l7B{5UVgIk}^wV|CT`b4GglLU&5ACTmiC2$RwUIyyCXH#dPD z(R1@1zh8Aoa$f{C) z-U2O0V`F1xo^!wVPhEbb!nG2NdXmaZBz`L)0V|z^9f2iuDknR8YpH?SDfYypi!^8~ ztUMY~QPELy+$upN=5^_m)YPu7u5njVEXBvLh7QH%j~}mGxq|uscMku52xw=l&Gqx{IrQAucicf6YnK*Q@^p3tWKWW0U-!w(>Feq3uVuPty9o+b_VuY|Bw<&bhwpmS z$f#{TSnz`U%$YNwu9i^0I8^GgHkT;R=om|mC5N%a3w+M2$-cg)xZcUWE&-v3=4L5r z>5st-a?1i&vDz@E^`SRaCG#pUl)IA^z?7`Y*TM!g+K%paVIJ@4ReJim-rnA>E)Eo` zzq2zZC+FqH`6EAkU@vGY1_lPEW8Evj9mvYc#=7sLzIPo7JZ?aM^@#KgC1rYQDxX2) z>0ySWG$gQ;s>m>9ia?rehf#j6xcusnqgZVwPG%;&UN6_G!=Q$n2#b`f&BZKa?zCNMkoE*GG9G$IY zp1HZ_6JUkCNhXWt&swa zaWyd6YnIy2JfqrK>m8gWN$6e3e%*-d=sa9ZPm99c&_e6+qvGP<;%G7t_D1%|uSXg9 zQ}PWv*cY&In(vJH%Cdxe`XCmD%U`_!2k#Ndajb{H#e9m_!E!o;$qnkSg^ubEt*xV_ z-#e36S|TZr9P#kH`*3we#Vg3FoXh(b0rmbeq2T<=vh%0rX1!YPEpr3m>agK*%G)yd zvt)qf&~>BN%?G+u=yrFtXFnwj4wY`bPa-?Z9?{m^)KrozajSvYUsU`W2L}Njagwm({Lc9O`aSj+&_dF+n7t3-K%~FUGq9gCxTQAgzRw^knq)c^rchAI1s@Wys7QPPMJ$F|L8=JQSx-#tIw{LI0lR1-Szo;p^m$zII(uK+84_1|XAvQu5 z7PN_=i|mn?$9P>_FNkw#XUcO>xE53 zT{=@5Qca)oFE5EbgcbLkUtt<>W_4J%r7^D~~i%hjw4Q?6z_U-&R-lsj$ zC|Yw=lFuotYehwU_ZyL*jg+=BpjGHHYNn>nwR%BYpZnTEV*q9;=#b|ue zYC1V4=7ELS=8Xnmt?q0%7!t;hcMN~)B~0a)jGv)dCGTX83_6ogOcZ%c{p34htOuVb z2BhrQw;vxHnxAJOA{r@o$P^Ma4sKccTHUlU;_eYP(e|3JLr1qR@ym~X<4-&5>6C0^ zqX#=t6z`%@cQVsEY*-&Uu6)ZZ1eKR`$tlpirlUpk@~URbI=>O~y_91Q>?kG;77gwf zV<-6Yge2SuWz*e3x7TIk{jw^)`|h@s(m9;+$byDBPg?NJA#KCTM`vr04ZqUHPBr+IjM8jSqs=8l5Nb#~sFla%(g zvCB`o5g(0u#mDdJT7Iz4qtS?5W%2g5XpfbHgzQ6eD97yA))REj=`$o|%jEADZcaT4 z^JY4M70o}iftDwZJ9yXi(#q5{A~-k5*+g=2uu%N_Faf{IK>Y_#iu3tI88x2X+kWR) z$j!{oT{7^$1lKFx9+d=RIK-6I_3zLI6Cr}ZsN|rKyuh)eVhHl~;W~vjWb5mg%*>>W zjJCz0R4)f*n3IEFP0`SBctdxuC@ZPBWvSb%<;a`E`yMmNS8X|TePBZR(&fuGT6CB}liTWJ5s7cNb7o&JI=1y@0qP~7Vj0?d=Q*kgal zjf;^NUjNzuvn@!C>9Uv085+zOA$V66^twD| zV0f5{^WD3tq9BD*e@dBe-=s}eevIPhrHUemw(nbw+}#>3ILq|gZZ1`~WZShdKjZkK z3VYT%YT;9OQ$A7rM6TQ921*s??IT0-IvSFweJR1|z3`2ac@cik{@K%Wa&^Olak}Qa z5KPSt-1dzYE7>8spZI+YL#ZOcM(dJJy;08%{hz#MGgnGJM%||4A@_RQV152;YZMtS z;k`1)v!$A6DR#KIkd*06Pfyw{jcIeUu$W)`h%B{FpV()nLs#9y!i15Z7P189G)T53 zpR!jn6FI@wR+)@@f-QT6w|+YX$!lnN*R7^b{9vR(F8?V(U!@Gch-XpniaWG*Co(HZ z;n*cAz2bETKPB9=v&nf^5EIkZpL?J9>UG;|pL%;W${ai2h!LfX^!5f*+3eVB7h1M= z8ChD^Of*$=pTs%evZwObS8|;BcO3E+1{BTUbr&dm15kN1wZv-wceLK43~9h)%vYIH>ca{;?Phr)464hLVxc5 zwK_8DjwW7|8a02(Gk$$^(%bb32ASnVgl$yBlIN+!wefwYUOjNqo(F>ZSP zJcl^sYgT1!&&eUUvG>2U`!>}UL;0n{kvh1-)rFIDxHa@F4wt|V%eEI86>HPhR#cJ< z1wB%DYvB10ON1c-q%x)~TGvCVR8<)hT7(fv#yGgk+fz}LF6%eS_?y9__Oo9tYmYBs z=I3{w=?D*LFD_P6bXiVQ(2l9|f9Jfrxq0Hunei0snGWZ_t*W8Mi%4LPQ>rhTjqYpJ zW8Tkh;ETeqx(k2g)_A$H{IaIzmg4xJl^`JOcUvRJhs%$Kp`@gh;DKNGMdvtte>GZr z&;NyM6#EA`K|{I7F&@+IN6(0S_;^*Bj0t+QIAi=rE~1n9>G2s3zE(>DNSfb}B)%|0 z{jn{Q9tyy~OTTwVwyl0^r9lBu&12sy_#VHk9;*vBSpf%aWK@qU#&%^e&4<)+ecju; zCEK|D)sG)Eb2rdNh>u9~El|}IfFdC#hV@RN)wBYEgoL9H{aLePO!K>qLuArm8F{ z(V1x6zY|ifskIP<63RKey$bu5UZ009Mde8du6?@9JQ+ZVKG=T-3DT?8-EFh%0WG=h zQWF8E{06IKTpIMFhiS9LX!!x1Qa7vJY~RlMgn+4H5It%ZJE*sw{S*7U0#R;B+~^zg#m-#8 zR00p;qL$AXwVaG=gOHrt3Pqx-hJzlDtD)W2G8t!FMa9i61&2jsf-GHveSJN)NNA{G zvR1(4@=XMSSy~#6MiV(WOy?SR6^TRb8z^vSZcG{JoZ6~eSI{PAW_AmvOHhoZL0V;R zUjS*Fcz`7?0hPI#nWP7G6w2~Tqtm>erizMzkr6=EACAgA+&yY9x23H7dl%rDPI78% zz^Bd3nE3c0Ec$bvu1!o`YKcCd9F4jpw?33_W(XM~xSoe4EuShY%^*weOG`mzWM-<| zxk#kwuNfmDn4%@0mFQrFP*SRe0yfrNK!jhT@D}!QJiU!)lsuLuHZ~VGt^vqVrh}=L z1NLq-baZ>&!AV-@*$qO;!d+`V4^ckNc-!_MI{JmkRFMl$rM`F<*b9ubx5rLn_h z;2#n313a$k!k6MF3+k0_!{9YS5t-^-(kdz#&xeEaU9-gYk)5J88#lLvv~P+1g8a|& zk0p-xx_Z((Rsd=ueSNjGw9fo(HSZ(G2ev7p4CFH5SZtKKOsM84I-Va|kKliaa@)&~ zcp@eymLZ}r|5fL%h6dPz`h0_*(+m8?PYF?)G07Gp{ zLo;7P!wkyoZo1$rORKgi@0$}!9_R+-=HgR0&G${QYF&r&*nZmhC+?cFok{mGA zvScjKE=>5gy2i+8$_H>aaMGaX%E4(qSd2!*d&&hux<`1fBg(~3XbP+1@P^_?ChU

Qp*B@UIa0E8=T~$H{03}SfCn-%%U6SW5a^l!H&w=0% z_wiYw>u;=$e2EARkQ0p0ExX~X)g15UvXfzNAJ+Xf=3*e~5go=>Z;MVj3R>~`QnEb2 zdgGmF-@#@vM$kfvpu6)-X1TJqv2>H-(6`pqN#3Z3I_{)!&8cKwLWM{K3TR*BF zA)vu)&4YAx+>=Y5LEs&M{Y-veA zPv6u{L`6k>mR*%sbYOz&5=wkhwXW8PGaXYK6g3J%mF_hIC0)r1$2^aaTeiK~ zCN?FrD!%cly1Kcu^UIenABKju=KDDpo#2^@sxUcH%-Dd`xL2qkJEHDrk5HU*A$Kzp zWTLc6)pu8wxW6edf_9EY2uB0lF^Eco1M0Yip04#2H9b8fk&)+fuZkxL5m8rGwY9x; zi2;jdJ+$ba7TOL+i^==!KK}mpj*cB+SA13FAaQMpeB2zVzicp~RjPdJmaJO&c5Bi{ zB!o9o(q_+Autc~pVq)yHlZ}(lAcH+|Lc@8({u&n-9`3THY)up4mP@nr~Y{umEtCEL1>xQhX_VXD9kUX0n zdf31DwBVtS!=j|aurrBE!T|;#oAKr0!|m)68~WfANAKNhuc{JGn`*&1!`=Y3fl^j- zvZLdHGXjwcQZ}^c4n0Am7PI+AerMa@k3?LM6D65s0Lx*p%t;?)9pOhixXmNv_}Tl< z%E^Vp-FttEY@L^HFe|9R-(s;dQ9nu62%S4JI@E>E^`~}>oXnIo=;AOLfO|LPYu~y`@ zT3^uk_FeG7=d@16#(fh3tGPHG(n;~G0z4u{tGhkzJGeTHv5Bp zRmciRNQwbU?7pY|N=m=8^9>|f_pR>Uje(RNa?Z3gJ7}N4T4WB_(mLWQi%QV=axVX| zD0<37Z@P^{WGe2BBb((HdDD}T<6dOuUhR)_)C(;w0l-t6lDFU86mR7}!QWi-hTjW& za$G*^fpvCQPY=|Ax!JQV`<7vsneIbZNrQpG43MjrFAe5!2nlg<&a*_*TKu^Qd!mbl zidtH6yZZ>{=G(B~FYPiGzoGVAcZsRZLFkD51lwK0THwc_@p{aV>DYVrU>2_q4c6j^ zfX~JME!+D?et7@B^>*&*j;H%tj&k@W7MR`0<#vn}ec6Kz33A`z?)+EGpP>nfZ;80o zcV%s+VmC2?M>@utOKdOl4=w`7T4wKfLz;r(>i0PB<&0)2Cp)|9t}7XU8)2FSJ58>M z;j(`on4uLZMd;ec6wYv+S5-~@6;L1`{KDYU`deSKn;$kgjP1L-k=`@v1f)&Or|P*(#eFc$DSv!o<(t1m^5cJ!_W{aIeSjYT5B zBNY|e3PFDO@{G?ReO%WT`vdQLJSlVA-#WQwK16>0^tG94kKk^qpE()VEx(yqKn?lJ zN=o8@4-JlM@mo1D0A@e4?)z;>Uz@Qt;2nhb!v%|%QUN>q zeK0l$+C6FM>4^mep8@qw5kEx674L?M|ANN3mjU(CDzTmKZkk`6B_3e8+Ro0yqaXmR z+6rp--K+n260UI6FsjUL-uTq|!_u{lm!B@#T@Faeyb7(;h^)xSt$5fy2yQusU3mC| zz>g44PF3OPKL^S8kz>xe_SQ?vwEkJopPqxJ>z#l7U|Z$QThn5h0tMTJ&G%%Yx>If# zK`uo_)z-|PC)woD+>jL8p$28FK)vwZ`nuM_JPSOeN`}F!SLBxkAMQHFrfbj@|9wwB zYffcp^Q^z{JMkT<`9;-thQUHHX*r@HckX^^nw5{)P1S&AB-_jZOU#q51ygd6F)zEq+#C8WsFml4MuwX3^A~x3#ldU0X9Zzn)tel_({pZer5+xv6QO zwKe|j#YkuP(HRjD?A&s2Y9_(t07#G$o9n*D6|>%1zvb|K3VrW;qWqsSqt;lWTriSj zY&?>BvhJ3#xPk|F6os0WR@FX@a`D_ZX$)|jnFcUfG-p$Iepgq57sScmX1K4>zl9Lg z0XY6en5r7}4c@J}RM-9Q2Ly^PUxf<6i5Fj8{|f@F->b+OaSd12EL)q74KOg^EPt@_ z7bpZsPF(l@m`?rOb98l@9y@#UfVC_MJC7UcR4l9?Dv*L;VLhrm3Vl;7tX-0Q>e>Yt U?!x_Rm{+kR#bj@0i0C}|Klp&`K>z>% literal 0 HcmV?d00001 diff --git a/monad/etc/monad.ucls b/monad/etc/monad.ucls new file mode 100644 index 000000000..9c98df7c6 --- /dev/null +++ b/monad/etc/monad.ucls @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/monad/index.md b/monad/index.md index a11360a2e..031b899cc 100644 --- a/monad/index.md +++ b/monad/index.md @@ -3,17 +3,21 @@ layout: pattern title: Monad folder: monad permalink: /patterns/monad/ -categories: Presentation Tier +categories: Other tags: - Java - Difficulty-Advanced + - Functional --- ## Intent Monad pattern based on monad from linear algebra represents the way of chaining operations together step by step. Binding functions can be described as passing one's output to another's input -basing on the 'same type' contract. +basing on the 'same type' contract. Formally, monad consists of a type constructor M and two +operations: +bind - that takes monadic object and a function from plain object to monadic value and returns monadic value +return - that takse plain type object and returns this object wrapped in a monadic value. ![alt text](./etc/monad.png "Monad") @@ -25,5 +29,7 @@ Use the Monad in any of the following situations * when you want to apply each function regardless of the result of any of them ## Credits + * [Design Pattern Reloaded by Remi Forax](https://youtu.be/-k2X7guaArU) * [Brian Beckman: Don't fear the Monad](https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads) +* [Monad (functional programming) on Wikipedia] (https://en.wikipedia.org/wiki/Monad_(functional_programming) \ No newline at end of file diff --git a/monad/src/main/java/com/iluwatar/monad/User.java b/monad/src/main/java/com/iluwatar/monad/User.java index de1be5a45..edd299643 100644 --- a/monad/src/main/java/com/iluwatar/monad/User.java +++ b/monad/src/main/java/com/iluwatar/monad/User.java @@ -8,11 +8,10 @@ public class User { private String email; /** - * - * @param name - name - * @param age - age - * @param sex - sex - * @param email - email + * @param name - name + * @param age - age + * @param sex - sex + * @param email - email address */ public User(String name, int age, Sex sex, String email) { this.name = name; diff --git a/monad/src/main/java/com/iluwatar/monad/Validator.java b/monad/src/main/java/com/iluwatar/monad/Validator.java index 7cc84298d..2298a4d8e 100644 --- a/monad/src/main/java/com/iluwatar/monad/Validator.java +++ b/monad/src/main/java/com/iluwatar/monad/Validator.java @@ -11,13 +11,22 @@ import java.util.function.Predicate; * given object together step by step. In Validator each step results in either success or * failure indicator, giving a way of receiving each of them easily and finally getting * validated object or list of exceptions. + * * @param Placeholder for an object. */ public class Validator { + /** + * Object that is validated + */ private final T t; + + /** + * List of exception thrown during validation. + */ private final List exceptions = new ArrayList<>(); /** + * Creates a monadic value of given object. * @param t object to be validated */ private Validator(T t) { @@ -52,6 +61,7 @@ public class Validator { /** * Extension for the {@link Validator#validate(Function, Predicate, String)} method, * dedicated for objects, that need to be projected before requested validation. + * * @param projection function that gets an objects, and returns projection representing * element to be validated. * @param validation see {@link Validator#validate(Function, Predicate, String)} @@ -65,7 +75,7 @@ public class Validator { } /** - * To receive validated object. + * Receives validated object or throws exception when invalid. * * @return object that was validated * @throws IllegalStateException when any validation step results with failure From 64f1fe8979103a11b1d9173f754c403acc7b53f6 Mon Sep 17 00:00:00 2001 From: Crossy147 Date: Sat, 20 Feb 2016 14:55:37 +0100 Subject: [PATCH 3/5] issue #335 typos fixed --- monad/index.md | 2 +- monad/src/test/java/com/iluwatar/monad/MonadTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monad/index.md b/monad/index.md index 031b899cc..d692e18da 100644 --- a/monad/index.md +++ b/monad/index.md @@ -32,4 +32,4 @@ Use the Monad in any of the following situations * [Design Pattern Reloaded by Remi Forax](https://youtu.be/-k2X7guaArU) * [Brian Beckman: Don't fear the Monad](https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads) -* [Monad (functional programming) on Wikipedia] (https://en.wikipedia.org/wiki/Monad_(functional_programming) \ No newline at end of file +* [Monad on Wikipedia](https://en.wikipedia.org/wiki/Monad_(functional_programming) \ No newline at end of file diff --git a/monad/src/test/java/com/iluwatar/monad/MonadTest.java b/monad/src/test/java/com/iluwatar/monad/MonadTest.java index 5ef2ecc69..2c18e25e7 100644 --- a/monad/src/test/java/com/iluwatar/monad/MonadTest.java +++ b/monad/src/test/java/com/iluwatar/monad/MonadTest.java @@ -23,7 +23,7 @@ public class MonadTest { @Test public void testForInvalidAge() { thrown.expect(IllegalStateException.class); - User tom = new User("John", 17, Sex.MALE, "john@qwe.bar"); + User john = new User("John", 17, Sex.MALE, "john@qwe.bar"); Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null") .validate(User::getAge, age -> age > 21, "user is underaged") .get(); @@ -31,7 +31,7 @@ public class MonadTest { @Test public void testForValid() { - User tom = new User("Sarah", 42, Sex.FEMALE, "sarah@det.org"); + User sarah = new User("Sarah", 42, Sex.FEMALE, "sarah@det.org"); User validated = Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null") .validate(User::getAge, age -> age > 21, "user is underaged") .validate(User::getSex, sex -> sex == Sex.FEMALE, "user is not female") From 80ff7bb217ab0b83161ed3cb79e9e343b0e774f8 Mon Sep 17 00:00:00 2001 From: Crossy147 Date: Sat, 20 Feb 2016 15:01:45 +0100 Subject: [PATCH 4/5] issue #335 brace typo --- monad/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monad/index.md b/monad/index.md index d692e18da..9ede8e1b4 100644 --- a/monad/index.md +++ b/monad/index.md @@ -32,4 +32,4 @@ Use the Monad in any of the following situations * [Design Pattern Reloaded by Remi Forax](https://youtu.be/-k2X7guaArU) * [Brian Beckman: Don't fear the Monad](https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads) -* [Monad on Wikipedia](https://en.wikipedia.org/wiki/Monad_(functional_programming) \ No newline at end of file +* [Monad on Wikipedia](https://en.wikipedia.org/wiki/Monad_(functional_programming)) \ No newline at end of file From 81e8d354a967b76f7b9c9511b42c9822a02cc46f Mon Sep 17 00:00:00 2001 From: Crossy147 Date: Sun, 21 Feb 2016 12:10:08 +0100 Subject: [PATCH 5/5] issue #335 review changes --- monad/index.md | 2 +- monad/src/main/java/com/iluwatar/monad/App.java | 16 ++++++++++++++++ .../test/java/com/iluwatar/monad/MonadTest.java | 6 +++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/monad/index.md b/monad/index.md index 9ede8e1b4..82deba922 100644 --- a/monad/index.md +++ b/monad/index.md @@ -17,7 +17,7 @@ together step by step. Binding functions can be described as passing one's outpu basing on the 'same type' contract. Formally, monad consists of a type constructor M and two operations: bind - that takes monadic object and a function from plain object to monadic value and returns monadic value -return - that takse plain type object and returns this object wrapped in a monadic value. +return - that takes plain type object and returns this object wrapped in a monadic value. ![alt text](./etc/monad.png "Monad") diff --git a/monad/src/main/java/com/iluwatar/monad/App.java b/monad/src/main/java/com/iluwatar/monad/App.java index 2ab376201..e330cfa64 100644 --- a/monad/src/main/java/com/iluwatar/monad/App.java +++ b/monad/src/main/java/com/iluwatar/monad/App.java @@ -1,7 +1,23 @@ package com.iluwatar.monad; import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; +/** + * The Monad pattern defines a monad structure, that enables chaining operations + * in pipelines and processing data step by step. + * Formally, monad consists of a type constructor M and two operations: + *
bind - that takes monadic object and a function from plain object to the + * monadic value and returns monadic value. + *
return - that takes plain type object and returns this object wrapped in a monadic value. + *

+ * In the given example, the Monad pattern is represented as a {@link Validator} that takes an instance + * of a plain object with {@link Validator#of(Object)} + * and validates it {@link Validator#validate(Function, Predicate, String)} against given predicates. + *

As a validation result {@link Validator#get()} it either returns valid object {@link Validator#t} + * or throws a list of exceptions {@link Validator#exceptions} collected during validation. + */ public class App { /** diff --git a/monad/src/test/java/com/iluwatar/monad/MonadTest.java b/monad/src/test/java/com/iluwatar/monad/MonadTest.java index 2c18e25e7..ae78572f8 100644 --- a/monad/src/test/java/com/iluwatar/monad/MonadTest.java +++ b/monad/src/test/java/com/iluwatar/monad/MonadTest.java @@ -24,7 +24,7 @@ public class MonadTest { public void testForInvalidAge() { thrown.expect(IllegalStateException.class); User john = new User("John", 17, Sex.MALE, "john@qwe.bar"); - Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null") + Validator.of(john).validate(User::getName, Objects::nonNull, "name cannot be null") .validate(User::getAge, age -> age > 21, "user is underaged") .get(); } @@ -32,11 +32,11 @@ public class MonadTest { @Test public void testForValid() { User sarah = new User("Sarah", 42, Sex.FEMALE, "sarah@det.org"); - User validated = Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null") + User validated = Validator.of(sarah).validate(User::getName, Objects::nonNull, "name cannot be null") .validate(User::getAge, age -> age > 21, "user is underaged") .validate(User::getSex, sex -> sex == Sex.FEMALE, "user is not female") .validate(User::getEmail, email -> email.contains("@"), "email does not contain @ sign") .get(); - Assert.assertSame(validated, tom); + Assert.assertSame(validated, sarah); } }