Showing posts with label algorithm. Show all posts
Showing posts with label algorithm. Show all posts

28 December 2017

Interview Gabriel Grill

I have known Gabriel since a long time. We met at an early meetings of the Java User Group Vienna many years ago. I noticed him sharing content concerning diversity. It was just a matter of time until I would make him share his views ;-)

Gabriel, please introduce yourself. Why did you choose to become a software developer?
My name is Gabriel Grill. Currently I am writing my Master's Thesis at the University of Technology, Vienna, and work at the Austrian Centre for Digital Humanities at the Austrian Academy of Sciences. I started programming in high school (HTL). My main reasons for choosing the field at that time were the possibilities for earning money afterwards and the high probability of having a secured job in the future. After I got to know programming more, I liked how seamless one could use this skill to create things, the puzzle solving aspects of it and the creativity one needs to find solutions. I always tried not to focus too much on computing only. Consequently - at university - I took lectures in the social sciences, was politically involved, played the drums, did theatre and so on.

brosI know that you are concerned with sexism. For example I noticed you sharing content about women in technology. Why does that matter to you?
I believe it is important to be socially engaged in groups, organisations, etc. one is a part of and try to improve conditions for marginalised people there. Inclusion helps everybody. I very much recommend to watch this video on gender-inclusive software design by Distinguished Professor Margaret Burnett, which explains this point much better then I could.

My engagement in tech is not limited to barriers women have to face, I think structural discrimination against this group is still a very big issue in the community. Structural means that the discrimination is ingrained into the culture, normalised, and thereby one has to actively learn, reflect and work against exclusionary mechanisms to change the status quo. In male majority communities, such as the developer community, often so called "bro" cultures emerge. They have certain unwritten rules of how one should act and look like, to be a part of them. An example to me is the beer with "the guys" after work. Bonds are often formed there which in turn may help the people participating to advance in their careers. But there are people who are not into that or do not have the time. Similarly smoke breaks allow those who participate to connect more. Another example, which I have noticed several times in conversations or talks at conferences, are various forms of sexualisation or objectification through jokes or comments, which have exclusionary effects. These seemingly small things together make up a culture in which some have it easier to be a part of. Many of these mechanisms are not unique to developer communities but prevalent in society. The "Pop Culture Detective" is a great YouTube-Channel with many videos on reflections of portrayals of culture in media. I recommend its videos on the "Big Bang Theory" and nerdom especially to male developers (Part 1 - The Adorkable Misogyny of The Big Bang Theory, Part 2 - The Complicity of Geek Masculinity on the Big Bang Theory). I think we all would benefit a lot from a more inclusive and reflective community where for instance I, as a man, would not have to act and dress in certain ways to feel "normal". I would love if our community was much more about supporting one another and thinking about how to improve society.

What other topics are you concerned about?
I usually do political work in the contexts I am directly involved in. Consequently I have been doing a lot of student politics working against different types discrimination based on income, gender, race, accessibility at university. I believe in Austria we have a special responsibility to reflect and remember our past and work against right wing tendencies that want to divide society. In this manner, I want to point to Karl Popper's "Paradox of tolerance" which states: "Unlimited tolerance must lead to the disappearance of tolerance. We should therefore claim, in the name of tolerance, the right not to tolerate the intolerant." I try to take part in protests or actions that I am fond of and generally work on awareness through campaigns.

I am also interested in net politics, privacy and ethics of programming or more generally algorithmic systems. I have created a seminar lecture at TU Wien together with professor Tompits and my colleague Matthias Fassl to enable students to read scientific papers on ethical and social aspects of such systems and have interesting discussions. I try to talk at events or conferences sometimes to raise points I find important. I think generally as developers we need to spend more time on reflecting and learning how to build systems for people. This entails taking diversity into account and creating software which is not only tailored for a small subset of people who are well-off anyway but also useful to people with little money or single parents. I educate myself through lectures mostly in the social sciences, blogs and papers. I recommend to look through YouTube and Twitter, and consume content of people who belong to marginalised groups, such as Feminist Frequency, Kat Blaque and Annie Elainey. There is a lot to learn about other experiences.

I think global warming is a big issue and my actions here are more on a personal level like eating only little meat.

