Interfaces
Interfaces are the means to polymorphism without using objects (in an OOP sense).
Function Interface
Function interfaces are a prototype for a singe function. Usually used as a callback or delegate.
A named function type.
A function interface declares only one function and does not use the self
keyword.
FunctionInterface: (p1: U16): U8 _
callFn(ptrFn: Ptr<FunctionInterface>): U8
return ptrFn(0x4242)
fnImpl: FunctionInterface
// may use declaration for readability
fnImpl: (p1: U16): U8
return p1.U8([4..12])
// matches on fn signature
r := callFn(fnImpl) // r = 0x24
The function interface is simply a function declaration. It ends with a _
to indicate it is not a function definition and contains no implementation.
Function interfaces are syntactically different from Object interfaces - so an object interface with one function cannot be mixed up/interchanged with a function interface.
Object Interface
TBD Not sure about the interface ‘type’ with functions in its scope.
Object interfaces are a template for one or more functions. Usually used as a means to polymorphism.
An object interface can declare one or more functions. It must have the self
keyword as a first parameter.
ObjectInterface<#S>
lowByte: (self: S, p1: U16): U8 _
hiByte: (self: S, p1: U16): U8 _
The Type of self
is set as a template parameter, for it is not known (or fixed) at this point.
Also note that there is no implementation _
.
An interface can have more template parameters however:
TemplateInterface<#S, #T>
lowByte: (self: S, p1: U16): T _
hiByte: (self: S, p1: U16): T _
Normal template parameter restrictions can be applied:
// recommended way to restrict self
MyStruct
...
RestrictedInterface<#S: MyStruct>
lowByte: (self: S, p1: U16): U8 _
hiByte: (self: S, p1: U16): U8 _
// The interface can only be implemented on MyStruct (or derived) types.
CompanionInterface<#S: TemplateInterface>
fn: (self: S, p1: U8): Str _
// The interface can only be implemented on types that also implement TemplateInterface.
Implement an interface
MyInterface<#S>
interfunc: (self: S, p: U8) _
MyStruct
...
// function name must match and template parameters must satisfy restrictions (none here)
interfunc: (self: MyStruct, p: U8)
...
// make struct instance
s = MyStruct
...
// will check if all interface functions are implemented for MyStruct
// it is a 'pointer' to the interface
a: MyInterface = s
// call (both the same)
a.interfunc(42) // because 'self'
interfunc(a, 42)
The interface implementation functions are matched based on the function name, template parameters, function parameter types and its return type. The self
parameter type may be derived from other types but must match exactly.
A compile time error is generated when the compiler detects that an interface is not fully implemented for a specific type of self
.
Test for Interface Implementation
How to test dynamically (at runtime) if an object implements an interface?
// interface definition
MyInterface<#S>
fn1: (self: S, p: U16): U8 _
// struct definition
MyStruct
...
// interface implementation on MyStruct
fn1: (self: MyStruct, p: U16): U8
...
// MyStruct instance initialization
s = MyStruct
...
// non-optional interface type will Error if not implemented
i: MyInterface = s
// optional interface type will be 'Nothing' is not implemented
o: MyInterface? = s
// test before use
if o
a := o.fn1(42)
The compiler has to check if the specified self
type has implementation for all the functions of the interface.
Here are the options:
MyInterface<#S>
interfunc: (self: S, p: U8) _
s: Struct
...
// non-optional => Error if not exists
i: MyInterface = s
// optional => 'Nothing' if not exists
o: MyInterface? = s
// use builtin functions for runtime checking
b := s.Is<MyInterface<Struct>>() // retval: Bool
i := s.As<MyInterface<Struct>>() // cast/convert (Opt<T>)
// use intrinsic/pragma for compile time checking
b := s?#MyInterface<Struct> // similar to check if field exists
TBD
Allow interface definition with types?
ObjectInterface<#S, #T>
fld1: U8
fld2: Str
fn1: (self: S, p1: U16): U8 _
fn2: (self: T, p1: U16): U8 _
TBD
Interfaces as traits. Traits are aspects or attributes of an object.
IHandleMessages
, IProvideConfiguration
, IConvertToString
, ISerialize
…
Interface Jackets: a wrapper around something else that implement a certain interface. Think Extension Methods for objects/interfaces. I think Swift uses protocol for this?
TBD
Static interfaces?
An interface on a type definition (not an instance). Derived Types can override functions and call ‘base’ implementations.
// as a specialized template?
TypeInterface<MyStruct>
staticFn: (p: U8): U8
// implementation here
TypeInterface<DerivedFromMyStruct>
staticFn: (p: U8): U8
// call 'base' as normal 'static' function
return MyStruct.staticFn(p)
TBD
A set of template types and functions that can be instantiated as a whole. Could be used to implement patterns or mechanisms.
// define a group
WithParent<T> { // syntax?
Parent: T
hasParent: <T>(self: T): Bool
...
trySetParent(self: T, parent: T): Bool
...
}
// use with custom type
MyStruct : WithParent<MyStruct>
fld1: U8
p := MyStruct
s := MyStruct
if not s.hasParent()
s.trySetParent(p)
// access extra field
s.Parent // set to p
Behavior
TBD
Often the behavior of a function or interface is overlooked or ignored. An interface is a contract, not just of the programmatic structure but also of it’s (expected) behavior. We would need something like (.NET) code-contracts to be able to tag (interface) functions with rules it will respect.
Simple pre- and post-conditions would be a start:
fnContract: (p: Str): I32
// syntax TBD
#p is not null or empty // pre-condition
#fnContract > 0 and < 100 // post-condition
...
The compiler could generate additional (test) code to enforce the rules. Pre-conditions would be parameter validation and post-conditions would be an unit test. See also Parameter Validation in Functions.
It is much harder to define the behavior of a function it has on state or the logical relation of it’s return values.
TBD
Pivot the interface and function?
Have an interface with a lot of different methods or have different interfaces with one method?
SupportEditing.Enabled vs. SupportEnabled.Editing