## Use an abstract contract (preferred)

Further clarifications to @Edmund's answer:

    contract A { // This doesn't have to match the real contract name. Call it what you like.
       function f1(bool arg1, uint arg2) returns(uint); // No implementation, just the function signature. This is just so Solidity can work out how to call it.
    }
    
    contract YourContract {
      function doYourThing(address addressOfA) returns(uint) {
        A my_a = A(addressOfA);
        return my_a.f1(true, 3);
      }
    }

This shows using the return value from `f1`.

Also, if `f1` encounters an exception (imagine its implementation is `function f1(bool arg1, uint arg2) returns(uint) { throw; }`), the exception is propagated and `my_a.f1` will also `throw` and it will revert a transaction that invoked `doYourThing`.

In practice, you'll have 3 files.

AbstractA.sol contains:

    contract A {
       function f1(bool arg1, uint arg2) returns(uint); // No implementation, just the function signature. This is just so Solidity can work out how to call it.
    }

YourContract.sol contains:

    import "AbstractA.sol"

    contract YourContract {
      function doYourThing(address addressOfA) returns(uint) {
        A my_a = A(addressOfA);
        return my_a.f1(true, 3);
      }
    }

A.sol contains:

    contract A {
       // implementation of f1
       function f1(bool arg1, uint arg2) returns(uint) {
           if (arg1) {
               throw;
           } else {
               return arg2;
           }
       }
    }

## Limitations of using `call`

`call` is suggested in @Badr's answer but should be used very carefully.  [Solidity docs](http://solidity.readthedocs.io/en/develop/types.html) state:

> All three functions `call`, `delegatecall` and `callcode` are very
> low-level functions and should only be used as a last resort as they
> break the type-safety of Solidity.

Also, the return value of `f1` can't be obtained by using `call` like `addressOfA.call(bytes4(keccak256("f1(bool, uint256)")), true, 3)` because `call` only returns a `bool` (`false` if the call encounters an exception).

This means that the exception must be propagated manually like:

    if (!addressOfA.call(bytes4(keccak256("f1(bool, uint256)")), true, 3)) {
        throw;
    }