Outside your personal topics discussed till now, what do you consider the biggest challenges (for humanity)?
That is a very tough question to answer. I think it would be arrogant of me to answer this question bluntly. Depending on your circumstances the answers would be very different. For instance to me climate change is a big issue, but there are many people out there who see this differently. I think it is very important to explain to them that doing nothing makes things worse, but there may be no universal human challenges on which everybody completely agrees on. As a middle European, white, male, able-bodied, computer science student to me climate change may be a big issue at the moment, but for people who are starving or live in poverty other priorities apply.

Many developers would probably answer this question somehow similar because it is still a very homogeneous group. That is why I believe questions like this should be asked to people with different backgrounds to get a better answer. This would necessarily include most people that are part of the global South. I think as developers we should look more outside our own bubbles to grow our perspectives. I consider dealing with remnants of colonialism as a very important issue and in turn exploitation of the global South as well as weapon trading and war. Most of the other issues I have mentioned in the previous question.

Speak up, make your voice heardWhat do you do to engage in the topics? For example, did you take part in public protests, donate money to NGOs or sign petitions?
I do things like trying to eat less meat and use public transport as much as possible, but ultimately I think most important are policies. The issues mentioned in the previous questions should be tackled collectively through democratic processes by engaging in a political party or other organisation, raising awareness through events or activism and learning on what issues are there and what they really are about. I do many of these things and the ones you mentioned in the question as well, but still time and money is limited and you have to prioritise.

I would like to see more impact of my regular work on these important topics. Do you think that is possible in general?
I do not know if it is possible in general, but I think as developers we are in the position to be able to choose were we want to work. We can ask critical questions, have a positive influence on the projects we work on, raise awareness on issues in the organisation we are a part of and so on. If many developers, consumers and other stakeholders are keen on social and ecological values, businesses will adapt. On the positive side, it seems that social responsibility is becoming a more important topic to many companies at the moment. I believe often more policy is necessary, e.g. to foster fairer working conditions and payment for people in the global south. I think little things like supporting people, talking to colleagues about political issues, learning about ethics or working on improving diversity, are contributions that should be highly valued. Unfortunately a lot of this kind of work, especially care labour, is not paid.

Which guidance do we have to navigate professional decisions? Did you take specific decisions because of your values and social responsibility?
I think it is important as a developer to be aware of your responsibilities and educate yourself on how to deal with them. The social sciences, political science and philosophy or more specific the fields of social informatics and human computer interaction have a lot of knowledge on how to navigate when being faced with political decisions. I strongly suggest to read more literature from these fields.

I want my development to be human-centred. I think about for whom I create something, am I really inclusive, is it ethical or bad for others and how can I best include stakeholders in the decision processes. These question usually do not have a trivial answer and that is why it is important to give them attention.

I have not encountered a situation where I was asked to write a piece of code which I considered to be very unethical, but I am aware that I have made ethical decision myself when coding. In my Master's Thesis I use text mining methods to extract information from news articles. There are many parameters to tune and models to choose and depending on my choices the results will be different. It is my obligation to document this process very well. How I get results and how I present these results is very political. I suggest reading this article which critiques a study that claims to be able to identify sexual orientation through images given to a trained machine learning system.

How do you think about selecting industry, customer and project based on your values and social responsibility?
I think one definitely should take their values and the social responsibility of the company into account. I would not be willing to work for a arms manufacturing company. Thorough research is required here because some companies do not state directly that their work or research may have a secondary use in armed conflicts. I would not like to work at companies in the gambling business or with a focus on surveillance.

DiversityDo you have problems with any industries?
It depends on the context very much. I would not want to work for an animal factory but if there is not another option or the factory is somehow vital I would probably reconsider. I think I am not able to give a general answer here, but animal factory and weapon manufacturing are as close as it gets to a no for me. This includes companies like Thales or Glock.

Did you ever reject a customer based on your values?
I did not need to reject a project offer so far, but I have chosen consciously not to apply to certain companies. During my studies I have chosen my projects in a way I felt they could benefit society.

On the other hand, what would be projects that you would love to work on?
At the moment I consider to go into research and work on ethical and social implications of algorithmic systems. I think research in this area could be useful to society. The current trend is towards putting more decision making algorithms in our lives but when these are mostly developed by a homogeneous group with almost no education on ethical and political issues, this becomes a democratic problem on who gets to decide what the algorithms do or what their optimisation goals are. Big companies are looking into these topics, due to critique from the public. I like developing very much. Working on projects that support marginalised people in some way would be interesting, like working on accessibility, support for campaigns or apps against hate. Projects that are very interesting from a technological point of view would also be options for me.

