
Is Go an Object-Oriented Language? - spf13
http://spf13.com/post/is-go-object-oriented
======
optymizer
The problem with the author's case for Go's is-a relationships is that it
breaks down the moment you want to pass the object to a function expecting the
original object.

For example,
[http://play.golang.org/p/EmodogIiQU](http://play.golang.org/p/EmodogIiQU)

    
    
        type A struct { }
        type B struct { A }  //B is-a A
    
        func save(A) { //do something }
     
        b := &B{}
    
        save(b);  //OOOPS! b IS NOT A
    

If Go had is-a relationships, the code above would be valid. Instead, Go only
implements has-a relationships, and simply provides shortcuts to calling
B.A.foo() as B.foo().

One _could_ create B.save(), which would call save(b.A), but the very reason
you're now proxying the call to save() is because there is no is-a
relationship in Go.

We all know about interfaces, but the problem is that is-a relationships do
exist, and you can't always use interfaces, because often you want to share
the data encapsulated by the objects, not only the behavior. One ends up
creating methods to fetch each piece of data, but in code that is supposed to
be performant, calling methods instead of accessing fields is suboptimal.

~~~
tolmasky
In my experience its always really telling that there are no incredibly simple
is-a relationships to use in inheritance justifications (as opposed to tons of
goto real world examples for just about every other feature under the sun).
Its always either incredibly abstract (B is-a A), or incredibly contrived
(Triangle is-a Shape). I've spent a lot of time in inheritance heavy code, and
I've yet to find something that wouldn't be just as, if not more, elegant
without inheritance. I've spent the most time in Cocoa (which I believe to be
very well designed BTW), but the inheritance there is clearly not needed IMO.
I usually find one of the following to be true:

1) Very shallow inheritance trees that could have very easily (and more
logically) been replaced with interfaces. For example, NSResponder being
everything's superclass even though just about all of its methods are empty
implementations ("subclassers responsibility"), aka, it clearly should have
been an interface.

2) Confused/strangely complex is-a relationships (mutable array is-a immutable
array, what? so if I specifically specify NSArray, I may still get a mutable
array and the type system will be happy???).

3) Strange rules around what methods are overridable, and more importantly,
how specifically they can be overridden. Why can't I in a UIView subclass
override -subviews to ensure that it always has the same subviews? Well,
implementation detail, that's why. Normally this would be fine, but since
anyone is allowed to muck around in a subclass, suddenly I need to know _the
way it was implemented_. This of course conflicts other parts of the framework
where you are definitely expected to override the method and not use a setter.

I have yet to be presented with one of these "killer" is-a relationships that
_must_ exist. If the sole excuse is "performance", then sure I'll conceded. I
guess I don't work in environments where member access is the performance
bottleneck so I guess I can't relate.

~~~
pcwalton
> In my experience its always really telling that there are no incredibly
> simple is-a relationships to use in inheritance justifications (as opposed
> to tons of goto real world examples for just about every other feature under
> the sun). Its always either incredibly abstract (B is-a A), or incredibly
> contrived (Triangle is-a Shape). I've spent a lot of time in inheritance
> heavy code, and I've yet to find something that wouldn't be just as, if not
> more, elegant without inheritance.

The flow tree (render object tree) in Servo (or any other browser engine) must
use inheritance: we have a heterogeneous tree of objects that all share a
common set of fields (position, intrinsic widths, collapsible margins, some
various bits that store state during reflow), but they all use virtual methods
because they must lay out their contents differently.

We can't use composition because we wouldn't get virtual methods. We can't use
an interface because then we would be forced into virtual dispatch for all of
those fields that are shared between flows.

Rust doesn't have OO yet either, so we're forced to hack around it in weird
ways (usually via a small amount of unsafe code to simulate inheritance).

> I have yet to be presented with one of these "killer" is-a relationships
> that must exist. If the sole excuse is "performance", then sure I'll
> conceded. I guess I don't work in environments where member access is the
> performance bottleneck so I guess I can't relate.

A browser engine is exactly that sort of environment. Forcing all member
access to go through virtual dispatch would murder the performance of any
browser.

Note that this was exactly the sort of thing that OO was designed for in
Simula: heterogeneous trees of objects that all share some common fields but
have different virtual methods. This generalizes to GUI libraries, game worlds
etc—in short, simulations :)

