Sunday, December 28, 2008

Understanding Monads

Monads are not trivial. If they were, there would not be so many tutorials and articles explaining them.

On the other hand, as with many concepts that we may learn with some difficulty and later think simple, they are not that complicated, if you understand a few other things first. As an analogy, it is hard to understand and use multiplication if you have not yet mastered the concept of addition, but most of us now are pretty comfortable with multiplication.

This is not intended to be another monad tutorial. As with learning addition before multiplication, I believe there are some essential concepts that are necessary before one can be comfortable with monads. In this post I list the concepts I found useful on the path to my understanding of monads (such as it currently is). I set out these details to help me solidify that understanding. Other people may be able to understand monads without understanding all of these concepts, or may require learning other concepts in addition to some of these, but perhaps there will be a few people who find these notes useful.

I came across monads when investigating Haskell after programming in C and Java for many years. The path I took reflects the fact that I have been used to thinking in terms of impure imperative languages. Monads are a more natural fit to functional languages, and I believe part of the task in understanding them is to think more in the functional style.

Below is my list of concepts I found helpful. Don't worry about trying to see how they all relate to monads, just read them. Later, when you read one of those monad tutorials, perhaps it will make more sense.

Contents

The Option Class in Scala

In Java, null is often used as a marker to indicate a missing value for an object. For example, System.getProperty returns null if the property is not defined. The Option class in Scala is a nice way to avoid using nulls this way, which makes for cleaner code. You can read about Scala's Option class in my post about avoiding nulls, and you should use the Option class a bit to get to know it.

Option is a monad, so once you know how to use it, you know how to use at least one monad. See how easy it is to start using monads?

Parser Combinators

Scala provides parser combinators as part of the standard library. This makes it easy to build simple parsers. You can see how this is done by reading my post about parser generators where I build a simple four-function expression parser.

When using parser combinators, parsers are built up by composing (combining) other parsers using various combining operators (combinators). The result of one of these combining operators is another parser. Eventually, you build up a top-level parser that can parse your entire grammar. Then you invoke that parser on your input stream to parse the input.

These parser combinators are monads. See "Monadic Parser Combinators".

Uniform Container Methods in Scala

The Option class, mentioned above, defines methods for map, flatMap and filter. All of the container classes in Scala, including Array, List and the Map trait, define these same three methods. The container classes also implement foreach, forall and exists. Option implements foreach, even though it never has more than one item. This makes more sense if you think of Option like a List that can have only zero or one element in it.

Having this same set of methods available for all of the collection classes makes it easier for the programmer to use any of them, but it also makes it possible to write a package of higher-level control code that can accept any of these classes. With such a higher-level package, you can add your own container class that will work with that package as long as your container class supplies the appropriate set of methods.

The container classes in Scala (including Option) are all monads. Because all monad classes supply a specific set of methods (informally, the monad interface), a monad library of higher-level control classes can be written that use that monad interface. In addition to supplying specific methods, there are a couple of consistency rules that constrain how those monad methods must behave. This is no different than, for example, the contract in Java relating the behavior of the equals and the hashCode methods for a class. As with any other interface definition and contract, if you write your own class and it implements the monad methods and follows the monad contract, then you can use that class with a monad library.

Functional Languages

Functional languages stress some things that are not typically discussed in imperative languages such as Java:
  • Referential transparency
  • Immutable data
  • Recursion rather than loops
  • Lazy evaluation
These concepts fit together nicely in a functional language. Using immutable data goes hand-in-hand with referential transparency. Lazy evaluation is an efficient way (and is sometimes necessary to make it even possible) to implement delayed computations.

Referential Transparency

A pure functional language, such as Haskell, has no side effects. This means that calling a function returns a value and does nothing else. In particular, a function can not set state. This is a very different way of thinking from imperative languages such as Java, where there are objects all over the place with state that gets changed by method calls.

Referential transparency is a phrase from the functional programming world which means, basically, "no side effects". Side effects include reading any state which is not passed in as an argument or setting any state which is not part of what is passed back as an argument. If a function is referentially transparent, then a call to that function with a specific set of values as arguments will always return exactly the same value.

When functions are composed into larger functions, it is easier to reason about them when they are referentially transparent and there are no side effects to worry about. Of course, in the real world it can be very useful to be able to store some state data, so how does one implement this functionality in a pure functional language?

The answer is Monads. This is described pretty well in a 1992 paper by Philip Wadler called "Monads for functional programming". Reading this paper helped me understand the motivation for monads. Monads provide a way to collect all those side effects into known locations in the program. When all of the side effects are collected into monads, the rest of the program (all of the non-monad parts) are still referentially transparent, so remain easier to compose. The side effects are still there, but because they are encapsulated by the monads, it is easier to deal with them.

Group Theory

When you read about monads, at some point you will come across a mention of Category Theory as the source of monads. Category Theory is pretty abstract, and you don't need to know it to understand monads. But monads are a mathematical concept, so digging into the math background of the concept could help solidify your understanding.

After reading a bunch of stuff about Category Theory, I went back to Group Theory, which for our purposes you can think of as a special case of Category Theory. While reviewing groups, rings and fields, I stumbled across monoids, a term which I had not recalled from my readings in the field years ago. It's not quite the same thing as a monad, but because Group Theory is focused on transformations, I started thinking of monads as transformations, which I think helped my understanding of them.

If you know your basic math, like addition and multiplication, you know something about Group Theory. Here's a little sampler:
  • The integers, when taken with the operation of addition, are a group. This is because addition of integers satisfies the following rules:
    1. There is a unique identity value (zero).
    2. The set (of integers) is closed under the operation (of addition).
    3. There is an inverse (the negative) for every value.
    4. The operation (addition) is associative.
  • The integers, when taken with the operations of addition and multiplication, are a ring. This is because they satisfy all of the above group rules (for addition), plus the following rules:
    1. There is a unique identity value (1) for the second operation (multiplication).
    2. The set (of integers) is closed under the second operation (multiplication).
    3. The second operation (multiplication) is associative.
    4. The first operation (addition) distributes over the second operation (multiplication).
  • The rational numbers, when taken with the operators of addition and multiplication, are a field. This is because they satisfy all of the above ring rules, plus the following:
    1. There is an inverse (the reciprocal) under the second operation (multiplication) for every value (except zero).
