Clojure Protocols can extend final Java classes (like java.lang.String)
clojure.org
clojure.org
System.in, out and err (the stdio streams) are all final
variables. They didn't used to be, but some clever applet-writer
realized that you could change them and start intercepting all output
and do all sorts of nasty stuff. So, the whip-smart folks at Sun went
and made them final. But hey! Sometimes it's okay to change them! So,
they also added System.setIn, setOut, and setErr methods to change
them!
``Change a final variable?!'' I hear you cry. Yep. They sneak in
through native code and change finals now. You might think it'd give
'em pause to think and realize that other people might also want to
have public read-only yet privately writable variables, but no.
Oh, but it gets even better: it turns out they didn't really have to
sneak in through native code anyway, at least as far as the JVM is
concerned, since the JVM treats final variables as always writable to
the class they're defined in! There's no special case for
constructors: they're just always writable. The javac compiler, on the
other hand, pretends that they're only assignable once, either in
static init code for static finals or once per constructor for
instance variables. It also will optimize access to finals, despite
the fact that it's actually unsafe to do so.Can someone explain how this worked? What does he mean by "intercepting all output"?
This is a very powerful trick. However the downside is that it is in the category of things where only one person gets to be clever. Because the second person to replace that object breaks the first. So they decided to discourage that.
(defprotocol Foo
(bar-ify [this]))
(extend-type java.lang.String
Foo
(bar-ify [this] (.concat this "Bar")))
(bar-ify "Hello world!")
Everything can be redefined interactively at the REPL. Also due to Rich Hickey's mad skills you get pretty much Java method dispatch performance.EDIT: There is a gross misunderstanding about how this works. The following is invalid and will throw an exception, please take the time to understand this:
user> (ns baz)
baz> (bar-ify "foo")
Your changes will not affect use of java.lang.String in other libs. user> (defprotocol Foo (describe [object]))
user> (extend-type java.lang.String
Foo
(describe [string] "a String"))
(ns bar)
bar> (user/describe "String") -> "a String"
bar> (extend-type java.lang.Object
user/Foo
(describe [obj] "an Object"))
bar> (in-ns 'user)
user> (describe 3) -> "an Object"
user> (describe "Hello") -> "a String"
So there is potential for two libraries extending the same things and causing a conflict if the semantics differ, but I'm not too concerned about that. It would be rather rude to extend someone else's protocol to cover someone else's java class without documenting that you do so.If your job is to deliver and maintain such an API, then by allowing subclassing you have a larger contract you have to maintain when you want to make changes to the underlying class: rather than just the public interface, you also need to worry about the protected interface. If your API is new, you may want to shepherd your users (by marking a class as final) to favor composition over inheritance to maximize your ability to evolve the implementation in ways that don't break.
EDIT: I'm not sure if it is the case or not; I can't find definitive documentation and I don't have a REPL available. Anyone know for sure?
if you try to set a final field you'll get an exception as expected.
protected members when everything is immutable is pointless. Clojure cleanly separates values and identities.
Except when used to mark the boundary between a stable public API and an internal API that is free to change between releases.
When I release a new major version of Foo Lib, and your program refers to private members, your program will break when I remove or rename them.
Immutability won't protect you from that.
EDIT: Ha, i just stumbled on this old yegge article, which is quite appropriate i think. With clojure APIs you think in verbs, so nouns are not that important
http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom...
EDIT2 : perfect quote :
"In the remotest regions, beyond the Functional Kingdoms, lies a fabled realm called Lambda the Ultimate. In this place it is said that there are no nouns at all, only verbs!"
here you go ;)
I already live in that Kingdom, so when I say "members", I mean member variables or member functions.
In other words, if you allow subclassing, then you also have to worry about the interface that you have explicitly marked visible to subclasses.
Normally a non-problem, by definition.
final is useful when sealing a class that inherits from a parent class with a protected interface, but even if "final" is reduced to an annotation (or even a comment), its presence explicitly limits your end of the API contract.
AFAIK there is also some kind of caching involved to improve performance.