~~~
tolmasky
_> The flow tree (render object tree) in Servo (or any other browser engine)
must use inheritance: we have a heterogeneous tree of objects that all share a
common set of fields (position, intrinsic widths, collapsible margins, some
various bits that store state during reflow), but they all use virtual methods
because they must lay out their contents differently._

Haven't used Servo, but one of the big eye opening composition experiences for
me was Unity's Scene Graph. Whereas Cocoa uses an inheritance model for its
view-tree, Unity has a tree of transforms that you do not subclass or change
in any way, and then you add behaviors to those transforms. If you want it to
render, you can attach a renderer, if you want to hit test, you attach a
collider. If you want any arbitrary other thing to happen, you create that
behavior. Its really nice, the idea of "tree" is completely separate from all
other concepts. Rendering a 3D game, on mobile, at 60fps (on GC-ed Mono no
less), makes me feel pretty good about its performance characteristics. Most
our perf issues were with limiting draw calls and optimizing shaders, not
method calling.

Similarly, I worked a lot on a browser engine in the past and virtual method
dispatch was again not this clear cut performance killer.

~~~
pcwalton
> Most our perf issues were with limiting draw calls and optimizing shaders,
> not method calling.

Sounds like the work done by tree traversals weren't high overhead in general
for your workload. But it does matter for some workloads.

> Similarly, I worked a lot on a browser engine in the past and virtual method
> dispatch was again not this clear cut performance killer.

We're seeing large gains from, as far as we can tell, having fewer virtual
method calls than other engines. Eliminating virtual dispatch opens up a huge
range of call-site optimizations since the methods can often be statically
inlined (as well as reducing the load on the branch target buffer).

> Similarly, I worked a lot on a browser engine in the past and virtual method
> dispatch was again not this clear cut performance killer.

That doesn't match my experience. Devirtualization opens up lots of inlining
opportunities, and inlining is one of the most critical optimizations that
compilers can do (mostly because of the other optimizations that it opens up;
e.g. const propagation, GVN, etc. etc.)

See this study: [http://hubicka.blogspot.com/2014/04/devirtualization-in-c-
pa...](http://hubicka.blogspot.com/2014/04/devirtualization-in-c-
part-5-feedback.html)

Devirtualization optimizations improve Dromaeo by 7-8%. That's a significant
win, especially since devirtualization is only a best-effort optimization and
Dromaeo has a lot of JS in it.

------
adamlett
I would say the defining property of OO, is polymorphism. It's the property
that allows some code to call a Bar() method on object Foo and not be
concerned with the exact type of object Foo. Without polymorphism, there is
little differnce between Bar.Foo() and Foo(Bar).

Implementation inheritance is a property of _some_ OO languages and one that
is hard to imagine separate from OO. Which is why perhaps so many insist upon
it being a required property for some language to be called OO. I am firmly in
the camp that thinks implementation inheritance is a bad idea and it is best
to avoid it even in langauges that support it. Thus I don't agree with anyone
who claims that it is an important characteristic of a language.

Whether object instances find their genesis in classes, factories or
prototypes are IMO the least important aspect to consider when discussing
whether or not some language is truly OO. It's the object instances that do
the important work. Where they came from is not so interesting.

~~~
abrahamsen
> I would say the defining property of OO, is polymorphism.

Make it dynamic polymorphism, and I agree.

~~~
groovy2shoes
Subtype polymorphism. Dynamic dispatch.