A monoid, in case you are interested, is like a group but without the requirement of having an inverse. Thus, the natural numbers with the addition operation are a monoid.

An item of interest related to monads: the value zero is a special value under multiplication in the field of integers, because any value multiplied by zero is still zero. This is similar to the None value in an Option in that the standard operations such as map and filter have no effect on None. None is thus a "zero" value for an Option. In the same way, an empty List is a "zero" value for a List.

Monads as Delayed Computations

Back in 2007, when I had just started looking at Haskell and had first run across monads, I had the good fortune to be sitting next to Conal Elliott on a long plane flight. He was on his way to deliver a paper at a Haskell conference, but at the time I had no idea who he was. Since he obviously knew a lot about Haskell, I asked him to explain monads to me, which he cheerfully attempted. Given how little I knew at the time about all of the pieces I list in this post, he did a very good job - but I still struggled with it.

My recollection of my interpretation of his explanation is this: In a pure functional program, there are no side effects, so the program must do the same thing every time. Clearly this does not work when there is user input, because you want the program to do different things based on different user input each time it is executed. So think of the program execution as being in two phases: first, create an execution tree that represents the program with all possible choices based on user input; then evaluate that execution tree, using the user input when required to prune off parts of the tree and select other parts.

Of course the actual tree with all possible choices might be infinitely large, and certainly will be too large to fully instantiate, so you don't really separate the above two phases and try to build the whole tree at once. Instead, you build it only as you need it (lazy evaluation). So you build a bit of the tree, then you evaluate that bit, which prunes out a bunch of potential branches and directs you to the next part of the tree that needs to be built. Repeat until you have completed the evaluation, in which case the program terminates.

In the above model, the decision points that are based on user input are the monads. I visualized the monad as a node in the tree with a bunch of children together with a decision function based on user input that determined which of those children to evaluate.

Conclusion

My conclusion: don't worry about understanding monads, just keep reading about them and learning more about functional programming. Some day it will all make sense and seem easy.

Update 2009-02-18: Change typo "with" to "without" as pointed out by Joseph.

Sunday, December 14, 2008

Java Resources Support

One of the programming philosophies that I try to follow in order to encourage myself always to do my best work is to assume that every program I write will eventually become a "production" program, distributed to customers all over the world. One detail that falls into that "all over the world" part is internationalization. In other words, I design internationalization into my programs from the start, even when the program is primarily for internal or personal use.

Java makes the string-replacement part of this pretty easy to do with its ResourceBundle class. If you use ResourceBundle consistently, you can easily ship localizations for multiple languages bundled with your application, and other people can take your program and resource files and localize them for other languages without having to recompile your program.

If you have thousands of strings being translated into dozens of languages, you might want to get yourself a sophisticated translation management system or ship around XLIFF files (or perhaps you would like to help write an open source localization repository). But for smaller projects such as I have worked on, I have found that some relatively simple support methods and a bit of convention have served me pretty well.

Contents

Building The Resources

To simplify loading resources, I find it easiest to put them all into one properties file per locale. But to improve maintainability, it is best to keep the resources in multiple files close to the source files in which they are used. I bridge these two conflicting goals by keeping my resources in multiple properties files, then concatenating them all together into a single properties file when I build my application.

To avoid confusion and emphasize the fact that my properties source files are not used in their source file form, I use the extension .props for those files. My concatenation program collects all of the .props files for one locale into one .properties file. I run it once for each locale for which I have files. The concatenation program also adds separators and source filenames in comments between the contents of each source file, and converts non-ascii characters to backslash-u notation. While I am at it, I also stuff some other information into the generated properties files, such as version and date information, so that I can easily access those details at runtime.

In conformance with the standard per-locale naming convention for resource bundle properties files, for each .props base file there can also be locale-specific translations. For example, for the base file Foo.props there could be Foo_hu.props with Hungarian translations, Foo_fr.props with French translations, and Foo_fr_CA.props with French-Canadian translations.

The properties file with the collected resources goes into a file in the same directory as the class files and gets packaged up in the jar file along with the class files. For example, I might name the collected resources file net/jimmc/app/Resources.properties, so it goes in the directory with all of the files in the net.jimmc.app package. When I load it, I specify its location as net.jimmc.app.Resources and the ResourceBundle code automatically finds it from my jar file, as shown in the initResources method below.

The Basic Interface

In order to be able to use the simplest possible code when calling to retrieve resources in my Java apps, I use support code, shown below, to do the following:
  • Encapsulate my ResourceBundle in the support code so that the caller does not need to worry about where the resources come from.
  • Make my resource retrieval functions so that, when a key is not found in the resources, it returns the key as the value of the method rather than throwing an exception. The key then appears in my GUI, where I can see it and correct it later when I want, rather than causing an exception that stops the program and forces me to correct the string immediately before I proceed.
  • Retrieve a resource string and format it with one or more arguments in one call.
In my Java apps, I define a ResourceSource interface with definitions for methods to implement the above functionality:
public interface ResourceSource { /** Get a string from resources. * @param name The resource name. * @return The value of the resource, * or the name if no value is found. */ public String getResourceString(String name); /** Get a string from resources, or null if not found. * @param name The resource name. * @return The value of the resource, * or null if no value is found. */ public String getResourceStringOrNull(String name); /** Get a string from a resource and pass it to MessageFormat.format * with the specified arguments, returning the result. * @param name The resource name. * @param args The args to MessageFormat.format. * @return The formatted resource string. */ public String getResourceFormatted(String name, Object[] args); /** Get a string from a resource and pass it to MessageFormat.format * with the specified one argument put into a new Object[1], * returning the result. * @param name The resource name. * @param arg The argument to MessageFormat.format. * @return The formatted resource string. */ public String getResourceFormatted(String name, Object arg); }

