Can someone point me to examples of multiparadigm (object-functional) programming in F#? - oop

Can someone point me to examples of multiparadigm (object-functional) programming in F#?
I am specifically looking for examples which combine OO & functional programming. There's been a lot of talk about how F# is a hybrid language but I've not been able to find examples which demonstrate the example of multiparadigm programming.
Thanks

I made a small (600 lines) Tetris clone with F# that is Object Oriented using XNA. The code is old (uses #light) and isn't the prettiest code you will ever see but it's defiantly a mix of OOP and functional. It consists of ten classes. I don't think I pass any first class functions around but it's a good example of the F#'s functional power in programming the small.
MyGame - Inherits XNA main game class and is the programs entry point.
Board - Keeps track of pieces that are no longer moving and horizontal line completes.
UI - The UI only has two states (playing and main menu) handled by bool stateMenu
Tetris - Handles game state. Game over and piece collision.
Piece - Defines the different Tetris shapes and their movement and drawing.
Player - Handles user input.
Shape - The base graphic object that maps to primative.
Primative - Wraps the Vertex primitive type.
I made a rough class diagram to help. If you have any questions about it feel free to ask in the comment section.

There are two ways of combining the functional and object-oriented paradigm. To some extent, they are independent and you can write immutable (functional) code that is structured using types (written as F# objects). An F# example of Client type written like this would be:
// Functional 'Client' class
type Client(name, income) =
// Memebers are immutable
member x.Name = name
member x.Income = income
// Returns a new instance
member x.WithIncome(ninc) =
new Client(name, ninc)
member x.Report() =
printfn "%s %d" name income
Note that the WithIncome method (which "changes" the income of the client) doesn't actually do any modifications - it follows the functional style and creates a new, updated, client and returns it as the result.
On the other hand, in F# you can also write object-oriented code that has mutable public properties, but uses some immutable data structure under the cover. This may be useful when you have some nice functional code and want to expose it to C# programmers in a traditional (imperative/object-oriented) way:
type ClientList() =
// The list itself is immutable, but the private
// field of the ClientList type can change
let mutable clients = []
// Imperative object-oriented method
member x.Add(name, income) =
clients <- (new Client(name, income))::clients
// Purely functional - filtering of clients
member x.Filter f =
clients |> List.filter f
(The example is taken from the source code of Chapter 9 of my book. There are some more examples of immutable object-oriented code, for example in parallel simulation in Chapter 14).

The most powerful experience I've had mixing OO (specifically mutation) and functional programming is achieving performance gains by using mutable data-structures internally while enjoying all the benefits of immutability by external users. A great example is an implementation I wrote of an algorithm which yields lexicographical permutations you can find here. The algorithm I use is imperative at it's core (repeated mutation steps of an array) which would suffer if implemented with a functional data structure. By taking an input array, making a read-only copy of it initially so the input is not corrupted, and then yielding read-only copies of it in the sequence expression after the mutation steps of the algorithm are performed, we strike a fine balance between OO and functional techniques. The linked answer references the original C++ implementation as well as benchmarks other purely functional implementation answers. The performance of my mixed OO / functional implementation falls in between the superior performance of the OO C++ solution and the pure functional F# solution.
This strategy of using OO / mutable state internally while keeping pure externally to the caller is used throughout the F# library notably with direct use of IEnumerators in the Seq module.
Another example may be found by comparing memoization using a mutable Dictionary implementation vs. an immutable Map implementation, which Don Syme explores here. The immutable Dictionary implementation is faster but no less pure in usage than the Map implementation.
In conclusion, I think using mutable OO in F# is most powerful for library designers seeking performance gains while keeping everything pure functional for library consumers.

I don't know any F#, but I can show you an example of the exact language mechanics you're looking for in Scala. Ignore it if this isn't helpful.
class Summation {
def sum(aLow : Int, aHigh : Int) = {
(aLow to aHigh).foldLeft(0) { (result, number) => result + number }
}
}
object Sample {
def main(args : Array[String]) {
println(new Summation sum(1, 10))
}
}
I tried to keep it super simple. Notice that we're declaring a class to sum a range, but the implementation is used with a functional style. In this way, we can abstract the paradigm we used to implement a piece of code.

I don't know about F#, but most softwares written in Scala are object-functional in nature.
Scala compiler is probably the largest and a state of the art example of an object-functional software. Other notable examples include Akka, Lift, SBT, Kestrel etc. (Googling will find you a lot more object-functional Scala examples.)

Related

Advantages and drawbacks to implementing core methods of a scripting language in the underlying language

Background: I am writing a scripting language interpreter as a way to test out some experimental language ideas. I am to the point of writing the core set of standard methods (functions) for built-in types. Some of these methods need to directly interface with the underlying data structures and must be be written using the underlying language (Haskell in my case, but that is not important for this question). Others can be written in the scripting language itself if I choose.
Question: What are the advantages and drawbacks to implementing core library functions in either the underlying language or in the language itself?
Example: My language includes as a built-in type Arrays that work just like you think they do -- ordered data grouped together. An Array instance (this is an OO language) has methods inject, map and each. I have implemented inject in Haskell. I could write map and each in Haskell as well, or I could write them in my language using inject. For example:
def map(fn)
inject([]) do |acc,val|
acc << fn(val)
#end inject
#end def map
def each(fn)
inject(nil) do |acc,val|
fn val
#end inject
#end def each
I would like to know what the advantages and drawbacks are to each choice?
The main advantage is that you're eating your own dog food. You get to write more code in your language, and hence get a better idea of what it's like, at least for generic library code. This is not only a good opportunity to notice deficiencies in the language design, but also in the implementation. In particular, you'll find more bugs and you'll find out whether abstractions like these can be implemented efficiently or whether there is a fundamental barrier that forces one to write performance-sensitive code in another language.
This leads, of course, to one disadvantage: It may be slower, either in programmer time or run time performance. However, the first is valuable experience for you, the language designer, and the second should be incentive to optimize the implementation (assuming you care about performance) rather than working around the problem — it weakens your language and doesn't solve the same problem for other users who can't modify the implementation.
There are also advantages for future-proofing the implementation. The language should remain stable in the face of major modifications under the hood, so you'll have to rewrite less code when doing those. Conversely, the functions will be more like other user-defined functions: There is a real risk that, when defining some standard library function or type in the implementation language, subtle differences sneak in that make the type or function behave in a way that can't be emulated by the language.

Achieving polymorphism in functional programming

I'm currently enjoying the transition from an object oriented language to a functional language. It's a breath of fresh air, and I'm finding myself much more productive than before.
However - there is one aspect of OOP that I've not yet seen a satisfactory answer for on the FP side, and that is polymorphism. i.e. I have a large collection of data items, which need to be processed in quite different ways when they are passed into certain functions. For the sake of argument, let's say that there are multiple factors driving polymorphic behaviour so potentially exponentially many different behaviour combinations.
In OOP that can be handled relatively well using polymorphism: either through composition+inheritance or a prototype-based approach.
In FP I'm a bit stuck between:
Writing or composing pure functions that effectively implement polymorphic behaviours by branching on the value of each data item - feels rather like assembling a huge conditional or even simulating a virtual method table!
Putting functions inside pure data structures in a prototype-like fashion - this seems like it works but doesn't it also violate the idea of defining pure functions separately from data?
What are the recommended functional approaches for this kind of situation? Are there other good alternatives?
Putting functions inside pure data structures in a prototype-like fashion - this seems like it works but doesn't it also violate the idea of defining pure functions separately from data?
If virtual method dispatch is the way you want to approach the problem, this is a perfectly reasonable approach. As for separating functions from data, that is a distinctly non-functional notion to begin with. I consider the fundamental principle of functional programming to be that functions ARE data. And as for your feeling that you're simulating a virtual function, I would argue that it's not a simulation at all. It IS a virtual function table, and that's perfectly OK.
Just because the language doesn't have OOP support built in doesn't mean it's not reasonable to apply the same design principles - it just means you'll have to write more of the machinery that other languages provide built-in, because you're fighting against the natural spirit of the language you're using. Modern typed functional languages do have very deep support for polymorphism, but it's a very different approach to polymorphism.
Polymorphism in OOP is a lot like "existential quantification" in logic - a polymorphic value has SOME run-time type but you don't know what it is. In many functional programming languages, polymorphism is more like "universal quantification" - a polymorphic value can be instantiated to ANY compatible type its user wants. They're two sides of the exact same coin (in particular, they swap places depending on whether you're looking at a function from the "inside" or the "outside"), but it turns out to be extremely hard when designing a language to "make the coin fair", especially in the presence of other language features such as subtyping or higher-kinded polymorphism (polymorphism over polymorphic types).
If it helps, you may want to think of polymorphism in functional languages as something very much like "generics" in C# or Java, because that's exactly the type of polymorphism that, e.g., ML and Haskell, favor.
Well, in Haskell you can always make a type-class to achieve a kind of polymorphism. Basically, it is defining functions that are processed for different types. Examples are the classes Eq and Show:
data Foo = Bar | Baz
instance Show Foo where
show Bar = 'bar'
show Baz = 'baz'
main = putStrLn $ show Bar
The function show :: (Show a) => a -> String is defined for every data type that instances the typeclass Show. The compiler finds the correct function for you, depending on the type.
This allows to define functions more generally, for example:
compare a b = a < b
will work with any type of the typeclass Ord. This is not exactly like OOP, but you even may inherit typeclasses like so:
class (Show a) => Combinator a where
combine :: a -> a -> String
It is up to the instance to define the actual function, you only define the type - similar to virtual functions.
This is not complete, and as far as I know, many FP languages do not feature type classes. OCaml does not, it pushes that over to its OOP part. And Scheme does not have any types. But in Haskell it is a powerful way to achieve a kind of polymorphism, within limits.
To go even further, newer extensions of the 2010 standard allow type families and suchlike.
Hope this helped you a bit.
Who said
defining pure functions separately from data
is best practice?
If you want polymorphic objects, you need objects. In a functional language, objects can be constructed by glueing together a set of "pure data" with a set of "pure functions" operating on that data. This works even without the concept of a class. In this sense, a class is nothing but a piece of code that constructs objects with the same set of associated "pure functions".
And polymorphic objects are constructed by replacing some of those functions of an object by different functions with the same signature.
If you want to learn more about how to implement objects in a functional language (like Scheme), have a look into this book:
Abelson / Sussman: "Structure and Interpration of Computer programs"
Mike, both your approaches are perfectly acceptable, and the pros and cons of each are discussed, as Doc Brown says, in Chapter 2 of SICP. The first suffers from having a big type table somewhere, which needs to be maintained. The second is just traditional single-dispatch polymorphism/virtual function tables.
The reason that scheme doesn't have a built-in system is that using the wrong object system for the problem leads to all sorts of trouble, so if you're the language designer, which to choose? Single despatch single inheritance won't deal well with 'multiple factors driving polymorphic behaviour so potentially exponentially many different behaviour combinations.'
To synopsize, there are many ways of constructing objects, and scheme, the language discussed in SICP, just gives you a basic toolkit from which you can construct the one you need.
In a real scheme program, you'd build your object system by hand and then hide the associated boilerplate with macros.
In clojure you actually have a prebuilt object/dispatch system built in with multimethods, and one of its advantages over the traditional approach is that it can dispatch on the types of all arguments. You can (apparently) also use the heirarchy system to give you inheritance-like features, although I've never used it, so you should take that cum grano salis.
But if you need something different from the object scheme chosen by the language designer, you can just make one (or several) that suits.
That's effectively what you're proposing above.
Build what you need, get it all working, hide the details with macros.
The argument between FP and OO is not about whether data abstraction is bad, it's about whether the data abstraction system is the place to stuff all the separate concerns of the program.
"I believe that a programming language should allow one to define new data types. I do not believe that a program should consist solely of definitions of new data types."
http://www.haskell.org/haskellwiki/OOP_vs_type_classes#Everything_is_an_object.3F nicely discusses some solutions.

Can Scala be seen as an 'Abstraction Inversion'?

Greetings,
It's a provocative question, aiming to open debate about how abstraction inversion are seen among developer community. I'm really curious to know what you think.
First, here is a quote from the abstraction inversion exemples given by Wikipedia:
http://en.wikipedia.org/wiki/Abstraction_inversion
Creating an object to represent a function is cumbersome in object-oriented languages such as Java and C++, in which functions are not first-class objects. In C++ it is possible to make an object 'callable' by overloading the () operator, but it is still often necessary to implement a new class, such as the Functors in the STL.
For me functions are first-class citizen in Scala, but when we use Scala to generate Java bytecode, Scala create specific class 'on top' of Java to make functional programming possible... can we see this as an abstraction inversion ?
Same can apply to Clojure or any functionnal language for the JVM... or even Apache Collections, for exemple this:
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/Closure.html
BTW, I'm not convinced about the wikipedia article objectivity. For example when speaking about possible abstraction inversion in micro-kernel the article say 'A body of opinion holds the microkernel design to be an abstraction inversion' but no such statement for functional type in OOP
The wiki article is really weak (does it represent an abstraction inversion itself? :), and the very concept is a bit dubious. But the fundamental gist of it seems to be that some basic element is hidden by the abstraction, forcing users of that abstraction to re-implement it.
For instance, from the talk page, comes a much more interesting example. Suppose a CPU had a tan math function, but no sin or cos, and a programming language implemented sin and cos in terms of tan, but did not expose tan itself. A programmer using that language would be forced to implement tan in terms of sin and cos, which, themselves, are implemented in terms of tan, therefore characterizing abstraction inversion.
So, back to Scala. Naturally, a programmer using functions in Scala is not incurring in abstraction inversion, because he is not being forced to re-implement a functionality available as a primitive to Scala.
On the other hand, one might claim that Scala's implementation of functions as class instances is an instance of abstraction inversion. For that to be true, however, two things must also hold true:
There must be a "function" primitive available to JVM.
Such a primitive must have offered an alternative to what Scala is doing.
What, exactly, is a function? What would a function primitive look like? In this context, "function" means data that is capable of being executed. One might say that all assembler code, is, in fact, data that is capable of being executed -- only it is not portable, and, futhermore, not bytecode, therefore failing the design principles.
On the other hand, all methods in Java are referenced by an identifier, through which Java locates the code to be executed for a given object's class hierarchy. This identifier is not exposed, though it can be used indirectly through reflection. If it were exposed, and some functionality offered to say "call this code", then a function could arguably be constructed around that.
So, I think a case could be made for 1 to be true. Let's proceed to the next.
If Java did offer a "method" data type, would Scala functions cease to be instances of a class? No, they would not. One of the fundamental design aspects of Scala is that every element of a running program is an object. The "primitives" that Java already have are presented as if they were normal objects with methods, and if there was a "method" primitive, so would it.
One possible consequence of method primitives would be to elevate methods to first class citizens, but functions, themselves, would hardly change.
Implementing a function via an object is not done simply because that's what's possible on the JVM, it cuts right to the underlying philosophy of Scala.
everything is an object: functions, actors, number literals, etc. It's also possible for any object to be appliable (by defining the apply() method) without actually being a subclass of FunctionN.
This duality is fundamental in the design of many standard library APIs, it allows for e.g. Maps to be viewed as both a function (mapping keys to values) and as a object (a collection of key/value pairs).
Scala is a true object/functional hybrid, with neither paradigm being dominant.
No, it can't be seen as abstraction inversion. The reason is simple: In Scala you have a choice which abstraction level you prefer. Most of the time it's more convenient to write
val f = 2 * (_:Int)
//--> f: (Int) => Int =
f(21)
//--> res5: Int = 42
but it is no problem to do it the "long" way:
val f = new Function1[Int,Int] { def apply(v:Int) = 2*v }
//--> f: java.lang.Object with (Int) => Int =
f(21)
//--> res6: Int = 42
As FunctionN are traits, it's possible to use mix-in inheritance, which allows you to avoid situations like the one for Functors in C++. E.g. you could "retrofit" a Java-Map as a function:
class JavaMap[K,V] extends java.util.HashMap[K,V] with Function1[K,V] {
def apply(k:K) = get(k)
}
val m = new JavaMap[Int, String]
m.put(5,"five")
m(5)
//--> res8: String = five
I believe not. The way first-class functions are implemented in Scala is just that, an implementation detail.

Object-oriented programming in a purely functional programming context?

Are there any advantages to using object-oriented programming (OOP) in a functional programming (FP) context?
I have been using F# for some time now, and I noticed that the more my functions are stateless, the less I need to have them as methods of objects. In particular, there are advantages to relying on type inference to have them usable in as wide a number of situations as possible.
This does not preclude the need for namespaces of some form, which is orthogonal to being OOP. Nor is the use of data structures discouraged. In fact, real use of FP languages depend heavily on data structures. If you look at the F# stack implemented in F Sharp Programming/Advanced Data Structures, you will find that it is not object-oriented.
In my mind, OOP is heavily associated with having methods that act on the state of the object mostly to mutate the object. In a pure FP context that is not needed nor desired.
A practical reason may be to be able to interact with OOP code, in much the same way F# works with .NET. Other than that however, are there any reasons? And what is the experience in the Haskell world, where programming is more pure FP?
I will appreciate any references to papers or counterfactual real world examples on the issue.
The disconnect you see is not of FP vs. OOP. It's mostly about immutability and mathematical formalisms vs. mutability and informal approaches.
First, let's dispense with the mutability issue: you can have FP with mutability and OOP with immutability just fine. Even more-functional-than-thou Haskell lets you play with mutable data all you want, you just have to be explicit about what is mutable and the order in which things happen; and efficiency concerns aside, almost any mutable object could construct and return a new, "updated" instance instead of changing its own internal state.
The bigger issue here is mathematical formalisms, in particular heavy use of algebraic data types in a language little removed from lambda calculus. You've tagged this with Haskell and F#, but realize that's only half of the functional programming universe; the Lisp family has a very different, much more freewheeling character compared to ML-style languages. Most OO systems in wide use today are very informal in nature--formalisms do exist for OO but they're not called out explicitly the way FP formalisms are in ML-style languages.
Many of the apparent conflicts simply disappear if you remove the formalism mismatch. Want to build a flexible, dynamic, ad-hoc OO system on top of a Lisp? Go ahead, it'll work just fine. Want to add a formalized, immutable OO system to an ML-style language? No problem, just don't expect it to play nicely with .NET or Java.
Now, you may be wondering, what is an appropriate formalism for OOP? Well, here's the punch line: In many ways, it's more function-centric than ML-style FP! I'll refer back to one of my favorite papers for what seems to be the key distinction: structured data like algebraic data types in ML-style languages provide a concrete representation of the data and the ability to define operations on it; objects provide a black-box abstraction over behavior and the ability to easily replace components.
There's a duality here that goes deeper than just FP vs. OOP: It's closely related to what some programming language theorists call the Expression Problem: With concrete data, you can easily add new operations that work with it, but changing the data's structure is more difficult. With objects you can easily add new data (e.g., new subclasses) but adding new operations is difficult (think adding a new abstract method to a base class with many descendants).
The reason why I say that OOP is more function-centric is that functions themselves represent a form of behavioral abstraction. In fact, you can simulate OO-style structure in something like Haskell by using records holding a bunch of functions as objects, letting the record type be an "interface" or "abstract base class" of sorts, and having functions that create records replace class constructors. So in that sense, OO languages use higher-order functions far, far more often than, say, Haskell would.
For an example of something like this type of design actually put to very nice use in Haskell, read the source for the graphics-drawingcombinators package, in particular the way that it uses an opaque record type containing functions and combines things only in terms of their behavior.
EDIT: A few final things I forgot to mention above.
If OO indeed makes extensive use of higher-order functions, it might at first seem that it should fit very naturally into a functional language such as Haskell. Unfortunately this isn't quite the case. It is true that objects as I described them (cf. the paper mentioned in the LtU link) fit just fine. in fact, the result is a more pure OO style than most OO languages, because "private members" are represented by values hidden by the closure used to construct the "object" and are inaccessible to anything other than the one specific instance itself. You don't get much more private than that!
What doesn't work very well in Haskell is subtyping. And, although I think inheritance and subtyping are all too often misused in OO languages, some form of subtyping is quite useful for being able to combine objects in flexible ways. Haskell lacks an inherent notion of subtyping, and hand-rolled replacements tend to be exceedingly clumsy to work with.
As an aside, most OO languages with static type systems make a complete hash of subtyping as well by being too lax with substitutability and not providing proper support for variance in method signatures. In fact, I think the only full-blown OO language that hasn't screwed it up completely, at least that I know of, is Scala (F# seemed to make too many concessions to .NET, though at least I don't think it makes any new mistakes). I have limited experience with many such languages, though, so I could definitely be wrong here.
On a Haskell-specific note, its "type classes" often look tempting to OO programmers, to which I say: Don't go there. Trying to implement OOP that way will only end in tears. Think of type classes as a replacement for overloaded functions/operators, not OOP.
As for Haskell, classes are less useful there because some OO features are more easily achieved in other ways.
Encapsulation or "data hiding" is frequently done through function closures or existential types, rather than private members. For example, here is a data type of random number generator with encapsulated state. The RNG contains a method to generate values and a seed value. Because the type 'seed' is encapsulated, the only thing you can do with it is pass it to the method.
data RNG a where RNG :: (seed -> (a, seed)) -> seed -> RNG a
Dynamic method dispatch in the context of parametric polymorphism or "generic programming" is provided by type classes (which are not OO classes). A type class is like an OO class's virtual method table. However, there's no data hiding. Type classes do not "belong" to a data type the way that class methods do.
data Coordinate = C Int Int
instance Eq Coordinate where C a b == C d e = a == b && d == e
Dynamic method dispatch in the context of subtyping polymorphism or "subclassing" is almost a translation of the class pattern in Haskell using records and functions.
-- An "abstract base class" with two "virtual methods"
data Object =
Object
{ draw :: Image -> IO ()
, translate :: Coord -> Object
}
-- A "subclass constructor"
circle center radius = Object draw_circle translate_circle
where
-- the "subclass methods"
translate_circle center radius offset = circle (center + offset) radius
draw_circle center radius image = ...
I think that there are several ways of understanding what OOP means. For me, it is not about encapsulating mutable state, but more about organizing and structuring programs. This aspect of OOP can be used perfectly fine in conjunction with FP concepts.
I believe that mixing the two concepts in F# is a very useful approach - you can associate immutable state with operations working on that state. You'll get the nice features of 'dot' completion for identifiers, the ability to easy use F# code from C#, etc., but you can still make your code perfectly functional. For example, you can write something like:
type GameWorld(characters) =
let calculateSomething character =
// ...
member x.Tick() =
let newCharacters = characters |> Seq.map calculateSomething
GameWorld(newCharacters)
In the beginning, people don't usually declare types in F# - you can start just by writing functions and later evolve your code to use them (when you better understand the domain and know what is the best way to structure code). The above example:
Is still purely functional (the state is a list of characters and it is not mutated)
It is object-oriented - the only unusual thing is that all methods return a new instance of "the world"

Theory behind object oriented programming

Alonzo Church's lambda calculus is the mathematical theory behind functional languages. Has object oriented programming some formal theory ?
Object Orientation comes from psychology not math.
If you think about it, it resembles more how humans work than how computers work.
We think in objects that we class-ify. For instance this table is a seating furniture.
Take Jean Piaget (1896-1980), who worked on a theory of children's cognitive development.
Wikipedia says:
Piaget also had a considerable effect in the field of computer science and artificial intelligence.
Some cognitive concepts he discovered (that imply to the Object Orientation concept):
Classification The ability to group objects together on the basis of common features.
Class Inclusion The understanding, more advanced than simple classification, that some classes or sets of objects are also sub-sets of a larger class. (E.g. there is a class of objects called dogs. There is also a class called animals. But all dogs are also animals, so the class of animals includes that of dogs)
Read more: Piaget's developmental theory http://www.learningandteaching.info/learning/piaget.htm#ixzz1CipJeXyZ
OOP is a bit of a mixed bag of features that various languages implement in slightly different ways. There is no single formal definition of OOP but a number of people have tried to describe OOP based on the common features of languages that claim to be object oriented. From Wikipedia:
Benjamin Cuire Pierce and some other researchers view as futile any attempt to distill OOP to a minimal set of features. He nonetheless identifies fundamental features that support the OOP programming style in most object-oriented languages:
Dynamic dispatch – when a method is invoked on an object, the object itself determines what code gets executed by looking up the method at run time in a table associated with the object. This feature distinguishes an object from an abstract data type (or module), which has a fixed (static) implementation of the operations for all instances. It is a programming methodology that gives modular component development while at the same time being very efficient.
Encapsulation (or multi-methods, in which case the state is kept separate)
Subtype polymorphism
object inheritance (or delegation)
Open recursion – a special variable (syntactically it may be a keyword), usually called this or self, that allows a method body to invoke another method body of the same object. This variable is late-bound; it allows a method defined in one class to invoke another method that is defined later, in some subclass thereof.
Abadi and Cardelli have written A Theory Of Objects, you might want to look into that. Another exposition is given the venerable TAPL (IIRC, they approach objects as recursive records in a typed lambda calculus). I don't really know much about this stuff.
One formal definition I've run into for strongly defining and constraining subtyping is the Liskov Substitution Principle. It is certainly not all of object-oriented programming, but yet it might serve as a link into the formal foundations in development.
I'd check out wikipedia's page on OO http://en.wikipedia.org/wiki/Object-oriented_programming It's got the principles and fundamentals and history.
My understanding is that it was an evolutionary progression of features and ideas in a variety of languages that finally came together with the push in the 90's for GUI's going mainstream. But i could be horribly wrong :-D
Edit: What's even more interesting is that people still argue about "what makes an OO language OO"..i'm not sure the feature set is even generally agreed upon that defines an OO language.
The history (simplified) goes that way :
First came the spagetti code.
Then came the procedural code (Like C and Pascal).
Then came modular code (Like in modula).
Then came the object oriented code (Like in smalltalk).
Whats the porpuse of object oriented programming ?
You can only understand if you recall history.
At first code was simply a sequence of instructions given to the computer (Literally in binary representation)
Then came the macro assemblers. With mneomonics for instructions.
Then people detected that sometimes you have code that is repeated around.
So they created GOTO. But GOTO (Or branch or jump etc) cannot return back to where it was called, and cannot give direct return values, nor can accept formal parameters (You had to use global variables).
Against the first problem, people created subroutines (GOSUB-like). Groups of instructions that could be called repeatedly and return to where it was called.
Then people detected that routines would be more usefull if they had parameters and could return values.
For this they created functions, procedures and calling conventions. Those abstractions where called on top of an abstraction called the stack.
The stack allows formal parameters, return values and something called recursion (direct or indirect).
With the stack and the ability for a function to be called arbitrarely (even indirectly), came the procedural programming, solving the GOTO problem.
But then came the large projects, and the necessity to group procedures into logical entities (modules).
Thats where you will understand why object oriented programming evolved.
When you have a module, you have module local variables.
Think about this :
Module MyScreenModule;
Var X, Y : Integer;
Procedure SetX(value : Integer);
Procedure SetY(value : Integer);
End Module.
There are X and Y variables that are local to that module. In that example, X and Y holds the position of the cursor. But lets suppose your computer has more than one screen. So what can we do now ? X and Y alone arent able to hold the X and Y values of all screens you have. You need a way to INSTANTIATE that information. Thats where the jump from modular programming goes to object oriented programming.
In a non object oriented language you would usually do :
Var Screens : Array of Record X, Y : Integer End;
And then pass a index value to each module call :
Procedure SetX(ScreenID : Integer; X : Integer);
Procedure SetY(ScreenID : Integer; Y : Integer);
Here screenid refers to wich of the multiple screens that you are talking about.
Object oriented inverts the relationship. Instead of a module with multiple data instances (Effectively what screenid does here), you make the data the first class citizen and attach code to it, like this :
Class MyScreenModule;
Field X, Y : Integer;
Procedure SetX(value : Integer);
Procedure SetY(value : Integer);
End Class.
Its almost same thing as a module !
Now you instantiate it by providing a implicit pointer to a instance, like :
ScreenNumber1 := New MyScreenModule;
And then proceed to use it :
ScreenNumber1::SetX(100);
You effectively turned your modular programming into a multi-instance programming where the variable holding the module pointer itself differentiates each instance. Gotcha ?
Its an evolution of the abstraction.
So now you have multiple-instances, whats the next level ?
Polymorphism. Etc. The rest is pretty standard object oriented lessons.
Whats the point ? The point is that object oriented (like procedures, like subroutines etc) did not evolve from a theoretical standpoint but from the praxys of many coders working around decades. Its a evolution of computer programming, a continual evolution.
IMO a good example of what makes a successful OO language could be found by comparing the similarities between JAVA and C#. Both are extremely popular and very similar. Though I think the general idea of a minimum OO language can be found by looking at Simula67. I believe the general idea behind Object Oriented programming to be that it makes it seem like the computer thinks more like a human, this is supported by things like inheritance (both class "mountain bike" and "road bike" belong to parent class "bicycle", and have the same basic features). Another important idea is that objects (which can be executable lines of code) can be passed around like variables, effectively allowing the program to edit itself based on certain criteria (although this point is highly arguable, I cite the ability to change every instance of an object based on one comparison). Another point to make is modularity. As entire programs could effectively be passed around like a variable (because everything is treated as an object), it becomes easier to modify large programs, by simply modifying the class or method being called, and never having to modify the main method. Because of this, expanding the functionality of a program can become much simpler. This is why web businesses love languages like C# and JAVA (which are full fledged OO). Gaming companies like C++ because it gives them some of the control of an imperative language (like C) and some of the features of object orientation (like C# or JAVA).
Object-oriented is a bit of a misnomer and I don't really think classes or type systems have much to do with OO programming. Alan Kay's inspiration was biological and what's really going on that matters is communication. A better name would be message-oriented programming. There is plenty of theory about messaging, for example Pi calculus and the actor model both have rigorous mathematical descriptions. And that is really just the tip of the iceberg.
What about Petri nets? Object might be a place, a composition an arc, messages tokens. I have not though about it very thoroughly, so there might be some flaws I am not aware of, but you can investigate - there is a lot of theoretical works related to Petri nets.
I found this, for example:
http://link.springer.com/book/10.1007%2F3-540-45397-0
Readable PDF: http://www.informatik.uni-hamburg.de/bib/medoc/M-329.pdf
In 2000 in my degree thesys I proposed this model; very shortly:
y + 1 = f(u, x)
x + 1 = g(u, x)
where:
u: input
y: output
x: state
f: output function
g: state function
Formally it's very similar to finite state machine, but the difference is that U, Y, S are set not finite, but infinite (numerable) and f and g are Touring Machine (TM).
f and g togheter form a class; if we add an initial state x0 we have an object. So OOP in something more than a TM a TM is a specific case of OOP. Note that the state x is of a different level than the state "inside" the TM. Inside the TM there are not side effect, the x state count for side effect.