0
\$\begingroup\$

In the case that Type is not an option, data must exist so I just use an assert to clarify and throw an exception if data is missing. Is there a better way to do this? I think in Java this would be acceptable but I know the scala crowd tends to have a different idea of what idiomatic use of the language is.

private def convertData(dataOption: Option[ByteString], tpe: Type): Any = {

    if(tpe.<:<(typeOf[Option[Any]])){
      dataOption match{
        case Some(x) => ???
        case None => None
      }
    } else{
      assert(dataOption.isDefined)
      ???
    }
}
\$\endgroup\$
4
\$\begingroup\$

In short: yes, assertions make perfect sense outside testing and can raise the confidence that the code is actually correct.

Design by contract

What you like to assert, namely, that "dataOption must be defined if tpe does not represent a subtype of Option[Any]" can be considered a precondition of your method. That is, a condition over the parameters (and fields and global state) that needs to hold in order for your method to run successfully. The method implementer states such conditions and can then rely on them, whereas the caller is responsible for ensuring that they hold. The dual concept are postconditions, where the caller gets the guarantee which the implementer must ensure.

Languages such as Eiffel and Spec# natively support this so-called design by contract approach to programming. Natively, because contract elements such as preconditions, postconditions and invariants are built into the language. There is a significant difference, however, when the contracts are looked at. Eiffel checks contracts at run-time, which is quite similar to testing in the sense that only concrete executions are considered. Spec#, on the other hand, verifies contracts statically, which means that it tries to assert that the contract holds for all possible executions. The latter is very challenging and not always possible, but it yields much stronger guarantees in case it is. The former is easier, but gives you weaker guarantees.

Other languages lack native support for such contracts, for example Java or C#. In order to design by contract, Java developers can use JML and C# developers can use Code Contracts. JML is based on Java annotations and can be used for static verification and run-time checking, as can Code Contracts be, but they are implemented as a regular library.

Scala

Scala also lacks built-in support for contracts, but there are quite a few projects that try to work around this. Amongst others, there are:

  • Very simple run-time checked pre- and postconditions described in the paper Contracts for Scala

  • Static verification of functional Scala code with Leon

  • Run-time checking of Scala with a Code-Contracts-like library (project report)

  • Static verification of possibly non-functional Scala code (project description, the report will be only soon)

The answer

Back to your question: You use assertions (and the control flow) to check the precondition of your method, hence, you kind of do design by contract - which is good, because it makes it more likely that errors are detected close to where they are caused. These checks are basically like small unit tests and thus can increase the confidence in your code.

In order to make the method's precondition as explicit as possible, I would move the assertion to the begin of the method

private def convertData(dataOption: Option[ByteString], tpe: Type): Any = {
  assert(tpe.<:<(typeOf[Option[Any]]) || dataOption.isDefined,
         "<put helpful error message here>")

  ...
}

You could also put assertions at all return points of the method in order to mimic postconditions.

In case you worry about performance, you can use the fact the assert is annotated as @elidable and thus can be removed by the compiler, for example, for a production build.

\$\endgroup\$
  • \$\begingroup\$ Incredible answer - thanks for filling this in for me and giving me suggestions on how to improve. \$\endgroup\$ – JasonG Jun 21 '13 at 13:59
  • \$\begingroup\$ On the other hand, assertions are making code less functional as it introduces side effects. Can/Should the similar effect be achieved with Partial Functions? stackoverflow.com/a/15972055/160596 \$\endgroup\$ – xvga Sep 12 '18 at 7:40
  • \$\begingroup\$ @xvga I don't see that PFs have an advantage here: a PF that is applied outside its domain will raise a corresponding exception. Callers could check that a PF is applicable, but that also applies to a function with an assertion at the beginning. With the difference, that the assertion can give you a meaningful error message where the PF will (in general) just report a MatchError. IMO, an assertion that checks a function's precondition effectively makes that function partial. \$\endgroup\$ – Malte Schwerhoff Sep 12 '18 at 8:02
  • \$\begingroup\$ @MalteSchwerhoff Could you please clarify how caller can check if function (with assertion at the beginning) is applicable? (except for trying calling it and catching exceptions). Otherwise, it might be just a matter of preference . So far, IMO, assertions are friendlier/easier to use. \$\endgroup\$ – xvga Sep 12 '18 at 12:51
  • \$\begingroup\$ @xvga Conceptually, a precondition (analogously, a postcondition) only makes sense if it is public, i.e. known to caller & callee. If it were private, a caller would not know how to systematically establish a callee's pre. Pres/posts are sometimes called contracts, a term that, IMO, conveys good intuition: a contract that caller & callee need to agree on to cooperate. A callee can implement a checkPre(...) method that both callee and caller can use to check if the pre is satisfied. A Scala PF essentially does that: isDefinedAt is the caller's checkPre, the MatchError is the callee's. \$\endgroup\$ – Malte Schwerhoff Sep 13 '18 at 6:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.