The Basic Implementation

My Application object, a singleton, implements my ResourceSource interface and is the source for resource values for the application. (This is conceptually the same as the IResources interface I mentioned in a previous post.) It contains an internal reference to my resources, so that the caller does not need to worry about that:
import java.text.MessageFormat; import java.util.ResourceBundle; private ResourceBundle resources;
During initialization of my Application object, I call a simple initResources method to load my resources from that one file containing the concatenated contents of all of the props files:
private void initResources() { resources = ResourceBundle.getBundle("net.jimmc.app.Resources"); }
My Application object has a private method that accesses my resources, which all of my other Application resource methods call (to simplify adding other functionality later):
private String getResourceValue(String name) throws MissingResourceException { return resources.getString(name); }
The methods in the Application object that implement the ResourceSource interface all call getResourceValue to get the resource value:
public String getResourceString(String name) { try { return getResourceValue(name); } catch (MissingResourceException ex) { return name; //use the resource name } } public String getResourceStringOrNull(String name) { try { return getResourceValue(name); } catch (MissingResourceException ex) { return null; } } public String getResourceFormatted(String name, Object[] args){ String fmt = getResourceString(name); return MessageFormat.format(fmt,args); } public String getResourceFormatted(String name, Object arg) { String fmt = getResourceString(name); Object[] args = { arg }; return MessageFormat.format(fmt,args); }

Field Names In Format Strings

When using the formatting methods in java.text.MessageFormat you specify the locations of replacement values using numbers in braces, so your formatting string might contain phrases such as {1} or {2,date,medium}. You have to ensure that these index numbers correspond properly to the arguments in the source code that uses that format string. Since these format strings are in a different file (the resource properties file) than the code that uses them, extra care is required to avoid mixing up the numbers, such as when adding or modifying the argument list. To minimize this problem, I add a version of getResourceFormatted that allows using field names rather than numbers in the format strings, so they contain phrases such as {count} and {modtime,date,medium}. When calling these methods, the fieldNames array contains names that correspond to the args of the same index.
/** Get a string from the resource file, map fields names to numbers, * and format it with the given arguments. */ public String getResourceFormatted(String name, Object[] args, String[] fieldNames){ String fmt = getResourceFormat(name,fieldNames); return MessageFormat.format(fmt,args); } /** Get a resource string and map field names to numbers. */ public String getResourceFormat(String name, String[] fieldNames) { String fmt = getResourceString(name); return mapFieldNamesToNumbers(fmt, fieldNames); } /** Replace named MessageFormat field references by field numbers. * The named field reference looks just like a regular format * segment, but with a string in place of the number. */ public String mapFieldNamesToNumbers(String s, String[] fieldNames) { for (int i=0; i<fieldNames.length; i++) { String fieldName = fieldNames[i]; if (fieldName==null || fieldName.equals("")) continue; //skip blanks in the array String p = "\\{"+fieldName+"\\}"; String q = "{"+i+"}"; s = s.replaceAll(p,q); p = "\\{"+fieldName+"\\,"; q = "{"+i+","; s = s.replaceAll(p,q); } return s; }

Runtime Overriding Of Resources

In order to allow loading additional resources at run time, for debugging, I add an additional variable to store my override resources:
private Properties resourceProperties;
I then add a method, which gets called from a debugging command to which I have specified a resources properties file name, to load values from the specified properties file into my override resources:
public void loadResourceProperties(String filename) { if (resourceProperties==null) resourceProperties = new Properties(); try { FileInputStream ifile = new FileInputStream(filename); resourceProperties.load(ifile); String msg = getResourceFormatted("info.LoadedResourceProperties", filename); startupMessage(msg); } catch (Exception ex) { String msg = getResourceFormatted( "error.LoadingResourceProperties",filename); throw new RuntimeException(msg,ex); } }
I modify my private getResourceValue method to look in my override properties before using the regular properties:
private String getResourceValue(String name) throws MissingResourceException { if (resourceProperties!=null) { String v = resourceProperties.getProperty(name); if (v!=null) return v; //found an override property value } return resources.getString(name); }

Recursive Resources

For some applications I want to be able to define resource strings that reference other resource strings or context values provided by the application. The standard replacement text in a message format starts with an open brace followed by a number. In my resource files, after implementing my Field Name extension above, I can also follow an open brace by a field name. With the extension in this section, I can follow an open brace by the "@" character and a context name or the name of another resource. The values for the context names are passed to the formatting methods in a Map.

For example, if I have these resources:
Sample.abc.foo=A formatted string with {@Sample.abc.bar} for {@User} Sample.abc.bar=a nested property
and I create a context Map with User=Jim, then using the methods in this section I can call getResourceFormattedRecurse passing name as "Sample.abc.foo" along with my context and get back "A formatted string with a nested property for Jim".

To implement this capability, I add the following to my ResourceSource interface:
/** Get a string from resources, recursing to do replacement of * special patterns with other resources. */ public String getResourceStringRecurse(String name); /** Get a formatted string from the resource file, including * recursive replacement of special strings. */ public String getResourceFormattedRecurse(String name, Object[] args, String[] fieldNames, Map ctx, int maxRecurse);
I add the corresponding code to my Application object:
public final static int DEFAULT_MAX_RESOURCE_RECURSE = 20; /** Get the value of a resource, replacing the special tags. * @see #replaceResourceRecurse */ public String getResourceStringRecurse(String name) { String value = getResourceStringOrNull(name); if (value==null) return name; return replaceResourceRecurse(value); } /** Get a formatted string from the resource file, including * recursive replacement of special strings. */ public String getResourceFormattedRecurse(String name, Object[] args, String[] fieldNames, Map ctx, int maxRecurse) { String fmt = getResourceString(name); if (maxRecurse>0) fmt = replaceResourceRecurse(fmt, ctx, maxRecurse); if (fieldNames!=null) fmt = mapFieldNamesToNumbers(fmt, fieldNames); return MessageFormat.format(fmt,args); } /** Given a string, look for occurrences of our special replacement * syntax and replace with the referenced value, with a * default recursion limit. * @param s The string through which we look for our special * replacement tags. The replacement tags are of the * form {@XXX} where XXX is the name of the field with * which to replace that tag. * @throws RecursionLimitException if the maximum recursion depth * is exceeded. * @see #replaceResourceRecurse(String,Items,int) */ public String replaceResourceRecurse(String s) { return replaceResourceRecurse(s, null, DEFAULT_MAX_RESOURCE_RECURSE); } /** Given a string, look for occurrences of our special replacement * syntax and replace with the referenced value. * @param s The string through which we look for our special * replacement tags. The replacement tags are of the * form {@XXX} where XXX is the name of the field with * which to replace that tag. * @param ctx The context dictionary for replacement. We first look * for XXX in this dictionary; if not found, we look for * a resource named XXX. May be null. * @param maxRecurse The maximum recursion depth. * @throws RecursionLimitException if the maximum recursion depth * is exceeded. */ public String replaceResourceRecurse(String s, Map ctx, int maxRecurse){ if (s==null) return s; int x = s.indexOf("{@"); //look for our replacements if (x<0) return s; //no replacements StringBuffer sb = new StringBuffer(); int a = 0; while (x>=0 && x<s.length()) { sb.append(s.substring(a,x)); int y = s.indexOf("}",x); if (y<0) { String eMsg = "Missing close brace "+ "in resource after "+ s.substring(x,s.length()); //TBD i18n throw new RuntimeException(eMsg); } String name = s.substring(x+2,y); String v = null; if (ctx!=null) //try the context if we have one v = (String)ctx.get(name); if (v==null) { //If no context property, try resource v = getResourceStringOrNull(name); } if (v!=null) { if (maxRecurse<=0) { throw new RecursionLimitException(name); } //Found the specified resource, recurse on it and //replace specials in it before we plug it into our string. try { v = replaceResourceRecurse(v,ctx,maxRecurse-1); } catch (RecursionLimitException ex) { //If we exceeded the recursion limit, we will be popping //back through here many times; we add on the name of //each item in the recursion chain to add in debugging. ex.setMessage(name+"->"+ex.getMessage()); throw ex; } sb.append(v); } a = y+1; x = s.indexOf("{@",a); } if (a<s.length()) sb.append(s.substring(a)); return sb.toString(); }
RecursionLimitException is a simple exception class that also allows setting the error message:
public class RecursionLimitException extends RuntimeException { private String ourMessage; /** Create a RecursionLimitException. */ public RecursionLimitException(String message) { super(message); } public void setMessage(String msg) { this.ourMessage = msg; } public String getMessage() { if (ourMessage!=null) return ourMessage; else return super.getMessage(); } }

Friday, December 5, 2008

Scala Operator Cheat Sheet

A list of the standard Scala operators.

Contents

Introduction

Scala syntax allows method names to use special characters such as +, * and :, and to use them as infix operators, which effectively allows operator overloading as well as the creation of new operators. This can make code more concise, but can also make it difficult to figure out what a particular Scala operator does, because you can't effectively do a Google search for \: or ++ or ~ or :: or pretty much any Scala operator method name. To make it even more difficult, an implicit conversion may be applied to one of the operands, so the class providing the operator method may not be the same as the declared class of the operand in the source code.

In order to simplify the task of finding Scala operators, I have collected the operator names from the standard Scala classes in this one place. When you are looking for an operator, you can just come to this page and search for the operator within this page using your browser's Find function. You will still need to figure out which of the classes that defines that operator is the right one for your situation, and whether an implicit conversion is being applied.

Of course this page can't list every operator you might find, because Scala allows new operators to be defined in a class. And there is the chance that my list is missing some operators, due either to incomplete or inconsistent documentation of a Scala class, or a bug in my extraction script. But at least it covers most of the standard operators, which should get you further along than having nothing.

Remember that operators that end in : are right associative and the invoking object appears on the right side of the operator. The unary operators + - ! ~ are defined by methods named unary_op where op is one of the four characters; look for those method names at the end of the list.

These operators were extracted from the Scala 2.7.2 final API documentation using the script given below. Many of the methods do not include a summary documentation line, so those appear blank below. Some include a summary that takes more than one line in the source file; my simple little script only picks up the first line. The links point to the on-line API documentation.

Scala Operators

!! AbstractActor
!! Actor Sends msg to this actor and immediately ...
!! Proxy
! Actor Sends msg to this actor (asynchronous).
! Channel Sends a message to this Channel.
! OutputChannel Sends msg to this ...
! Proxy Sends msg to this ...
!= Any o != arg0 is the same as !(o == (arg0)).
!= AnyRef
!= Boolean
!= Byte
!= Char
!= Double
!= Float
!= Int
!= Long
!= Short
!? AbstractActor
!? Actor Sends msg to this actor and awaits reply ...
!? Channel Sends a message to this Channel and ...
!? Proxy
% BigInt Remainder of BigInts
% Byte
% Char
% Double
% Float
% Int
% Long
% Short
% Elem Returns a new element with updated attributes, resolving namespace uris from this element's scope. ...
&&& Parsers.Parser
&& Boolean
&+ NodeBuffer Append given object to this buffer, returns reference on this NodeBuffer ...
& BigInt Bitwise and of BigInts
& Boolean
& Byte
& Char
& Enumeration.Set32 Equivalent to * for bit sets. ...
& Enumeration.Set64 Equivalent to * for bit sets. ...
& Enumeration.SetXX Equivalent to * for bit sets. ...
& Int
& Long
& Short
&~ BigInt Bitwise and-not of BigInts. Returns a BigInt whose value is (this & ~that).
&~ Enumeration.Set32 Equivalent to - for bit sets. ...
&~ Enumeration.Set64 Equivalent to - for bit sets. ...
&~ Enumeration.SetXX Equivalent to - for bit sets. ...
>>> Byte
>>> Char
>>> Int
>>> Long
>>> Short
>> BigInt (Signed) rightshift of BigInt
>> Byte
>> Char
>> Int
>> Long
>> Short
>> Parsers.Parser Returns into(fq)
>> Parsers.Parser Returns into(fq)
> BigDecimal Greater-than comparison of BigDecimals
> BigInt Greater-than comparison of BigInts
> Byte
> Char
> Double
> Float
> Int
> Long
> Ordered
> PartiallyOrdered
> Short
>= BigDecimal Greater-than-or-equals comparison of BigDecimals
>= BigInt Greater-than-or-equals comparison of BigInts
>= Byte
>= Char
>= Double
>= Float
>= Int
>= Long
>= Ordered
>= PartiallyOrdered
>= Short
<< BigInt Leftshift of BigInt
<< Byte
<< Char
<< Int
<< Long
<< Short
<< Buffer Send a message to this scriptable object.
<< BufferProxy Send a message to this scriptable object.
<< Map Send a message to this scriptable object.
<< MapProxy Send a message to this scriptable object.
<< Scriptable Send a message to this scriptable object.
<< Set Send a message to this scriptable object.
<< SetProxy Send a message to this scriptable object.
<< SynchronizedBuffer Send a message to this scriptable object.
<< SynchronizedMap Send a message to this scriptable object.
<< SynchronizedSet Send a message to this scriptable object.
< BigDecimal Less-than of BigDecimals
< BigInt Less-than of BigInts
< Byte
< Char
< Double
< Float
< Int
< Long
< Ordered
< PartiallyOrdered
< Short
< OffsetPosition Compare this position to another, by first comparing their line numbers, ...
< Position Compare this position to another, by first comparing their line numbers, ...
<= BigDecimal Less-than-or-equals comparison of BigDecimals
<= BigInt Less-than-or-equals comparison of BigInts
<= Byte
<= Char
<= Double
<= Float
<= Int
<= Long
<= Ordered
<= PartiallyOrdered
<= Short
<~ Parsers.Parser A parser combinator for sequential composition which keeps only the left result
** Enumeration.SetXX
** Set Intersect. It computes an intersection with set that. ...
** Set This method is an alias for intersect. ...
* BigDecimal Multiplication of BigDecimals
* BigInt Multiplication of BigInts
* Byte
* Char
* Double
* Float
* Int
* Long
* Short
* Set
* RichString return n times the current string
* Parsers.Parser Returns a parser that repeatedly parses what this parser parses, interleaved with the `sep' parser. ...
* Parsers.Parser Returns a parser that repeatedly parses what this parser parses
* Parsers.Parser Returns a parser that repeatedly parses what this parser parses, interleaved with the `sep' parser. ...
* Parsers.Parser Returns a parser that repeatedly parses what this parser parses, interleaved with the `sep' parser.
* Parsers.Parser Returns a parser that repeatedly parses what this parser parses
++: ArrayBuffer Prepends a number of elements provided by an iterable object ...
++: Buffer Prepends a number of elements provided by an iterable object ...
++: BufferProxy Prepends a number of elements provided by an iterable object ...
++: SynchronizedBuffer Prepends a number of elements provided by an iterable object ...
++ Array Returns an array consisting of all elements of this array followed ...
++ Enumeration.SetXX
++ Iterable Appends two iterable objects.
++ IterableProxy Appends two iterable objects.
++ Iterator Returns a new iterator that first yields the elements of this ...
++ List Appends two list objects.
++ RandomAccessSeq Appends two iterable objects.
++ RandomAccessSeqProxy Appends two iterable objects.
++ Seq Appends two iterable objects.
++ SeqProxy Appends two iterable objects.
++ IntMap Add a sequence of key/value pairs to this map.
++ LongMap Add a sequence of key/value pairs to this map.
++ Map Add a sequence of key/value pairs to this map.
++ Set Add all the elements provided by an iterator ...
++ Set Add all the elements provided by an iterator to the set.
++ SortedMap Add a sequence of key/value pairs to this map.
++ SortedSet Add all the elements provided by an iterator ...
++ Stack Push all elements provided by the given iterable object onto ...
++ Stack Push all elements provided by the given iterator object onto ...
++ TreeHashMap
++ TreeHashMap Add a sequence of key/value pairs to this map.
++ Collection Operator shortcut for addAll.
++ Set Operator shortcut for addAll.
++ ArrayBuffer Appends two iterable objects.
++ Buffer Appends a number of elements provided by an iterable object ...
++ Buffer Appends a number of elements provided by an iterator ...
++ Buffer Appends two iterable objects.
++ BufferProxy Appends a number of elements provided by an iterable object ...
++ Map Add a sequence of key/value pairs to this map.
++ MapProxy Add a sequence of key/value pairs to this map.
++ PriorityQueue
++ Set Add all the elements provided by an iterator ...
++ SynchronizedBuffer Appends a number of elements provided by an iterable object ...
++ RichString Appends two iterable objects.
++ RichStringBuilder Appends a number of elements provided by an iterable object ...
++ RichStringBuilder Appends two iterable objects.
++= Map Add a sequence of key/value pairs to this map.
++= MapWrapper Add a sequence of key/value pairs to this map.
++= ArrayBuffer Appends a number of elements in an array
++= ArrayBuffer Appends a number of elements provided by an iterable object ...
++= ArrayStack Pushes all the provided elements onto the stack.
++= Buffer Appends a number of elements in an array
++= Buffer Appends a number of elements provided by an iterable object ...
++= Buffer Appends a number of elements provided by an iterator
++= BufferProxy Appends a number of elements provided by an iterable object ...
++= Map Add a sequence of key/value pairs to this map.
++= MapProxy Add a sequence of key/value pairs to this map.
++= PriorityQueue Adds all elements provided by an Iterable object ...
++= PriorityQueue Adds all elements provided by an iterator into the priority queue.
++= PriorityQueueProxy Adds all elements provided by an Iterable object ...
++= PriorityQueueProxy Adds all elements provided by an iterator into the priority queue.
++= Queue Adds all elements provided by an Iterable object ...
++= Queue Adds all elements provided by an iterator ...
++= QueueProxy Adds all elements provided by an Iterable object ...
++= QueueProxy Adds all elements provided by an iterator ...
++= Set Add all the elements provided by an iterator ...
++= SetProxy Add all the elements provided by an iterator ...
++= Stack Pushes all elements provided by an Iterable object ...
++= Stack Pushes all elements provided by an iterator ...
++= StackProxy Pushes all elements provided by an Iterable object ...
++= StackProxy Pushes all elements provided by an iterator ...
++= SynchronizedBuffer Appends a number of elements provided by an iterable object ...
++= SynchronizedMap Add a sequence of key/value pairs to this map.
++= SynchronizedPriorityQueue Adds all elements provided by an Iterable object ...
++= SynchronizedPriorityQueue Adds all elements provided by an iterator into the priority queue.
++= SynchronizedQueue Adds all elements provided by an Iterable object ...
++= SynchronizedQueue Adds all elements provided by an iterator ...
++= SynchronizedSet Add all the elements provided by an iterator ...
++= SynchronizedStack Pushes all elements provided by an Iterable object ...
++= SynchronizedStack Pushes all elements provided by an iterator ...
++= RichStringBuilder Appends a number of elements provided by an iterable object ...
+: ArrayBuffer Prepends a single element to this buffer and return ...
+: Buffer Prepend a single element to this buffer and return ...
+: BufferProxy Prepend a single element to this buffer and return ...
+: ListBuffer Prepends a single element to this buffer. It takes constant ...
+: ObservableBuffer Prepend a single element to this buffer and return ...
+: SynchronizedBuffer Prepend a single element to this buffer and return ...
+: RichStringBuilder Prepend a single element to this buffer and return ...
+: BufferWrapper Prepend a single element to this buffer and return ...
+: RefBuffer Prepend a single element to this buffer and return ...
+ BigDecimal Addition of BigDecimals
+ BigInt Addition of BigInts
+ Byte
+ Char
+ Double
+ Enumeration.SetXX Create a new set with an additional element.
+ Float
+ Int
+ List
+ Long
+ Short
+ EmptySet Create a new set with an additional element.
+ HashSet Create a new set with an additional element.
+ ListSet.Node This method creates a new set with an additional element.
+ ListSet This method creates a new set with an additional element.
+ Map
+ Map Add a key/value pair to this map.
+ Map Add two or more key/value pairs to this map.
+ Queue Creates a new queue with element added at the end ...
+ Queue Returns a new queue with all all elements provided by ...
+ Set Add two or more elements to this set.
+ Set Create a new set with an additional element.
+ Set1 Create a new set with an additional element.
+ Set2 Create a new set with an additional element.
+ Set3 Create a new set with an additional element.
+ Set4 Create a new set with an additional element.
+ SortedMap Add a key/value pair to this map.
+ SortedMap Add two or more key/value pairs to this map.
+ SortedSet Create a new set with an additional element.
+ Stack Push all elements provided by the given iterable object onto ...
+ Stack Push an element on the stack.
+ TreeSet A new TreeSet with the entry added is returned,
+ Buffer adds "a" from the collection. Useful for chaining.
+ Collection adds "a" from the collection. Useful for chaining.
+ Map Add a key/value pair to this map.
+ Set adds "a" from the collection. Useful for chaining.
+ Buffer Append a single element to this buffer and return ...
+ BufferProxy Append a single element to this buffer and return ...
+ Map Add a key/value pair to this map.
+ Map Add two or more key/value pairs to this map.
+ MapProxy Add a key/value pair to this map.
+ MapProxy Add two or more key/value pairs to this map.
+ ObservableBuffer Append a single element to this buffer and return ...
+ PriorityQueue
+ Set Add a new element to the set.
+ Set Add two or more elements to this set.
+ SynchronizedBuffer Append a single element to this buffer and return ...
+ Parsers.Parser Returns a parser that repeatedly (at least once) parses what this parser parses.
+ Parsers.Parser Returns a parser that repeatedly (at least once) parses what this parser parses.
+= Collection adds "a" from the collection.
+= Map Add a key/value pair to this map.
+= ArrayBuffer Appends a single element to this buffer and returns ...
+= ArrayStack Alias for push.
+= BitSet Sets i-th bit to true. ...
+= Buffer Append a single element to this buffer.
+= BufferProxy Append a single element to this buffer.
+= HashSet Add a new element to the set.
+= ImmutableSetAdaptor Add a new element to the set.
+= JavaSetAdaptor Add a new element to the set.
+= LinkedHashSet Add a new element to the set.
+= ListBuffer Appends a single element to this buffer. It takes constant ...
+= Map Add a key/value pair to this map.
+= Map Add two or more key/value pairs to this map.
+= Map This method defines syntactic sugar for adding or modifying ...
+= MapProxy Add a key/value pair to this map.
+= MapProxy Add two or more key/value pairs to this map.
+= ObservableSet Add a new element to the set.
+= PriorityQueue Add two or more elements to this set.
+= PriorityQueue Inserts a single element into the priority queue.
+= PriorityQueueProxy Inserts a single element into the priority queue.
+= Queue Inserts a single element at the end of the queue.
+= QueueProxy Inserts a single element at the end of the queue.
+= Set Add a new element to the set.
+= Set Add two or more elements to this set.
+= SetProxy Add a new element to the set.
+= Stack Pushes a single element on top of the stack.
+= StackProxy Pushes a single element on top of the stack.
+= SynchronizedBuffer Append a single element to this buffer.
+= SynchronizedMap Add a key/value pair to this map.
+= SynchronizedMap Add two or more key/value pairs to this map.
+= SynchronizedPriorityQueue Inserts a single element into the priority queue.
+= SynchronizedQueue Inserts a single element at the end of the queue.
+= SynchronizedSet Add a new element to the set.
+= SynchronizedStack Pushes a single element on top of the stack.
+= RichStringBuilder Append a single element to this buffer.
+= Reactions Add a reaction.
+= RefBuffer Append a single element to this buffer.
+= CachedFileStorage adds a node, setting this.dirty to true as a side effect
+= IndexedStorage adds a node, setting this.dirty to true as a side effect
+= SetStorage adds a node, setting this.dirty to true as a side effect
-> Map.MapTo
-> Map.MapTo
-- List Computes the difference between this list and the given list ...
-- Map Remove a sequence of keys from this map
-- Set Remove all the elements provided by an iterator ...
-- SortedMap Remove a sequence of keys from this map
-- MutableIterable Operator shortcut for removeAll.
-- Set Operator shortcut for removeAll.
-- Map Remove a sequence of keys from this map
-- MapProxy Remove a sequence of keys from this map
-- Set Remove all the elements provided by an iterator ...
--= Map Remove a sequence of keys from this map
--= MapProxy Remove a sequence of keys from this map
--= Set Remove all the elements provided by an iterator ...
--= SetProxy Remove all the elements provided by an iterator ...
--= SynchronizedMap Remove a sequence of keys from this map
--= SynchronizedSet Remove all the elements provided by an iterator ...
- BigDecimal Subtraction of BigDecimals
- BigInt Subtraction of BigInts
- Byte
- Char
- Double
- Enumeration.SetXX Remove a single element from a set.
- Float
- Int
- List Computes the difference between this list and the given object ...
- Long
- Short
- EmptyMap Remove a key from this map
- EmptySet Remove a single element from a set.
- HashMap Remove a key from this map
- HashSet Remove a single element from a set.
- IntMap Remove a key from this map
- ListMap.Node Creates a new mapping without the given key. ...
- ListMap This creates a new mapping without the given key. ...
- ListSet.Node - can be used to remove a single element from ...
- ListSet - can be used to remove a single element from ...
- LongMap Remove a key from this map
- Map Remove a key from this map
- Map Remove two or more keys from this map
- Map1 Remove a key from this map
- Map2 Remove a key from this map
- Map3 Remove a key from this map
- Map4 Remove a key from this map
- Set Remove a single element from a set.
- Set Remove two or more elements from this set.
- Set1 Remove a single element from a set.
- Set2 Remove a single element from a set.
- Set3 Remove a single element from a set.
- Set4 Remove a single element from a set.
- SortedMap Remove a key from this map
- SortedMap Remove two or more keys from this map
- TreeHashMap Remove a key from this map
- TreeMap Remove a key from this map
- TreeSet Remove a single element from a set.
- UnbalancedTreeMap.Node Remove a key from this map
- UnbalancedTreeMap Remove a key from this map
- Map Remove a key from this map
- MutableIterable
- Set
- ListBuffer Removes a single element from the buffer and return ...
- Map Remove a key from this map
- Map Remove two or more keys from this map
- MapProxy Remove a key from this map
- MapProxy Remove two or more keys from this map
- Set Remove a new element from the set.
- Set Remove two or more elements from this set.
-= Buffer removes "a" from the collection.
-= Collection removes "a" from the collection.
-= Map Remove a key from this map, noop if key is not present.
-= BitSet Clears the i-th bit.
-= Buffer Removes a single element from this buffer, at its first occurrence. ...
-= HashMap Remove a key from this map, noop if key is not present.
-= HashSet Removes a single element from a set.
-= ImmutableMapAdaptor Remove a key from this map, noop if key is not present.
-= ImmutableSetAdaptor Removes a single element from a set.
-= JavaMapAdaptor Remove a key from this map, noop if key is not present.
-= JavaSetAdaptor Removes a single element from a set.
-= LinkedHashMap Remove a key from this map, noop if key is not present.
-= LinkedHashSet Removes a single element from a set.
-= ListBuffer Remove a single element from this buffer. It takes linear time ...
-= Map Remove a key from this map, noop if key is not present.
-= Map Remove two or more keys from this map
-= MapProxy Remove a key from this map, noop if key is not present.
-= MapProxy Remove two or more keys from this map
-= ObservableMap Remove a key from this map, noop if key is not present.
-= ObservableSet Removes a single element from a set.
-= OpenHashMap Remove a key from this map, noop if key is not present.
-= Set Remove two or more elements from this set.
-= Set Removes a single element from a set.
-= SetProxy Removes a single element from a set.
-= SynchronizedMap Remove a key from this map, noop if key is not present.
-= SynchronizedMap Remove two or more keys from this map
-= SynchronizedSet Removes a single element from a set.
-= Reactions Remove the given reaction.
-= CachedFileStorage removes a tree, setting this.dirty to true as a side effect
-= IndexedStorage removes a tree, setting this.dirty to true as a side effect
/% BigInt Returns a pair of two BigInts containing (this / that) and (this % that).
/: Iterable Similar to foldLeft but can be used as ...
/: IterableProxy Similar to foldLeft but can be used as ...
/: Iterator Similar to foldLeft but can be used as ...
/ BigDecimal Division of BigDecimals
/ BigInt Division of BigInts
/ Byte
/ Char
/ Double
/ Float
/ Int
/ Long
/ Short
:/: Document
::: List
:: List
:: Document
:\ Iterable An alias for foldRight. ...
:\ IterableProxy An alias for foldRight. ...
:\ Iterator An alias for foldRight. ...
== Any o == arg0 is the same as o.equals(arg0).
== AnyRef o == arg0 is the same as if (o eq null) arg0 eq null else o.equals(arg0).
== Boolean
== Byte
== Char
== Double
== Float
== Int
== Long
== Short
? Actor Receives the next message from this actor's mailbox.
? Channel Receives the next message from this Channel.
? InputChannel Receives the next message from this Channel.
? Parsers.Parser Returns a parser that optionally parses what this parser parses.
? Parsers.Parser Returns a parser that optionally parses what this parser parses.
\ NodeSeq Projection function. Similar to XPath, use this \ "foo"
\\ NodeSeq projection function. Similar to XPath, use this \\ 'foo
^ BigInt Bitwise exclusive-or of BigInts
^ Boolean
^ Byte
^ Char
^ Int
^ Long
^ Short
^? Parsers.Parser A parser combinator for partial function application
^? Parsers.Parser A parser combinator for partial function application
^^ Parsers.Parser A parser combinator for function application
^^ Parsers.Parser A parser combinator for function application
^^ Parsers.UnitParser A parser combinator for function application
^^^ Parsers.Parser
| BigInt Bitwise or of BigInts
| Boolean
| Byte
| Char
| Enumeration.Set32 Equivalent to ++ for bit sets. Returns a set ...
| Enumeration.Set32 Equivalent to + for bit sets. Returns a set ...
| Enumeration.Set64 Equivalent to ++ for bit sets. Returns a set ...
| Enumeration.Set64 Equivalent to + for bit sets. Returns a set ...
| Enumeration.SetXX Equivalent to ++ for bit sets. Returns a set ...
| Enumeration.SetXX Equivalent to + for bit sets. Returns a set ...
| Int
| Long
| Short
| Parsers.Parser A parser combinator for alternative composition
| Parsers.Parser A parser combinator for alternative composition
| Parsers.UnitParser A parser combinator for alternative composition
|| Boolean
||| Parsers.Parser
||| Parsers.Parser A parser combinator for alternative with longest match composition
||| Parsers.Parser A parser combinator for alternative with longest match composition
||| Parsers.UnitParser A parser combinator for alternative with longest match composition
~! Parsers.Parser A parser combinator for non-back-tracking sequential composition
~! Parsers.Parser A parser combinator for non-back-tracking sequential composition with a unit-parser
~! Parsers.Parser A parser combinator for non-back-tracking sequential composition
~! Parsers.UnitParser A parser combinator for non-back-tracking sequential composition with a unit-parser
~! Parsers.UnitParser A parser combinator for non-back-tracking sequential composition
~> Parsers.Parser A parser combinator for sequential composition which keeps only the right result
~ BigInt Returns the bitwise complement of this BigNum
~ Parsers.OnceParser A parser combinator for sequential composition
~ Parsers.Parser A parser combinator for sequential composition
~ Parsers
~ Parsers.OnceParser A parser combinator for sequential composition with a unit-parser
~ Parsers.OnceParser A parser combinator for sequential composition
~ Parsers.Parser A parser combinator for sequential composition with a unit-parser
~ Parsers.Parser A parser combinator for sequential composition
~ Parsers.UnitOnceParser A parser combinator for sequential composition with a unit-parser
~ Parsers.UnitOnceParser A parser combinator for sequential composition
~ Parsers.UnitParser A parser combinator for sequential composition with a unit-parser
~ Parsers.UnitParser A parser combinator for sequential composition
unary_! Boolean
unary_+ Byte
unary_+ Char
unary_+ Double
unary_+ Float
unary_+ Int
unary_+ Long
unary_+ Short
unary_- BigDecimal Returns a BigDecimal whose value is the negation of this BigDecimal
unary_- BigInt Returns a BigInt whose value is the negation of this BigInt
unary_- Byte
unary_- Char
unary_- Double
unary_- Float
unary_- Int
unary_- Long
unary_- Short
unary_~ Byte
unary_~ Char
unary_~ Int
unary_~ Long
unary_~ Short

Extraction Script

I ran this script under Mac OS X 10.4.11, directed the output to a file, and included that file in this post.
#!/bin/sh - #Extract scala operator names from scaladoc files. #cd to the "api" directory containing the scala API docs, then #run this script and redirect stdout to a file. docsubdir=scala TROOT="http://www.scala-lang.org/docu/files/api" find $docsubdir -name \*.html | \ xargs grep -A 4 'class="signature"' | \ grep -E " <em><a href.*_self|<div>" | \ grep -A 1 -E '<em>.*"_self">([^a-zA-Z0-9_]|unary_)' | \ grep -v '^--' | \ sed -e 's|<em>.*>\(.*\)</a></em>|\1|' | \ sed -e 's/html-/html/' | \ sed -e 's/.*\(<div>.*\)/\1/' | \ uniq | \ awk ' !/<div>/ { if (d!="") { print d; d="" } } {d=d" "$0} ' | \ sed -e 's/^[ ]*//' | \ sed -e 's|\(<div>.*[^>]\)$|\1 ...</div>|' | \ sed -e 's/<div>/<span>/' -e 's|</div>|</span>|' | \ sort +1 | uniq | \ sed -e 's|^\([^ ]*/\)\(.*\).html[ ]*\([^ ]*\)\(.*\)$|<br/><b>\3</b> <a href="'$TROOT'/\1\2.html">\2</a>\4|' | \ grep -v '^<br/><b>[a-tv-zA-Z]' | \ grep -v '[a-zA-Z]\$[a-zA-Z]' | \ sed -e 's/unary_/~~~~unary_/' | sort | sed -e 's/~~~~unary_/unary_/' | \ cat -
Updated 2008-12-21: Added summary line before Contents list.