Thank you Gabriel.

16 August 2016

Absolute Priority Premise, an Example

Optimus Prime, Transformers ToysTransformation Priority Premise
I would like to start this article with Uncle Bob Martin's Transformation Priority Premise - TPP for short - which he defined in 2011. With the premise Uncle Bob gave an algorithm how to find the next test when doing Test Driven Development. During (classic) TDD we use transformations to change the code to get from red to green. For example when starting with a new method the very first test is usually red because the empty or generated method returns null. We fake the first test by changing that null to return a constant value, e.g. return 5. This transformation is called nil->constant and has a high priority. A later test might force us to add a conditional to enable another fake. This transformation is called unconditional->if and has a medium priority. Replacing the value of a variable, i.e. variable->assignment has a low priority.

According to Uncle Bob, these transformations have a preferred ordering. We should prefer higher priority transformations to pass tests and chose tests in a way that they can be passed with higher priority transformations. When an implementation seems to require a low priority transformation, we could backtrack to see if there is another test to pass first which does not need that transformation. The theme behind the TPP is that as the tests get more specific, the code gets more generic. If you want to know more about TPP, see Uncle Bob's cartoon follow-up on the TPP and Sorting and his talk on the Transformation Priority Premise.

Absolute Priority Premise
In 2012 Micah Martin set out to define some heuristics to compare code objectively and proof that some code is better than some other code. Building on the TPP's priorities he defined the Absolute Priority Premise - APP for short. The APP knows six components, i.e. basic building blocks of code, and assigns them a mass. The building blocks and their weights were
  • constant, a value in code has mass of 1.
  • binding, a name or variable has a mass of 1, too.
  • invocation, calling a method or function - mass 2.
  • conditional, any form of if, switch or case - mass 4.
  • loop, for or while loops - mass 5.
  • assignment, replacing the value of a variable - mass 6.
More complex building blocks have a higher mass. The total mass of a piece of code is the sum of the mass of its respective components. A lower value is better. The best set of specific values of mass are unknown and I will use Micah's weights. If you chose to count different, please let us discuss!

A detailed explanation of the Absolute Priority Premise is given in the two presentations of 8th Light University (8LU) - Part One and Part Two. See also Micah's Coin Changer Kata as a complete example of applying the premise.

Measuring the Mass of Code
I like Micah's idea to measure the mass of code. It might not be a direct indication of readability but simpler code is always better. I wrote six different versions of the Word Wrap kata and was wondering which one would be considered the "best". I should have calculated the mass of these algorithms manually, but I followed Terence Parr's advice, to avoid working by hand five days what I can spend five years of my life automating. I had to create a tool to calculate the mass of Java code - which of course took me much longer, especially as I verified the mass of each algorithm manually to make sure my code worked as expected.

Absolute Priority Counter
I created abpricou, the ABsolute PRIority COUnter. It parses Java source and collects its mass as defined by the Absolute Priority Premise. It is written in Python, because it was Python month when I started working on it. It uses the Python Java Parser plyj. plyj offers a Visitor API for parser events,
class CountingVisitor(m.Visitor):
    def __init__(self):
        super(CountingVisitor, self).__init__()
        ...
Constants and bindings are staight forward, e.g.
    def visit_Literal(self, literal):
        return self._a_constant_value()

    def visit_FormalParameter(self, parameter):
        return self._a_name()
Literals in the code are counted as constants and parameters are only names for values, thus bindings. A method or function counts as a constant for the code it represents and a binding for its name.
    def visit_MethodDeclaration(self, declaration):
        self._a_name()
        return self._code_is_a_constant()
Invocations, conditionals and loops are the same. For example
    def visit_MethodInvocation(self, invocation):
        return self._an_invocation()

    def visit_IfThenElse(self, conditional):
        return self._a_conditional()

    def visit_While(self, loop):
        return self._a_loop()
The only interesting case is the assignment. Not every assignment in Java is counted as an assignment, only re-assignments which modify values are counted. final fields or local variables are just names for expressions similar to parameters.
    def visit_Assignment(self, assignment):
        if ... :
            # code to ignore
            # final field is assigned in constructor
            # final local is assigned in block
        else:
            return self._an_assignment()
Get the tarball and installable egg of the Absolute Priority Counter.

