Basic

Here's an example of single-argument interface using animals, and the implementation of a duck.

Definition

First we define the interface methods, and a list of mandatory and optional properties of the interface, with conditions, using the @interface macro.

The @interface macro takes three arguments:

  1. The name of the interface, which should usully end with "Interface"
  2. The mandatory and optional components of the interface written as a NamedTuple, with functions or tuple of functions that test them.
  3. The interface docstring (the interface is represented as a type)
module Animals

using Interfaces

abstract type Animal end

function age end
function walk end
function talk end
function dig end

components = (
    mandatory = (
        age = (
            "all animals have a `Real` age" => x -> age(x) isa Real,
            "all animals have an age larger than zero" => x -> age(x) >= 0,
        ),
    ),
    optional = (
        walk = "this animal can walk" => x -> walk(x) isa String,
        talk = "this animal can talk" => x -> talk(x) isa Symbol,
        dig = "this animal can dig" => x -> dig(x) isa String,
    )
)

description = """
Defines a generic interface for animals to do the things they do best.
"""

@interface AnimalInterface Animal components description

end;

Implementation

using Interfaces, Test

Now we implement the AnimalInterface, for a Duck.

struct Duck <: Animals.Animal
    age::Int
end

Animals.age(duck::Duck) = duck.age
Animals.walk(::Duck) = "waddle"
Animals.talk(::Duck) = :quack

We then test that the interface is correctly implemented

ducks = [Duck(1), Duck(2)]
Interfaces.test(Animals.AnimalInterface, Duck, ducks)
true

As well as two optional methods

Interfaces.test(Animals.AnimalInterface{(:walk,:talk)}, Duck, ducks)
true

Finally we declare it, so that the information can be used in static dispatch.

The @implements macro takes two arguments.

  1. The interface type, with a tuple of optional components in its first type parameter.
  2. The type for which the interface is implemented.
@implements Animals.AnimalInterface{(:walk,:talk)} Duck [Duck(1), Duck(2)]

Now let's see what happens when the interface is not correctly implemented.

struct Chicken <: Animals.Animal end

As expected, the tests fail

chickens = [Chicken()]
try
    Interfaces.test(Animals.AnimalInterface, Chicken, chickens)
catch e
    print(e)
end



    @test Interfaces.test(Duck) == true # Test all implemented interfaces for Duck
    @test Interfaces.test(Animals.AnimalInterface) == true # Test all implemented types for AnimalInterface
    @test Interfaces.test_objects(Animals.AnimalInterface) == Dict(Duck => ducks)
Test Passed

This page was generated using Literate.jl.