It is possible to "Just use maps" in Java!

I’d like to claim it is possible to “just use maps” in Java!

This discussion is a follow up on a previous discussion “Just use maps” in Java?.

“just use maps” in Java could be done with:

  1. Paguro: a Java library that provides an implementation of persistent maps and a version of Clojure’s Transducers
  2. String maps only (keys are strings, values are arbitrary)
  3. Specific getters for each type of values to avoid the need for casting objects (in the spirit of what @ericnormand suggested)

Here is an example of how a Data-Oriented programming code snippet could look like in Java

Let’s create a nested map that represents a library catalog data (my favourite example!) via Paguro’s map and tup constructors:

var catalog = map(tup("booksByIsbn",
                      map(tup("978-1779501127",
                              map(tup("isbn", "978-1779501127"),
                                  tup("title", "Watchmen"),
                                  tup("publicationYear", 1987),
                                  tup("authorIds", vec("alan-moore",
                                                       "dave-gibbons")))))),
                  tup("authorsById",
                      map(tup("alan-moore",
                              map(tup("name", "Alan Moore"),
                                  tup("bookIsbns", vec("978-1779501127")))),
                          tup("dave-gibbons",
                              map(tup("name", "Dave Gibbons"),
                                  tup("bookIsbns", vec("978-1779501127")))))));

And here is an example of a simple data manipulation code: Calculating the list of the author names:

(See implementation of getAsMapOfMaps and getAsString below)

getAsMapOfMaps(catalog, "authorsById")
  .values()
  .map(author -> getAsString(author, "name"))
  .toImList() 
// PersistentVector("Alan Moore","Dave Gibbons")

I think that this piece of Java code is quite similar to what we are used to write in Clojure:

(->> catalog
     vals
    (map #(get % "name"))
     (into []))

Now the interesting question is: how the Java community is going to receive an approach like this and to be willing to relax on compile-time type validation in order to gain flexibility.

Could you suggest ways of improving the DOP approach in Java so that it increases the odds of being well-received by the Java community?

Implementation of specific getters:

import org.organicdesign.fp.collections.ImList;
import org.organicdesign.fp.collections.ImMap;

public class SpecificGetters {
    // TODO: move to methods in ImMap and ImList
    // TODO: Implement getInAs...
    // TODO: Implement getAsMapOfLists, getAsNumber, ...

    public String getAsString(Map o, Object k) {
        return (String)o.get((String)k);
    }

    public ImMap getAsMap(Map o, Object k) {
        return (ImMap)o.get((String)k);
    }

    public ImMap<String, ImMap> getAsMapOfMaps(ImMap o, Object k) {
        return (ImMap<String,ImMap>)o.get((String)k);
    }

    public ImList getAsList(Map o, Object k) {
        return (ImList)o.get((String)k);
    }
}
2 Likes

Hi Yehonathan,

I think the first improvement would be to make it closer to a literal data representation.

  1. JSON strings method

I know Java doesn’t have multi-line strings by default, but I think they are available now in Java 13 (according to this: Java 13 brings multi-line strings with text blocks | Revyuh). If you could make a JSON to collections parser that was convenient to use with string blocks, that would help a ton with readability.

JSON("""
{"booksByIsbn": {"978-1779501127": {"isbn": "978-1779501127",
.....
""")
  1. Lest nested syntax

I wonder if a syntax without so much nesting would be better. Instead of having to write all of those tuples, couldn’t Java varargs be used?

var catalog = map("booksByIsbn", map("978-1779501127", map("isbn", "978-1779501127",
                                                           "title", "Watchmen",
                                                           "publicationYear", 1987,
                                                           "authorIds", vec("alan-moore", "dave-gibbons")))
.....
  1. “Builder” syntax

Might it look better with builder syntax? That is, chain the puts?

var catalog = map().put("booksByIsbn", map().put("978-1779501127", map().put("isbn", "978-1779501127")
                                                                        .put("title", "Watchmen")
                                                                        .put("publicationYear", 1987)
...

Well, it’s still tedious!

  1. Inside out

It might be best to write these inside out.

var books = vec(map(book1), map(book2), ...); // book1 and book2 would be the kvs of those books
var authors = vec(map(author1), map(author2)); // author1 and author2 would be the kvs of those authors
var catalog = map("booksByIsbn", indexBy("isbn", books),
                  "authorsById", indexBy("id", authors);

I would experiment with these to get it more convenient to write and print out.

Rock on!
Eric

PS I’ll keep thinking of other ways to make it more acceptable in Java.

I wrote an article to summarise my understanding about how to apply Data-Oriented programming in Java.

The author of Paguro explains here the rationale behind the verbosity of map creation API.