Data.
If you want to be loosely-coupled, the answer is data. Your programs or components will not "execute functions" on each other, they will pass data to each other. (Internally, this will probably be done by calling a method - but the mechanics aside, it should be, conceptually, passing data).
If the data, inside your process, is treated as immutable, even better.
This gives clear component boundaries, and protects, to a great extent, from changes blowing up on you (as you can transform one form of data into another relatively easily). It also makes it easy to transfer to multi-process or networked applications, as the semantics of data passing are the same everywhere - there's no assumption of magical hidden state changes without passing some new piece of data.
Well-defined interfaces that pass data, and you're golden. And I do mean data - not 'objects' that know how to do all kinds of magic things. Just simple, pure data.
By the way - that's not saying that objects don't have a place in your program - they're the things that act on your data.
Also, make sure that you keep the parts of your program that deal with the data separate from the parts of the program that are responsible for actually shuttling it around, and the parts that show it to the user. I know, it's that whole 'keep your business logic separate from your persistence and UI layer' stuff again.