[http://en.wikipedia.org/wiki/Subtype_polymorphism](http://en.wikipedia.org/wiki/Subtype_polymorphism)

[http://en.wikipedia.org/wiki/Dynamic_dispatch](http://en.wikipedia.org/wiki/Dynamic_dispatch)

Polymorphism is a feature of the type system, and thus inherently static.
Dynamic dispatch is something you often wind up with as a consequence of
subtyping, but neither requires the other, strictly speaking.

------
asgard1024
I have to say, I like Go model better than "standard" OOP (as in Java, C#..).

Class is a single construct used for three different abstractions, namely:

\- modularity/hiding

\- inheritance

\- polymorphism

This eventually turned out to be a bad idea (as evidenced by all the mess with
virtual methods, multiple inheritance and structural patterns), and interfaces
(and namespaces) were added to partly remedy this.

In Go, you instead get three orthogonal constructs:

\- modules

\- embedded structures

\- interfaces

These directly correspond to basic principles of OOP. Nice and clean.

~~~
dragonwriter
I like that Go and Rust reexamined some of the underlying traditions of the
C++/Java/C# approach to OOP rather than reflexively repeating them -- and
while I think Go and Rust both, on a high level, took good (but very
different) approaches, I think the one thing that Rust did right that would be
better _even with the rest of Go 's approach_ than the way Go did it is
explicit and detached declaration of interface implementations for data types.

~~~
NateDad
So you're saying you'd prefer it if you had to explicitly tell Go that your
type implements an interface? Something like this?

    
    
      type Shape interface {
          Area() int
      }
    
      type Square struct {
         sideLen int
      }
    
      // Somehow denote that this function is implementing
      // Shape's Area function
      func (s Square) Shape.Area() int {
          return sideLen * sideLen
      }
    

Because, one of the things I like best about Go's interfaces is that you _don
't_ have to do that.

~~~
dragonwriter
> So you're saying you'd prefer it if you had to explicitly tell Go that your
> type implements an interface?

I'd prefer that to having to avoid using the most natural names for methods to
avoid implementing an unintended interface -- it seems to me that Go's
approach in this area makes easy things easier and hard things harder.

~~~
NateDad
Why do you care if you accidentally implement an interface?

~~~
dragonwriter
Because conforming to the signatures necessary to implement an interface
doesn't imply confirming to its semantics, so accidental interface
"implementations" are a type-safety problem.

~~~
NateDad
They're really not. You still have to have someone pass your type into
something expecting an interface. It's no different than passing a string into
something expecting a path when you pass it an html document... at some point
the programmer needs to decide if passing the value into the method makes
sense.

In the canonical example:

    
    
      type Boat interface {
          Launch()  
      }
    
      func LaunchBoat(b Boat) {
         // do some boat stuff
    
         b.Launch()
      }
    
      type NuclearMissile struct{}
    
      func (nm NuclearMissile) Launch() {
          // launch nuclear missile
      }
    
      func main() {
          rocket := NuclearMissile{}
          LaunchBoat(rocket)
      }
    

Sure, this _compiles_ , but the code doesn't make any sense. Lots of things
compile, that doesn't mean the code makes sense. At some point it's the
programmer's responsibility to think a little.

~~~
dragonwriter
> You still have to have someone pass your type into something expecting an
> interface.

Yes, that's generally the case with type-safety problems, even with the type-
safety problem existing, it takes an actual programming error for it to become
a problem -- and that is, indeed, the standard response of people saying type-
safety isn't important (usually, though, its not a reason people would say
something isn't a type-safety problem.) And its not completely invalid --
there is a reason that in a world dominated by static-typed languages with
limiting type systems, dynamic languages like Ruby and Python that don't offer
type safety but do offer a lot of flexibility that the type systems of
C++/Java/etc. made, at best, cumbersome to acheive.

OTOH, if you are choosing a language with the extra ceremony involved in
static typing, its kind of a big step back to not even get the level of safety
with interfaces that you'd get with C#/Java, much less a more modern,
expressive static type system.

------
jnks
The author of this blog post is a little confused about embedded structs. His
examples of _has-a_ and _is-a_ are both _has-a_ 's, and the syntax change
involved (leaving off a name for the embedded type) doesn't actually do
anything.

    
    
      type Person struct {
         Name string
         Address Address
      }
    

is equivalent to

    
    
      type Person struct {
         Name string
         Address
      }
    

And in fact, these are equivalent too:

    
    
      p.Address.Zip = "01313"
    
      p.Zip = "01313"
    

[http://play.golang.org/p/aKH3YxT5Mb](http://play.golang.org/p/aKH3YxT5Mb)

Go doesn't really support _is-a_ for structs, as pointed out elsewhere in the
comments here. Interface implementation is the only way to get the sort of
"this type can be substituted for this other type" idea that _is-a_
inheritance provides in other languages.

------
ChuckMcM
Reminds me of the (possibly) apocryphal story of Nickolas Wirth telling his
audience at Apple that Modula-2 was OO and having one of the audience members
object. To which Dr. Wirth replied, "Who are we to say what object oriented
means exactly?" and the objector, who turns out to be Alan Kay, says, "Well I
invented the term so I get to define it, this isn't object oriented."

I would say that similar objections would be made about Go calling it self
'object oriented' however I also don't know what is being asserted.

Go has many constructs that make abstraction easier, and that is what many
programmers want out of the OO idea, so its fine. I'm sure there specific
things that some people require before they will label something as OO. Is it
a functional question or a religious question as to whether or not Go is
Object Oriented?

~~~
jasode
>I'm sure there specific things that some people require before they will
label something as OO.

For many folks, it's basically _built-in language syntax_ for objects
(data+methods) instead of using patterns, idioms, and conventions. The object-
oriented nature is _explicit_ with syntax of language keywords instead of
_implicit_ with code organization.

>Is it a functional question or a religious question as to whether or not Go
is Object Oriented

I think it's more of a functional/pragmatic one and not religious. (I would
substitute the word "religious" for "psychological" \-- more on that in the
next paragraph). If a language is designated as "object-oriented", I think
it's reasonable to have some expectations that the programmer _does not_ have
to write idioms & patterns to emulate C++/C# type of objects.

That said, there are still _psychological_ motivations for expanding "object-
oriented" to describe what Go can do. The problem is that the term "object-
oriented" has gained a lot of currency as something useful and desirable in
the programming world. Therefore, if someone labels something (e.g. Go) as
"not object-oriented", that has an implied judgement that Go is somehow
"handicapped" and has less power than C++/C#/etc.

Ideally, all programmers would treat the following statements as something
neutral and non-threatening: " _Go is not object-oriented. C language not-
object-oriented._ "

But since we can't (the psychology), we get articles explaining how C and Go
are actually object-oriented after all. We do this, that, and the other thing,
and voila, "C is object oriented."

~~~
jeremyjh
>Therefore, if someone labels something (e.g. Go) as "not object-oriented",
that has an implied judgement that Go is somehow "handicapped" and has less
power than C++/C#/etc.

But that is precisely the point of Go: it IS less powerful. It has no
inheritance, no generics or templates, no macros or pre-processor, no raw
pointer access, no exceptions etc. This is all completely intentional, and yes
it will be a turn-off to many.

~~~
jasode
Understood.

But then people can just shift the argument around to the word "powerful".
Unlike electricity where "power" is composed of just 2 dimensions (voltage and
current), "power" in a language has hundreds of dimensions.

If for one programmer, the "interesting" things in Go include ultra fast
compile times, builtin concurrency primitives, network library, etc, then to
him, " _Go is more powerful than C++._ "

The disagreement over what dimensional components of "powerful" is worth
comparing then feeds more debates (and implied judgments of language
worthiness).

------
jbert
Something I've done in some golang code recently is to fake some OO features.
I'd appreciate some commentary on the approach.

So I want a few different, but similar things. These are actually stages in a
processing pipeline, each stage doing different processing steps.

What I'm currently doing, which mostly works well, is to have a struct type
('Stage') which does all the generic work (equivalent to an abstract base
class in C++). The Stage contains a function ptr ('Each') to actually do the
processing step.

I can then have various 'derived' types which embed 'Stage'. Each one is
assembled via a ctor which sets up the 'Each' function ptr. Effectively this
provides inheritance with method overloading for the Each function.

I also have an interface ('Stager') which is satisfied by the Stage type, and
so consequently by all the derived types (since they embed Stage).

So, I seem to have most of the benefits of C++ abstract base class and
'inheritance' (of data and methods), including overriding of methods by
subclasses (using explicit assignment to function ptr).

It feels pretty nice to work with. The main concern I have is the slight
klunkiness of the Stage/Stager duality. I also don't think I'd like this if I
had many overridden methods (I just have one atm).

Anyone care to comment on a better way to this or other critique?

~~~
NateDad
I think you'd do better making the stages into plain functions that take an
interface... why do the stages need to be structs at all?

~~~
jbert
It's really the code to connect the stages. I want to support 1->many, so a
single stage can fan out it's output to multiple consumers.

The code which wants to be generic is in the handling of things like setting
up channels between stages, handling data passing between them and
cancellation (shutdown).

I also want to support a layer of abstraction, where I can compose a graph of
stages into a single stage.

The goal is to have a number of primitive processing stages and allow
abstraction and composition to build more complex processing.

Basically I could move to a pure interface and associated functions (not
methods). That would perhaps be more idiomatic go. It's just a slight shame
that a bunch of code which lives to my mind slightly more naturally as a set
of methods on a type gets promoted to top-level package functions due to the
inabillity to define methods on an interface. But that's probably OK.

------
chimeracoder
> Since a standard definition doesn’t exist, for the purpose of our discussion
> we will provide one.

Who said a standard definition doesn't exist? From Alan Kay, who 'invented'
object-orientation and coined the term:

> OOP to me means only messaging, local retention and protection and hiding of
> state-process, and extreme late-binding of all things. It can be done in
> Smalltalk and in LISP. There are possibly other systems in which this is
> possible, but I’m not aware of them[0].

Yes, you can make the argument that the term has evolved in common parlance
beyond what Kay originally conceived of, but it's silly to propose a "modern"
definition of object-orientation and not at least mention the original
definition.

[0] From a 2003 email: [http://userpage.fu-
berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...](http://userpage.fu-
berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en)

[1] Note that he does _not_ mention Java, even though he write this during the
height of Java's popularity:
[http://www.tiobe.com/index.php/paperinfo/tpci/Java.html](http://www.tiobe.com/index.php/paperinfo/tpci/Java.html)

~~~
vidarh
Kay's definition is by no means "standard". "Standard" usage of the term
started deviating from Kay's definition almost as soon as it was conceived. By
time the term was widespread, it already meant something different to what he
envisioned.

It may be "silly" not to mention the original, but in this context the
original a distraction: the original definition would exclude pretty much
every language we today tend to consider object-oriented.

~~~
kd0amg
The blog post gives us, "To me this feels very much like an object. I am able
to create a structured data type and then define methods that interact with
that specific data." Defining OOP as functions that consume structured data
fails to exclude a lot of languages we don't consider object-oriented.

------
AUmrysh
You can do this same thing in C to get Objects.

Also I believe there may be a typo in your last paragraph

>while staying clear of the brittle mess than is inheritance

It should be "that" instead of "than", I think.

~~~
jerf
C is not an object-oriented _language_ because it has no language features
intended to make objects easier to use or work with. You can't actually attach
"methods" to structs in C.

You can use objects in C, and there's numerous in-the-wild big examples. But
you have to implement it yourself or use some library, and there isn't one
"standard" for the language.

Go clearly does have some features designed to make objects a first-class
element. Equally clearly there are some other languages that have "more" such
features.

This post I'm making is merely descriptive; there's no positive or negative
attached to these statements.

~~~
slm_HN
"You can't actually attach "methods" to structs in C."

You can have structure members that are function pointers which is basically
attaching a "method".

~~~
pdpi
You still have to explicitly pass a `this` to that function, which is kind of
my earmark for telling methods and functions apart. (In this regard, Python
treads a fine line where it has an explicit `self` parameter that is passed in
automatically).

~~~
knome
You _can_ manually pass in the `self` parameter to unbound methods if you
like.

    
    
        import itertools
    
        class Test():
            def __init__( self, number ):
                self._number = number
                return
    
            def aaa( self ):
                print 'AAA<%s>' % str( self._number )
                return
    
            def bbb( self ):
                print 'BBB<%s>' % str( self._number )
    
        def main():
            instances = [ Test( n ) for n in range( 100 ) ]
            functions = [ Test.aaa, Test.bbb ]
    
            for instance, function in zip( instances, itertools.cycle( functions ) ):
                function( instance )
    
        if __name__ == '__main__':
            main()

------
ben336
I think the "object"-less OO tag is a bit overblown. Structs are objects. Just
because Go creators didn't choose to name them that doesn't mean there's a
fundamental difference between Go structs and other languages' objects.

~~~
grey-area
The fundamental difference (and there is one), is that Go omits the concept of
inheritance, preferring composition instead. So in contrast to many mainstream
languages today - Java, Obj-C, C#, C++, Ruby, Python, PHP , etc. there is no
concept of inheritance, only composition of objects and conforming to
interfaces.

There's a good quote in the article about this under 'Inheritance Is Best Left
Out' \- James Gosling responds to someone asking what he'd change in Java in
retrospect - _“I’d leave out classes,”_ he replied.

~~~
voidlogic
>>The fundamental difference (and there is one), is that Go omits the concept
of inheritance, preferring composition instead

Right, but that doesn't mean Go is not object oriented, it simply eschews
inheritance for composition. Go also offers something else n lieu of
inheritance that many other OO languages don't have, embedding.

------
Jare
Surprised there is no mention of hiding or message passing, which are two more
common aspects of Object Orientation. Without them, I think the idea that
methods are "attached" to objects is rather incomplete.

~~~
jdmichal
More common, but certainly not required. For instance, JavaScript does not
provide hiding at all as part of its OO mechanisms. (Implementations that
allow hiding use closures to provide it.)

~~~
Jare
Exactly, that would fit with the tone of the article, which does away with the
idea of OO defined in terms of axioms like inheritance, etc. and instead
explores how these pieces of the OO picture work and relate to each other.

------
kd0amg
_What have’t we done._

Attached that behavior to the struct itself so that invoking a struct's "area"
function could give any of several different behaviors depending on exactly
what "rect" struct you have.

~~~
jerf
To do that in Go, you just add:

    
    
        type Area interface {
            area() int
        }
    

though I'd note I'm deliberately just copying the article as written, as an
"area" that's confined to an int is awfully weird.

Note you can indeed just add that, and you're able to take "Area"s anywhere
you like, and you can even do it in a different module entirely; nothing has
to "declare" than a rect implements Area.

To forstall the usual next question, no, Go has no further overloading based
on type or anything else. My personal advice if you want to use that a lot is
to use a different language. I like Go, but I look on the people trying to use
it for machine learning or matrix math or other intensely mathematical
computational loads with a bit of mystification. It isn't what it's good for,
and I see no sign the core devs even consider it a marginal use case (to say
nothing of a core use case) and have no intentions of changing the language to
make this easier. If you really, really _need_ overloading for your core use
case, pick something else. Go is built for the environments where overloading
is generally dangerous and used by people to do excessively "clever" things on
the server, not for the environments where it is necessary. (Or perhaps
"environment", singular, since "intensely mathematical code" is the only such
thing I know of; everywhere else I've ever seen it it's asking for trouble.)

~~~
rakoo
> Go has no further overloading based on type or anything else

You _can_ overload in Go: see for example how a zlib compressor is implemented
[0].

The gist is that you take a standard io.Writer, embed it in your struct, and
override the Write() method. This way you have a new io.Writer you can use
wherever a io.Writer is needed. This pattern is actually standard in Go (and I
guess in other languages where interfaces are more important than
implementations)

Or maybe I didn't understand ?

[0]
[http://golang.org/src/pkg/compress/zlib/writer.go?s=4340:439...](http://golang.org/src/pkg/compress/zlib/writer.go?s=4340:4391#L136)

~~~
jerf
That's embedding. If it's a replacement for any traditional OO concept, it's
"inheritance", not overloading. (It _isn 't_ a replacement, but if you found
yourself _needing_ inheritance, embedding is what you'd use to get the
closest.) Overloading would allow multiple definition of the same function
name that are dispatched in various ways based on the types being called.

I show this only for example because it's horrifying Go code, but the closest
go equivalent would be:

    
    
        func OverloadedSomething(params ...interface{}) interface{} {
            // (int, int) int
            if len(params) == 2 {
                a, isAInt := params[0].(int)
                b, isBInt := params[1].(int)
                if isAInt && isBInt {
                    return a + b
                }
            }
    
            // (string) string
            if len(params) == 1 {
                aStr, isAStr := params[0].(string)
                if isAStr {
                    return aStr + " world"
                }
            }
    
            // etc etc
    
            panic("OverloadedSomething not given something it can resolve")
        }
    

Which could then be called like:

    
    
        sum := OverloadedSomething(1, 2).(int)
        helloWorld := OverloadedSomething("hello").(string)
    

I may have the ... on the wrong side of the interface{} in the params.

The Go thing to do is to declare separate functions for each implementation.
Other languages have support for doing that sort of resolution at compile
time, so you don't get the obvious run-time hit you'd take trying to do that
in Go.

------
NateDad
This is honestly kind of a dumb question, because the answer depends on who is
asking the question. Object oriented has become such an overused and little
understood term, that you can't just give an answer without trying to discern
what the person asking is looking for. There's almost always a better question
to ask than "Is x language object oriented?".

