I consider this possibility a very useful even without additional checks (those
in the concept body), like of existence of procedures defined for the type with
stated signatures and the like, exactly because the programmer explicitly
declares the purpose of his type and what can be done with, what functionality
is really supported by the type. I think this explicit promise/contract is the
main point for interfaces in Delphi, Java, PHP, C#, and signature checking is
additional to that.
I'll try to make it more visual:
type Browser = concept type b
proc goTo(self: b, url: string)
proc back(self: b)
proc forward(self: b)
proc makeSomeStuffWithABrowser(br: Browser) =
br.goTo("http://www.nim-lang.org/")
# creating a concrete type satisfying the concept
type MySimpleBrowser = distinct int # whatever it would be
proc goTo(self: MySimpleBrowser, url: string) = echo "going to " & url & "
with MySimpleBrowser"
proc back(self: MySimpleBrowser) = discard
proc forward(self: MySimpleBrowser) = discard
# using it
var sb: MySimpleBrowser
makeSomeStuffWithABrowser(sb) # it works, well; it should
# here we don't want to define another Browser, just an unrelated type;
maybe in another module
type FileManager = distinct int
proc goTo(self: FileManager, path: string) = echo "going to " & path & "
with X"
proc back(self: FileManager) = discard
proc forward(self: FileManager) = discard
var fm: FileManager
# and here by accident...
makeSomeStuffWithABrowser(fm) # works too :(
Lando's suggestion is perfect in my view, if not to restrict it to object type
- it can fit equally well any types, as concepts do. It can be seen like a type
class (like e.g. `SomeReal* = float|float32|float64`), but extendable and with
inverse binding: created empty and populated with concrete types at their
definition site. Like:
type X = explicit concept c
# ...
# X here matches no concrete types, regardless of checks in the concept body
type Y = distinct int satisfies X # conditions in X concept body are
checked,
# compile-time error, if not satisfied;
# Y is added to the list of
implementations for X
type Z = distinct int satisfies X
proc p(x: X) = discard
# here the same as p(x: Y|Z)
Regarding wording, `explicit concept` and `satisfies` seem to be not worse than
usual `interface` and `implements`, especially taking into account
distinctions; just for people coming from those languages those terms may be
more familiar; maybe `explicit concept` will be easier to understand to others.
Regarding implementation, macro vs. built-in: I wanted to implement such a
thing some (long) time ago via macros and concepts and was faced then with some
VM limitations; maybe they were eliminated from then. The intended syntax was
(for simplicity):
type X = ...
implements X, I