Enforce interface implementation

I’m trying to refactor GenericInstruments.jl in order to play nicer with instrument front panels GUIs for our online university labs.
The problem is the following: different instruments keep popping up and the code needs to be compliant with the interface defined for the GUIs.

For example, let’s say that a GUI for a power supply needs two methods: set_voltage(instr, val) and get_voltage(instr) in order to set the psu and to read it. The reality is not that simple for other instruments which have tens of methods.

How can I enforce development of a driver for a new instrument to be compliant with the necesities of the GUI? Let’s say a new contributor does this.

Should I tell him to go and implement all the methods described in the documentation? In Matlab there are abstract classed which you can inherit from: if you forgot to implement one of the methods defined in the abstract class it yells at you.

1 Like

You should direct them to your GUI API documentation. Additionally, you may provide a test-for-compliance program to be run after the new instrument driver has been brought into Julia.

using NewDriver
using DriverCompliance

test_driver_compliance()

where a simple test could be

const required_api_funcs = (:get_voltage, :set_voltage)

function test_driver_compliance()
  result = true
  for fn in required_api_funcs
       result = result && isdefined(Main, fn)
  end
  return result
end

You may want to check for appropriate method signatures, and to list any missing or incorrectly argumented functions.

4 Likes

This book: Hands-On Design Patterns and Best Practices with Julia | Packt

Has a section about the implementation of interfaces which appears to be similar to what you are searching for. The ebook is for 5 pounds now, so it might be an interesting reference.

2 Likes

You can’t really “enforce” it in Julia. Eg even if you require a particular method signature, it may just return a bogus result (of the wrong type, or value).

I just would provide a test suite for the interface in a small package.

6 Likes

I am currently reading that book. I did look back at the interface implementation section and basically it boils down to: define function f(Any...) error("Should be implemented by the concrete type.") end and creating a test suite that can be called over a type to check if it implements the promised behavior (or, at least, does not throw that error message).

2 Likes

Well, I would not say that it is only that, but it is effectively something simple. Yet, I am not comfortable in just showing what the book says, since it is being sold almost for free… The author deserves some credit.

3 Likes

Thank you all for your suggestions! I also bought the book and looking into it.

I previously solved this issue by simply providing a template file with empty functions for the person implementing the interface to fill in. It’s not very glamorous, but it solved the problem very effectively. It had the added benefit of making various implementations similar in terms of the file structure, making the code easier to navigate.

On a side note: my situation was also to provide implementations for university lab equipment, in my case for processes for automatic control :slight_smile:

4 Likes

I also used this method in the past, it also worked as a rough documentation. Might give it a try again.

As for the lab, we’re using the GUIs mainly for debugging purposes, hoping the students still feel at least a bit connected to the hardware. The main purpose is to control the equipment remotely and teach them how large scale measurements are really done in the industry. Equipment changes, now I’m struggling to integrate the C++ library of ADALM2000 :sweat_smile:

I didn’t read the book so I am not responding to that, but in general I don’t think that defining functions to just throw an error indicating that someone should implement a method is good practice, instead of just getting a MethodError — after all, that’s what it is for.

I guess it depends on how much you want to give your users training wheels. While it is probably not a good practice in general (meaning you always should do it if possible), I see no reason to consider it a bad practice in general either.

BinaryTraits.jl (from @tk3369 , the author of the book) has signature checking feature, it’s worth taking a look.

I was recently in the same situation, and ended up creating a separated test package where the code is in the src folder so it can be easily called from the tests of other packages. I am happy with it exactly because it also tests requirements that are not described in the signature.

2 Likes

One of my collegues wrote a blog post about this pattern of supplying a test suite function

It works really well.

I think FilesPathsBase.jl
is a good example of this pattern.
as is Models.jl

1 Like

Since there’s a reference about my book, I should probably voice my opinion :slight_smile:

I think it depends on the intended usage and audience.

  • As a application developer, I may want to see a MethodError for missing implementations because it’s what I normally get when I make a mistake in my code and the quickest way to get to the problem.

  • As a user of some third-party package, I may want to see a nice error that says “oh, sorry, you have hit a bug. Please submit an issue at our github site XYZ”. I don’t consider this a good practice to let the users of your package receive these low-level method errors.

BinaryTraits.jl was created due to the lack of formal interface/traits support by the language. The problem that I intended to solve is to let the developer specify interface formally, and so an implementer of that interface can easily discover the interface requirements and verify the correctness of the implementation during unit testing.

I am happy to hear more perspectives about this matter.

7 Likes

FWIW, once one is writing code, there is no clear distinction between “developers” and “users”. It can be assumed that someone writing Julia code should be able to deal with a MethodError.

That said, user friendly messages can still be provided with error hints.

5 Likes

This is a very interesting feature I did not know. Maybe when the LTS version has this feature the bad practice will be defining a custom error instead of using this “error hints”.

That is a really cool idea.

Kind of lets you have your cake and eat it.
You can avoid the antipattern of NotImplementedException
which can give less clear and useful information than a MethodError (see that linked blog post for an example);
while still also being able to give a bit of an extra human readable hint about what is most-likely wrong.

3 Likes

I would not assume what you assume. Many libraries like JuMP are used by people that have very little knowledge of Julia and benefit from training wheels, many do not have reasons to learn the language deeply just to use the features they intend to use and I would find obnoxious to try to force them to learn more advanced concepts instead of pointing out they probably forgot a parentheses or something like that.

3 Likes

Yes, libraries that expose their API primarily via a DSL can benefit from nicer error messages, and ideally validating input to the extent possible so that errors are caught early.

My point is that if you have an API which takes user-defined types that should conform to some kind of interface,

  1. it is reasonable to assume that the user can deal with a backtrace,
  2. providing a test suite is the best option if you want to be really nice to your users — it can cover way more than simple “does this method exist” checks and go into semantics,
  3. error hints can be a lightweight alternative to 2. in simple cases.
2 Likes