diff --git a/README.md b/README.md index 374ccda..bb1e24d 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,17 @@ -# A more-or-less functional util library for general use. Now with added code generation. +# More-or-less functional util library for Java. -Code generation is highly recommended since it provides kind of poor-mans-first-class-functions in regular java code, thus greatly lessening the pain of using functional constructs. See examples for more info. +Code generation (https://github.com/solita/meta-utils) is highly recommended since it provides first-class-functions in regular java code, thus greatly lessening the pain of using functional constructs. ## Installing -> git clone git://github.com/solita/functional-utils.git +Add the following file (or the latest version) to classpath: -and use as a regular java project. Eclipse project/classpath files are included. +https://github.com/solita/functional-utils/releases/download/0.8/functional-utils-0.8.jar -...or include as a jar file in the classpath. Bundled pom.xml can be used to package the jar, or just use [the latest ready-made file](functional-utils.jar) - -You might also want to use *Eclipse favorites* (any similar thing in Idea?) so that you never have to write a single import clause. +You might also want to use *Eclipse favorites* (any similar thing in Idea?) so that you don't jave to write so many import clauses. Add the following types/functions to *Eclipse Preferences -> Java -> Editor -> Content Assist -> Favorites*: ![](Favorites.png) -If (and when) you want to use the standard Java library as first class functions, add [the ready-made metaclasses for JavaSE6](javase6meta.jar) to your classpath. -TODO: JavaSE7. - - -## Code generation - -### Eclipse (tested in Juno) - -Create (or open an existing) Java project. - -Add functional-utils as a project dependency (as a jar file or a project): -![](JavaBuildPath.png) - -Project properties -> Java Compiler -> Annotation Processing: - -![](AnnotationProcessing.png) - -Project properties -> Java Compiler -> Annotation Processing -> Factory Path: - -![](FactoryPath.png) - -Now whenever you save a file the metadata classes are automatically generated and immediately ready for use. - -### IntelliJ Idea - -TODO: Anyone know how Idea supports Annotation Processors? - -### Maven/Gradle/... - -Please consult the documentation of your build tool on how to enable and control annotation processing. - -### Metaclasses for 3rd party libraries? - -Since I was able to generate the metaclasses for the standard Java library, -there's a good chance that it will also work for whichever 3rd party library where the source code is available. - - ## Examples Just a few examples of using these utils. @@ -122,15 +77,15 @@ Just a few examples of using these utils. // ... Ordering> byNaturalOrdering = Compare.byNatural(); - Ordering byComparable = Compare.by(Employee_.salary); - Ordering byExplicitComparator = Compare.by(Employee_.salary, Ordering.Natural()); + Ordering byComparable = Compare.by(salary); + Ordering byExplicitComparator = Compare.by(salary, Ordering.Natural()); Ordering<_1>> by_1 = Compare.by_1; List> listOfTuples = newList(); sort(listOfTuples, by_1); // Employee does not implement comparable, but we can first map to _2 and then to salary - sort(listOfTuples, Compare.by(Tuple2_._2().andThen(Employee_.salary))); + sort(listOfTuples, Compare.by(Transformers._2().andThen(salary))); // sorted by the contents of an iterable sort(Collections.>newList(), Compare.byIterable()); @@ -139,12 +94,12 @@ Just a few examples of using these utils. sort(Collections.>newList(), Compare.byOption()); // sorted by a function to an Option - sort(Collections.newList(), Compare.byOption(Employee_.name)); + sort(Collections.newList(), Compare.byOption(name)); // and the same with explicit comparators - sort(Collections.>newList(), Compare.byIterable(Compare.by(Employee_.salary))); - sort(Collections.>newList(), Compare.byOption(Compare.by(Employee_.salary))); - sort(Collections.newList(), Compare.byOption(Employee_.name, Ordering.Natural())); + sort(Collections.>newList(), Compare.byIterable(Compare.by(salary))); + sort(Collections.>newList(), Compare.byOption(Compare.by(salary))); + sort(Collections.newList(), Compare.byOption(name, Ordering.Natural())); ### fi.solita.utils.functional.Tuple @@ -152,78 +107,79 @@ Just a few examples of using these utils. Map.Entry tuple2extendsMapEntry = Tuple.of("a", "b"); Tuple2 pairExtendsTuple2 = Pair.of("left", "right"); // ... until Tuple22 - + // a List of common supertype List strings = Tuple.asList(Tuple.of("str1", "str2")); List objects = Tuple.asList(Tuple.of(42, "foo")); List numbers = Tuple.asList(Tuple.of(42, 42l)); - + Object[] values = Tuple.of("str").toArray(); - + Tuple3 tuple = Tuple.of(42, "b", true); String valueByFieldAccess = tuple._2; - + Pair pair = Pair.of(42, "right"); String right = pair.right; int left = pair.left; - + Tuple2 appendedRight = Tuple.of("str").append(42); Tuple3 prependedLeft = appendedRight.prepend(true); - + Tuple4 bigtuple = Tuple.of(42, "str", true, new Object()); Tuple2 prefix2 = bigtuple.take2(); Tuple3 prefix3 = bigtuple.take3(); - + List> listOfTuples = newList(); - Iterable projection = map(listOfTuples, Tuple_._2_.get_2()); + Iterable projection = map(listOfTuples, Transformers._2()); ### fi.solita.utils.functional.Function - static int constant() { - return 42; - } - - static int length(String s) { - return s.length(); - } - - static int mod(int modulus, int i) { - return i % modulus; - } + static Transformer length = new Transformer() { + @Override + public Integer transform(String source) { + return source.length(); + } + }; + + static Function2 mod = new Function2() { + @Override + public Integer apply(Integer modulus, Integer i) { + return i % modulus; + } + }; //... // Apply is a function from T to R Apply f = length; - + // Function0 ia a 0-arg function, Function1 1-arg, Function2 2-arg, etc. - Function0 zeroArg = constant; + Function0 zeroArg = Function.of(42); Function1 oneArg = length; Function2 twoArg = mod; - + int applied = length.apply("foo"); Iterable mappedOverFunction = map(newList("a", "aa"), f); - - // partial application: + Function0 partiallyApplied = length.ap("foo"); int result = partiallyApplied.apply(); Function1 modulo42 = mod.ap(42); - + Function1 partiallyApplyFirstParam = mod.apply(42, _); Function1 partiallyApplySecondParam = mod.apply(_, 84); // a function can also be split to two functions, dividing some params // to the first one and others to the second one: Function1> split = mod.apply(__, _); - + // function composition, one way or the other - Function1 func = Function1.id(); + Function1 func = Function.id(); Function1 composed = func.andThen(length); Function1 composed2 = length.compose(func); - + // multi-param function can be views as a 1-arg function of a Tuple: Function2 m = mod; Function1,Integer> tuppled = m.tuppled(); - + Function2 twoArgFunction = mod; Function1> curried = twoArgFunction.curried(); @@ -245,7 +201,7 @@ Just a few examples of using these utils. //... List longs = newList(1l, 2l); - + // Longs (if assumed unbounded), Booleans and Strings are monoids, // but they do not have "default instances of Monoid typeclass" so we // must give one as a parameter. @@ -265,7 +221,175 @@ Just a few examples of using these utils. ### fi.solita.utils.functional.Functional -TODO + // head + int head = head(someIterableOrArray); + assertEquals(1, head); + + // headOption + Option headOption = headOption(someIterableOrArray); + assertEquals(Some(1), headOption); + assertEquals(None(), headOption(emptyList())); + + // last + int last = last(someIterableOrArray); + assertEquals(3, last); + + // lastOption + Option lastOption = lastOption(someIterableOrArray); + assertEquals(Some(3), lastOption); + assertEquals(None(), lastOption(emptyList())); + + // tail + Iterable tail = tail(someIterableOrArray); + assertEquals(newList(2,3), newList(tail)); + + // init + Iterable init = init(someIterableOrArray); + assertEquals(newList(1,2), newList(init)); + + // take + Iterable take = take(2, someIterableOrArray); + assertEquals(newList(1,2), newList(take)); + + // takeWhile + Iterable takeWhile = takeWhile(odd, someIterableOrArray); + assertEquals(newList(1), newList(takeWhile)); + + // drop + Iterable drop = drop(1, someIterableOrArray); + assertEquals(newList(2,3), newList(drop)); + + // dropWhile + Iterable dropWhile = dropWhile(odd, someIterableOrArray); + assertEquals(newList(2,3), newList(dropWhile)); + + // span + Pair, Iterable> span = span(odd, someIterableOrArray); + assertEquals(Pair.of(newList(1), newList(2,3)), + Pair.of(newList(span.left), newList(span.right))); + + // isEmpty + boolean isEmpty = isEmpty(someIterableOrArray); + assertEquals(false, isEmpty); + + // containment + boolean contains = contains(1, someIterableOrArray); + assertEquals(true, contains); + + // size + long size = size(someIterableOrArray); + assertEquals(3l, size); + + // cons + Iterable cons = cons(0, someIterableOrArray); + assertEquals(newList(0,1,2,3), newList(cons)); + + // concat + Iterable concat = concat(someIterableOrArray, someIterableOrArray); + assertEquals(newList(1,2,3,1,2,3), newList(concat)); + + // find + Option find = find(even, someIterableOrArray); + Option findFromMap = find(42, someMap); + assertEquals(Some(2), find); + assertEquals(Some("foo"), findFromMap); + assertEquals(None(), find(even, Collections.emptyList())); + + // filter + Iterable filter = filter(odd, someIterableOrArray); + Map filterFromMap = filter(evenKey, someMap); + assertEquals(newList(1,3), newList(filter)); + assertEquals(newMap(Pair.of(42, "foo")), filterFromMap); + + // exists + boolean exists = exists(even, someIterableOrArray); + assertEquals(true, exists); + + // forall + boolean forall = forall(even, someIterableOrArray); + assertEquals(false, forall); + + // foreach + foreach(someProcedure, someIterableOrArray); + + // map + Iterable map = map(negateInt, someIterableOrArray); + Map mapOfMap = map(Function.>id(), someMap); + Iterable> mapMultiple = map(negateInt, negateInt.andThen(negateInt), someIterableOrArray); + assertEquals(newList(-1,-2,-3), newList(map)); + assertEquals(newMap(Pair.of(42, "foo")), mapOfMap); + assertEquals(newList(Pair.of(-1,1), Pair.of(-2,2), Pair.of(-3,3)), newList(mapMultiple)); + + // flatMap + Iterable flatMap = flatMap(repeatTwice, someIterableOrArray); + assertEquals(newList(1,1,2,2,3,3), newList(flatMap)); + + // flatten + Iterable flatten = flatten(map(repeatTwice, someIterableOrArray)); + Iterable flattenArrays = flatten(new Integer[][]{new Integer[]{1}, new Integer[]{2}}); + assertEquals(newList(1,1,2,2,3,3), newList(flatten)); + assertEquals(newList(1,2), newList(flattenArrays)); + + // fold + Option fold = fold(SemiGroups.intSum, someIterableOrArray); + String foldFromZero = fold("->", SemiGroups.stringConcat, map(Transformers.toString, someIterableOrArray)); + assertEquals(Some(6), fold); + assertEquals("->123", foldFromZero); + + // reduce + long reduce = reduce(Monoids.longSum, map(Transformers.int2long, someIterableOrArray)); + assertEquals(6l, reduce); + + // union + Set union = union(newSet(1,2), newSet(2,3)); + assertEquals(newSet(1,2,3), union); + + // intersection + Set intersection = intersection(newSet(1,2), newSet(2,3)); + assertEquals(newSet(2), intersection); + + // subtract + Iterable subtract = subtract(someIterableOrArray, newList(2)); + assertEquals(newList(1,3), newList(subtract)); + + // min + Option min = min(someIterableOrArray); + assertEquals(Some(1), min); + assertEquals(None(), min(Collections.emptyList())); + + // max + Option max = max(someIterableOrArray); + assertEquals(Some(3), max); + assertEquals(None(), max(Collections.emptyList())); + + // sum + long sum = sum(1,2,3); + assertEquals(6l, sum); + + // product + long product = product(1,2,3); + assertEquals(6l, product); + + // sort + Iterable sort = sort(someIterableOrArray); + Iterable sortBy = sort(Compare.byNatural().reverse(), someIterableOrArray); + assertEquals(newList(1,2,3), newList(sort)); + assertEquals(newList(3,2,1), newList(sortBy)); + + // reverse + // repeat + // range + // sequence + // distinct + + // zip + // zipWithIndex + // transpose + // groupBy + // grouped + + // mkString + // unlines ## Word of warning @@ -274,9 +398,6 @@ This package comes with no warranty what-so-ever. It's higly experimental, might Packages and classes may get renamed or moved, and things may suddenly break. Use at your own risk! -And no, I honestly do not believe that Java is the language of the future. Please use a "real" language if you can. -But if you are stuck in the enterprise world, this project might just make your life worth living. - Bug reports, feature requests and opinionated recommendations are highly welcome ;)