Let's see some code: Word Wrap Kata
As I said before, I developed the counter to calculate the mass of my different implementations of Word Wrap. I was interested in the mass of the algorithm and skipped constructs like class definitions. (I suppose a class definition is a constant for the code and a binding for the name.) I also ignored Annotations, Enumerations and Generics. The first algorithm uses recursion to loop over the blanks to find the end of each line. Its code is
   final char BLANK = ' ';
   final char NEWLINE = '\n';

   String wrapRecursive(String line, int maxLineLen) {
      if (line.length() <= maxLineLen) {
         return line;
      }

      int indexOfBlank = line.lastIndexOf(BLANK, maxLineLen);
      int split;
      int offset;
      if (indexOfBlank > -1) {
         split = indexOfBlank;
         offset = 1;
      } else {
         split = maxLineLen;
         offset = 0;
      }
      return line.substring(0, split) + NEWLINE
           + wrap(line.substring(split + offset), maxLineLen);
  }
including the components
| Component   | Mass  | Count |
| constant    |   1   |   7   |
| binding     |   1   |   8   |
| invocation  |   3   |  10   |
| conditional |   4   |   2   |
| loop        |   5   |   0   |
| assignment  |   6   |   0   |
resulting in a total mass of 53. Further implementations have the following masses:Scales
  • The tail recursive solution has neither loops nor assignments similar to the recursive one and has a total mass of 71. (The source of this and all further variants is given in Word Wrap Kata Variants.)
  • The looping variant contains a loop and an assignment resulting in a mass of 68.
  • The optimised loop which avoids temporary String objects, contains a loop and an assignment and some more invocations. Its mass is 80.
  • The loop using a buffer saves even more heap allocations and needs more invocations summing up to 105.
  • The solution using Regular Expressions as shown in the original article has a mass of 55. Merging the parts of the expression into a single expression would save five invocations, but makes the expression less readable. I argue that the Regular Expression itself has some weight, as it contains one conditional ("|" in line 3) and two loops ("(.{1," + maxLineLen + "})" in lines 1 and 4). So a fair weight is 69.
  • The functional version using flatMap and reduce is quite verbose due the lack of inferred pair types in Java. As plyj does not support Java 8 I was unable to measure its mass.
First Conclusion on Algorithms
The most basic, recursive version of Word Wrap has the least weight. What does it mean? Is it the best version? It is the most compact version without playing Code golf, at least in Java. It does not mutate any variables but it puts the highest load on the Garbage Collector. All the discussed algorithms have different memory and run time performance characteristics. I had hoped for a clear answer what the best version would be and I am not seeing that. I challenge you to leave a comment with a version of Word Wrap with a smaller weight. Would it be considered better code?

The mass of the basic loop version seems too much compared to the recursive version. The current weights favour functional programming by putting a penalty on loops and assignments. Mutating a local variable has a smaller weight than mutating the state of an object. The looping version uses StringBuilder instead of plain String + which needs two more invocations for its construction. Then its components are
| Component        | Mass  | Count |
| constant         |   1   |   8   |
| binding          |   1   |  10   |
| local assignment |   1   |   3   | like a new local binding
| invocation       |   3   |  10   |
| conditional      |   4   |   1   |
| loop             |   5   |   1   |
| assignment       |   6   |   0   |
and its mass is 60 which is a bit more code than the recursive solution.

More Structure
The different wrap() functions above are just algorithms. They are not factored into parts that might change at different speeds over time and definitely violate the SRP. They also break the OCP as it is impossible to change the strategy of splitting without touching the logic of collecting. To address this I wrote another recursive version following Tell, don't ask,
interface Page {
   void renderLine(String lineOfProperLength);
}

class Wrapper {

   final char BLANK = ' ';

   final Page page;
   final int maxLineLen;

   Wrapper(Page page, int maxLineLen) {
      this.page = page;
      this.maxLineLen = maxLineLen;
   }

   void wrap(String line) {
      if (line.length() <= maxLineLen) {
         page.renderLine(line);
         return;
      }

      int indexOfBlank = line.lastIndexOf(BLANK, maxLineLen);
      int split;
      int offset;
      if (indexOfBlank > -1) {
         split = indexOfBlank;
         offset = 1;
      } else {
         split = maxLineLen;
         offset = 0;
      }

      page.renderLine(line.substring(0, split));
      wrap(line.substring(split + offset));
   }
}
which is very similar to the tail recursive solution above, with a total weight of 56. The number is smaller because it does not contain the necessary implementation of Page.renderLine(). The shown code does less, which makes it difficult to compare. On the other hand Page.renderLine() might be used by other parts of the code as well, so it is not strictly a part of Word Wrap.

