Having fun with ooc
👋 This page was last updated ~13 years ago. Just so you know.
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
:
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:
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 ListInt
to String
before
calling it.
Extending classes and covers
Always with the same output, this program is a bit more convoluted:
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.
Here's another article just for you:
Peeking inside a Rust enum
During a recent Rust Q&A Session on my twitch
channel, someone asked a question that
seemed simple: why are small string types, like SmartString
or SmolStr
,
the same size as String
, but small vec types, like SmallVec
, are larger
than Vec
?
Now I know I just used the adjective simple, but the truth of the matter is: to understand the question, we’re going to need a little bit of background.