Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/beef331/traitor
Trait/interface system for Nim, allowing semi-checked generics or dynamic dispatch on signatures.
https://github.com/beef331/traitor
Last synced: 26 days ago
JSON representation
Trait/interface system for Nim, allowing semi-checked generics or dynamic dispatch on signatures.
- Host: GitHub
- URL: https://github.com/beef331/traitor
- Owner: beef331
- Created: 2022-02-11T09:03:17.000Z (over 2 years ago)
- Default Branch: master
- Last Pushed: 2024-08-21T04:46:58.000Z (3 months ago)
- Last Synced: 2024-08-21T05:50:04.476Z (3 months ago)
- Language: Nim
- Homepage: http://www.jasonbeetham.com/traitor/
- Size: 452 KB
- Stars: 28
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
- awesome-nim - traitor - A macro heavy trait library made from boredom. (Language Features / Object-Oriented Programming)
README
# Traitor
A trait library made from bordem.
[Docs](http://www.jasonbeetham.com/traitor/)
## What does it do?!
Traitor allows one to use to use tuples to describe interfaces that can ensure types implement procedures for runtime dispatch.
The trait tuple must be an distinct tuple.
All procedures must have `Atom` appearing only once as the first argument.
All trait procedures should be annotated with their appropriate calling convention,
as Nim defaults to `{.closure.}` for types you must annotate it `{.nimcall.}`.The following is an example:
```nim
import traitor
type
Clickable = distinct tuple[ # Always use a distinct tuple interface to make it clean and cause `implTrait` requires it
over: proc(a: Atom, x, y: int): bool {.nimcall.}, # Notice we use `Atom` as the first parameter and it's always the only `Atom`
onClick: proc(a: Atom): string {.nimcall.}]
UnimplementedTrait = distinct tuple[
overloaded: ( # We can add overloads by using a tuple of procs
proc(a: var Atom) {.nimcall.},
proc(a: Atom, b: int) {.nimcall.})
]Button = object
x, y, w, h: intRadio = object
x, y, r: intimplTrait Clickable
proc over(btn: Button, x, y: int): bool =
btn.x < x and (btn.x + btn.w) > x and btn.y < y and (btn.y + btn.h) > yproc onClick(btn: Button): string = "Clicked a button"
proc over(radio: Radio, x, y: int): bool =
radio.r >= (abs(radio.x - x) + abs(radio.y - y))proc onClick(radio: Radio): string = "Clicked a radio"
emitConverter Button, Clickable # Emit a `converter` for `Button` -> `Traitor[Clickable]`
type ClickObj[T] = TypedTraitor[T, Clickable]
var
elements = [
Traitor[Clickable] Button(w: 10, h: 20), # We can convert directly if we use `emitConvert`
Button(x: 30, y: 30, w: 10, h: 10),
Radio(x: 30, y: 30, r: 10).toTrait(Clickable) # Otherwise we need to convert with `toTrait(trait)`
]assert elements[0].over(3, 3) # We can call procedures as if they're normal
assert elements[1].over(33, 33)
assert elements[2].over(30, 30)for i, x in elements:
if x of TypedTraitor[Button, Clickable]: # We can use `of` to check if it's the given type
assert i in [0, 1]
elif x of ClickObj[Radio]:
assert i == 2assert elements[2].getData(Radio) == Radio(x: 30, y: 30, r: 10) # We can use `getData` to extract data
elements[2].getData(Radio).x = 0 # It emits a `var T` so it can be mutated
assert elements[2].getData(Radio).x == 0let elem = ClickObj[Button](elements[0])
elem.setProc onClick, proc(arg: Traitor[Clickable]): string = "Hmmmm"
for i, x in elements:
let msg = x.onClick()
case i
of 0:
assert msg == "Hmmmm"
of 1:
assert msg == "Clicked a button"
of 2:
assert msg == "Clicked a radio"
else:
assert falsefor i, elem in elements:
elem.unpackIt:
if i in [0, 1]:
assert it is TypedTraitor[Button, Clickable]
else:
assert it is TypedTraitor[Radio, Clickable]Clickable.repackIt(0):
assert It is TypedTraitor[Button, Clickable]Clickable.repackIt(1):
assert It is TypedTraitor[Radio, Clickable]```
## Additional information
Defining `-d:traitorNiceNames` can be used to make the generate procedures have nicer names for debugging.