Unfortunately, the ooc language could have better documentation. In the meantime, I'd like to blog about about some features that might not be very well-known.

Nested functions

Here's a program that prints 1, 3, 5, 7, 9:

ooc
import structs/ArrayList

main: func {

  list := ArrayList<Int> new()

  addIfOdd := func (i: Int) {
    if (i % 2 != 0) list add(i)
  }

  for (i in 0..10) {
    addIfOdd(i)
  }

  list map(|i| i toString()) join(", ") println()

}

If you're doing something repeatedly, and it won't be too readable with a closure, you can simply declare a nested function! Note that we don't make use of the GNUC extension here, the ooc compiler unwraps it as a proper closure, with a context and all.

Filter, map, join, backslash line continuations

That program prints exactly the same thing, but it does it in fewer lines:

ooc
import structs/ArrayList

main: func {

  list := ArrayList<Int> new()
  for (i in 0..10) list add(i)

  list filter(|i| i % 2 != 0) \
       map(|i| i toString()) \
       join(", ") \
       println()

}

As in most languages, you can call the filter (only retain element if the condition is true) and map (create a new list with elements returned by the function) methods on lists.

join also works on List, which is why we have to map from Int to String before calling it.

Extending classes and covers

Always with the same output, this program is a bit more convoluted:

ooc
import structs/[ArrayList, List]

extend Range {
  list: func -> ArrayList<Int> {
    list := ArrayList<Int> new()
    for (i in min..max) list add(i)
    list
  }
}

extend List<Int> {
  filterEven: func -> List<T> {
    filter(|i| i as Int % 2 != 0)
  }

  println: func {
    map(|i| i as Int toString()) join(", ") println()
  }
}

main: func {
  (0..10) list() filterEven() println()
}

The point of this example is to show that even though ooc is a static language, and even if you use types defined in external libraries, you can add methods to it. They'll only be accessible from modules where you import the module containing the extends, and they aren't overloadable by subclasses, etc. etc.

They're basically syntactic sugar! Sweet, sweet syntactic sugar.

Note that in that example, we extend both a cover, ie. Range, and a generic class, ie. List<T>. As a reminder, covers are by-value, and they usually have a 1-1 mapping with underlying C types (e.g. Int, Float, Double are all covers), or they define a structure (like Range, in this case).

As you can see, extending a cover works quite well, but for generic classes, the compiler ignores the Int in extend List<Int>, so within filterEven and println we have to cast i to Int ourselves. Not very clean if you ask me, but it does work.

Conclusion

You've seen three ways to have fun with ooc. It doesn't stop there of course, I might continue blogging about fun features you can benefit from in your ooc programs.