Moar Structure!1!!
Later I created an extremely factored and decomposed implementation of Word Wrap that separated concepts of rendering, splitting, accumulating lines and hyphenation rules which should fulfil both SRP and OCP. Ignoring the HyphenationRule because that is not covered by the other solutions, its mass is 167 due to many method invocations and parameter names:
| Component   | Mass  | Count |
| constant    |   1   |  17   |
| binding     |   1   |  33   |
| invocation  |   3   |  30   |
| conditional |   4   |   4   |
| loop        |   5   |   1   |
| assignment  |   6   |   1   |
There is a loop and an assignment, at its core this implementation is a looping one. Using the recursive solution I should be able to get rid of the loop and the assignment.

Conclusion
The Absolute Priority Premise counts the different components of a program and sums their weights. If the weights are correct than the program with the smallest mass would be considered the "best" program. This is not true for algorithms like the Word Wrap because the APP ignores features like memory usage or performance optimisation. For general purpose code the validity of the APP is unclear. It just measures the number of code constructs, favouring more compact, functional code. On the other hand the four rules of simple design encourage us to introduce explaining variables to reveal the intent of the code which is more important than fewer elements.

12 August 2011

Word Wrap Kata Variants

It's time for some exercise - time for a code kata. I like the simple ones because they don't take much time and still provide a certain amount of training. After having done the Prime Factors Kata more than 20 times using Java, Ruby, C#, Turbo Pascal, BASIC and even Forth, I feel like trying a new one for a change. I choose the Word Wrap Kata, also by Uncle Bob. Unlike Prime Factors, Word Wrap seems to be less popular, there are only a few experiences with it published, e.g. Word Wrap using Python.

Recursive
The kata's task is to write a function that, like a word processor, breaks the line by replacing the last space in a line with a newline. The most straight forward solution to this is IMHO the recursive one. I only need to consider the first blank or forced break and call myself with the remaining text.
public String wrap(String line, int maxLineLen) {
   if (line.length() <= maxLineLen) {
      return line;
   }
   int indexOfBlank = line.lastIndexOf(BLANK, maxLineLen);
   int split;
   int offset;
   if (indexOfBlank > -1) {
      split = indexOfBlank;
      offset = 1;
   } else {
      split = maxLineLen;
      offset = 0;
   }
   return line.substring(0, split) + NEWLINE +
      wrap(line.substring(split + offset), maxLineLen);
}
Usually kata is not about the final solution, but about the process to get there. Still I want to compare different solutions, so let's analyse this one. If the line needs to be split n times (into n+1 shorter lines) then this solution creates 3*n String objects and additional n StringBuilders for string concatenation. ... 4*n objects are created.

Exercise Time at Karate DojoTail Recursive
To be tail recursive a function's last statement must be the recursive call.
public String wrap(String line, int maxLineLen) {
   StringBuilder accumulator = new StringBuilder();
   wrap(line, maxLineLen, accumulator);
   return accumulator.toString();
}
private void wrap(String remainingLine, int maxLineLen, StringBuilder accumulator) {
   if (remainingLine.length() <= maxLineLen) {
      accumulator.append(remainingLine);
      return;
   }
   int indexOfBlank = remainingLine.lastIndexOf(BLANK, maxLineLen);
   int split;
   int offset;
   if (indexOfBlank > -1) {
      split = indexOfBlank;
      offset = 1;
   } else {
      split = maxLineLen;
      offset = 0;
   }
   accumulator.append(remainingLine.substring(0, split));
   accumulator.append(NEWLINE);
   wrap(remainingLine.substring(split + offset), maxLineLen, accumulator);
}
This solution creates the new String in the very end. It needs 2 Strings per new line and only one StringBuilder and a final String to return it. ... 2*n+2 objects are created.

Loop
A tail recursive function can be rewritten to reuse the stack frame transforming it into a plain loop. As Java does not support that optimisation, I do it by hand.
public String wrap(String line, int maxLineLen) {
   StringBuilder accumulator = new StringBuilder();
   String remainingLine = line;
   while (remainingLine.length() > maxLineLen) {
      int indexOfBlank = remainingLine.lastIndexOf(BLANK, maxLineLen);
      int split;
      int offset;
      if (indexOfBlank > -1) {
         split = indexOfBlank;
         offset = 1;
      } else {
         split = maxLineLen;
         offset = 0;
      }
      accumulator.append(remainingLine.substring(0, split));
      accumulator.append(NEWLINE);
      remainingLine = remainingLine.substring(split + offset);
   }
   accumulator.append(remainingLine);
   return accumulator.toString();
}
The loopy :-) solution creates the same number of objects as the tail recursive one, but has reduced call overhead. ... Still 2*n+2 objects are created.

Optimised Loop
Let's optimise away the splitting of the remaining line because it gets split again in the next call, so all these Strings are only temporarily used.
public String wrap(String line, int maxLineLen) {
   StringBuilder accumulator = new StringBuilder();
   int pos = 0;
   while (pos + maxLineLen < line.length()) {
      int indexOfBlank = line.lastIndexOf(BLANK, pos + maxLineLen);
      int split;
      int offset;
      if (indexOfBlank > pos - 1) {
         split = indexOfBlank;
         offset = 1;
      } else {
         split = pos + maxLineLen;
         offset = 0;
      }
      accumulator.append(line.substring(pos, split));
      accumulator.append(NEWLINE);
      pos = split + offset;
   }
   accumulator.append(line.substring(pos));
   return accumulator.toString();
}
Now only one String is created per new line, one for the last remaining part and one after the final concatenation. ... n+3 objects are created.

wrappedUsing a Buffer
If I could get rid of half of the String splitting, why not drop the other half too?
public String wrap(String line, int maxLineLen) {
   StringBuilder accumulator = new StringBuilder();
   accumulator.append(line);
   int pos = 0;
   int inserted = 0;
   while (pos + maxLineLen < line.length()) {
      int indexOfBlank = line.lastIndexOf(BLANK, pos + maxLineLen);
      if (indexOfBlank > pos - 1) {
         accumulator.setCharAt(inserted + indexOfBlank, NEWLINE);
         pos = indexOfBlank + 1;
      } else {
         accumulator.insert(inserted + pos + maxLineLen, NEWLINE);
         pos = pos + maxLineLen;
         inserted++;
      }
   }
   return accumulator.toString();
}
Only one StringBuilder and one String are created. ... 2 objects are created. This is definitely the most garbage collector friendly solution because it creates only one temporary object, the StringBuilder.

Copying Characters
If there are blanks in line then all characters get copied once in append() and all solutions behave similar. (The method substring() does not copy characters, it just creates a new String with different pointers in the character array of the original String.) But if there are no blanks in line then the last solution copies all characters after the split point around one or more times. Copying large numbers of characters might be slower than allocating an object, especially as the JVM/Hotspot is optimised for short lived, small objects.

Resizing the StringBuilder
For all solutions using an explicit StringBuilder another optimisation is to avoid automatic resizing. The size of accumulator must be large enough to contain all additional newlines. If a line has a blank then it's replaced, so no new character is added. Only when a line contains no blank, then a newline is inserted. This can happen up to lineLen / maxLineLen times. Flooring (rounding down in integer division) is the right thing because after the last part, e.g. remainingLine, nothing is added.
...
   StringBuilder accumulator =
      new StringBuilder(calcMaxSize(line.length(), maxLineLen));
...
private int calcMaxSize(int lineLen, int maxLineLen) {
   int maxCharsAdded = lineLen / maxLineLen;
   return lineLen + maxCharsAdded;
}
Note that all these optimisations are highly theoretical. In a typical web or database application it doesn't matter at all if a few temporary objects are created or not. Remember that "Early optimisation is the root of all evil" (Donald Knuth). I would stick with the first solution because it's the shortest and easy to understand.

Bonus Round
public String wrap(String line, int maxLineLen) {
   return line.replaceAll("([^ ]{" + maxLineLen + "})" + // 1
      "(?=[^ ])" +                                       // 2
      "|" +                                              // 3
      "(.{1," + maxLineLen + "})" +                      // 4
      " ",                                               // 5
      "$1$2" + NEWLINE);
}
This solution is even shorter, just one line using a Regular Expression. It replaces the split points with a newline or adds one. The pattern matches areas of line which (1) contain exact maxLineLen characters that are not blanks, (2) which must be followed by a character that's not a blank (and which is not consumed) (3) or it matches areas of (4) one up to maxLineLen characters (5) which are followed by a blank. The match is replaced with the first (1) and second (3) groups together with a newline. The single blank (5) is not a member of any group and is dropped.

(Get the source.)