Hallo Forum,
ich wuerde euch gerne kurz das smalldragon-Projekt vorstellen.
smalldragon ist eine einfache statisch typisierte Programmiersprache,
die nach C transpiliert werden kann.
Beispiel-Code:
1
//struct-field reordering will get applied to improve structure packing
2
structExample{
3
@privatechara;//only accessible in this file
4
uint32b;
5
charc;
6
uint32d;
7
}
8
fnmain()~>int{
9
10
x=2;//lokale variablen typinferenz
11
12
if0<x<3{//comparison-operator-chaining
13
x++;
14
}
15
16
uint8y=3;//fixed-width integer types
17
18
foriin0..x{//ranges
19
y=subr((m)->m+1,x);//basic lambda support
20
}
21
return0;
22
}
23
@deprecated//a few basic annotations
24
fnsubr(((int)->int)f,intk)->int{
25
returnf(k);
26
}
Das Projekt ist nicht als Ersatz fuer C/C++ oder so gedacht,
sondern als Ergaenzung um schnell kleine Software-Module erstellen
zu koennen, und dennoch die Vorteile von C zu nutzen bezueglich
der Performance, Support fuer viele Architekturen, etc..
Bestimmte Features von C werden in smalldragon vorraussichtlich nicht
verfuegbar sein, wie etwa pointer-arithmetik, preprocessor,
inline-assembly,
etc...
Ueber euer Feedback wuerde ich mich freuen :)
Projekt-Website: https://smalldragon.org/
Github: https://github.com/pointbazaar/smalldragon
Gruss, Alexander
Andras H. schrieb:> Ehrlich? Ist es nicht egal ob man
Die Grammatik von C ist kein genialer Wurf gewesen. Wenn jemand eine
neue definiert, kann ich das nachvollziehen. Allerdings würde ich
empfehlen, auch Variablendeklarationen mit einem Keyword einzuleiten,
statt mit einem Typ. Dann sind Typnamen normale Identifier. Mit den
später nachgereichten Typedefs ging in C der Spass los.
Naja, mir gefällt reines C ehrlich gesagt besser.
Die Funktionsschreibweise mit fn ~> (wie in rust / go) ist etwas
ungewohnt.
Bei der lokale variablen typinferenz habe ich etwas bedenken, weil man
die Variable überhaupt nicht deklarieren muss. Wie ist dort der scope?
By python kann man schnell mal in sowas rein laufen:
1
#!python
2
defx(name):
3
fornamein["test"]:
4
pass
5
returnname# ist immer "test"
Kann einem hier sowas auch passieren? Sind die Variablen implizit
Funktions oder Block scope? Hat die Sprache überhaupt blöcke und block
scopes? Und hier:
https://smalldragon.org/html/local-var-typeinference.html ist das
wirklich "runtime cost" oder ist da "compiletime cost" gemeint?
Ein paar Beispiele enthalten while(true){}, aber anderenorts sind keine
Klammern. Wäre while true {} nicht konsistenter? Oder ist das nur ein
Dokumentationsartefakt?
Dann zu den Generics. Für mich sind das eher typ templates. Unter einem
Generic würde ich eher sowas verstehen:
1
structsomething_generic{
2
inta;
3
void*x;
4
intb;
5
}
6
7
structsomething_int{
8
inta;
9
int*x;
10
intb;
11
}
Der Unterschied ist, something_int und something_generic sind gleich
gross. Man kann struct something_int nach struct something_generic und
zurück casten, wie in Java. Wäre x hier kein pointer, ist das nicht der
fall. Das ist dann wie bei C++ templates.
Ich bin mir auch nicht sicher, wie sinnvoll es ist, die Funktionen als
"Subroutinen" zu bezeichnen. Haben diese irgendwelche speziellen
Semantiken? Kehren sie nicht immer zurück wie in ASM, oder haben sie
keinen Stack für die Argumente wie Fortran, oder gibt es andere
Besonderheiten bezüglich des control flows (können sie yieldn wie ein
Generator, oder zwischeneinander switchen wie coroutinen?). Eine
Funktion ist auch eine Subroutine, aber ist da irgend was was diese
"Subroutinen" hier von Funktionen unterscheidet?
Der nächste Punkt wäre Modularität und Interfaces. Ich bin ein grosser
Fan von Modularität und Interfaces. In C, jede Kompillationsunit ist
unabhängig kompilierbar, und die Headerfiles stellen ein Interface dar,
um alles zusammenzusetzen. Ist sowas hier noch machbar.
Das nächste wäre compile time reflection introspection Macros. Gibt
es hier irgendws?
Und dann zum Schluss, naja, wie immer sind vor allem die Dinge nicht
drin, die für mich eigentlich speziell interessant wären. Dinge die ich
unter anderem gerne in meinen Programmen verwende sind:
* Linktime linked lists, also globale unveränderbare Listen über
mehrere Dateien verteilt, sowie optionale Implementierungen. Solches
Zeugs:
https://www.mikrocontroller.net/articles/C_Linkerhacks_%2B_Modularisierung
* Interfaces. In C, im Grunde einfach ein Structtyp mit
Funktionspointern, und ein Structtype einen Pointer darauf hat. Das
Implementieren des Interface ist dann einfach, letzteres bei einem
anderen Struct als member zu haben, und beim Instantiieren den Pointer
auf die Implementation (das struct objekt mit den Funktionen drinn), zu
setzen.
* Mehr STL zeugs. Hashmaps, Dynamische Arrays, (Doubly) Linked Listen,
eventuell ein generisches Listeninterface für alle. Eventuell was für
reference counting, wie in C++ die smart pointer. Was man halt früher
oder später so braucht.
Was auch toll wäre, ist aber eher Wunschdenken:
* Exception handling. Es gibt ja viele Methoden, throw in C++, Optional
/ tuple zeug in anderen Sprachen (also per typisiertem sentinel im
Rückgabewert). Einfach was besseres als setjump...
* Da kommt mir gerade noch der Gedanke, Optional, tuple zeug,
typisierte Sentinelwerte. Sowas wäre schon praktisch.
* async / await & Generatoren fände ich echt praktisch, aber leider
schwierig umzusetzen. Aber Allgemein, primitive für event loops, warten
auf input (eventuell bytes von Files, anderes Zeugs von anderen
Programmteilen (pipes), etc.), managen von callbacks, solches zeugs,
sind immer sehr nützlich. Leider aber auch sehr, sehr schwer nicht
invasiv und gut umzusetzen.
Oh, ich habe gerade gesehen, mit -> und ~> hast du eine Unterscheidung
zwischen Idempotenten (ohne Seiteneffekte) und normalen Funktionen (mit
Seiteneffekten).
Das Finde ich gut.
Und wie hast du eigentlich das halting Problem mit @halts gelöst?
Daniel A. schrieb:> Oh, ich habe gerade gesehen, mit -> und ~> hast du eine Unterscheidung> zwischen Idempotenten (ohne Seiteneffekte) und normalen Funktionen (mit> Seiteneffekten).> Das Finde ich gut.
Der Unterschied zwischen „~>“ und „->“ ist beim überfliegen je nach Font
viel zu klein, imo. Da fänd ich ein extra Keyword besser (meinetwegen
„sub“ für Nebeneffekte und „fn“ für reine Funktionen?).
Ich lese die Doku so, dass die Typinferenz zur Laufzeit geschieht? Das
wäre zumindest unituitiv, sofern da keine Variant-Konstrukte im Spiel
sind.
Rückgabetypen nach den Namen, aber Variablentypen vor den Namen ist imo
etwas inkonsistent. Zumal Variablentypen hinter den Namen eine deutlich
sauberere Syntax für Inferenz ermöglicht.
Lambdas in C … Ein Traum ^^
Die Sprache hätte für mich aktuell allerdings wohl nicht genug „selling
points“ um sie statt C zu benutzen und anderen Alternativen vorzuziehen.
Aber wenn ich das Projekt richtig verstehe, ist der Fokus aber auch gar
nicht, die komplette Software in der Sprache zu schreiben. Dann wäre
etwas Doku, wie man das Resultat dann in C/C++ einbindet wohl hilfreich.
Ach ja: Gibt es überhaupt Arrays in irgendeiner Form? Braucht man ja
hin- und wieder mal. In der Standardbibliothek gibt es auf jeden Fall
Funktionen, die welche verarbeiten, aber nirgendwo was, wie man sie
erstellt oder deklariert.
Die Syntax zur Definition mit Typinferenz finde ich auch etwas
unglücklich, da sie nicht von einer Zuweisung zu unterscheiden ist.
Aber ansonsten cooles Projekt mit interessanten Ideen. :)
Endlich binden die bitweisen Operatoren &, ^ und | stärker als die
Vergleichsoperatoren, so wie es sein soll.
Ein paar Probleme gibt es noch mit Array-Überläufen bei langen
Variablen- und Typnamen, aber darauf hast du ja teilweise in Kommentaren
schon hingewiesen und wirst das sicher noch beheben.
Yalu X. schrieb:> Endlich binden die bitweisen Operatoren &, ^ und | stärker als die> Vergleichsoperatoren, so wie es sein soll.
Da kann ich nur zustimmen. In C/C++ ist das etwas unglücklich.
Andras H. schrieb:> Ehrlich? Ist es nicht egal ob man> fn main() ~> int {> oder> int main(void) {> schreibt?
Hallo Andreas,
es ist fuer mich persoenlich klarer, den Typ:
1
() ~> int
vom Name
1
main
zu trennen und einfach nebeneinander zu schreiben, ohne beide zu
vermischen. Ist natuerlich Praeferenz Sache.
Daniel A. schrieb:> Oh, ich habe gerade gesehen, mit -> und ~> hast du eine Unterscheidung> zwischen Idempotenten (ohne Seiteneffekte) und normalen Funktionen (mit> Seiteneffekten).> Das Finde ich gut.>> Und wie hast du eigentlich das halting Problem mit @halts gelöst?
Hallo Daniel,
Vielen Dank fuer dein Feedback :)
@halts ist bei mir eine Annotation, die hier (vielleicht zu kurz)
dokumentiert ist:
https://smalldragon.org/html/annotations.html
Natuerlich kann man nicht fuer jede Funktion entscheiden, ob sie
terminiert
oder nicht.
Man kann @halts aber benutzen um einige wenige funktionen zu
annotieren, bei denen es sehr leicht ist zu zeigen, dass sie
terminieren.
So leicht, dass das sogar der Transpiler uebernehmen kann.
Wenn smalldragon dann nicht zeigen kann, dass es terminiert, gibt's
ne kleine Fehlermeldung.
Jemand schrieb:> Ach ja: Gibt es überhaupt Arrays in irgendeiner Form? Braucht man ja> hin- und wieder mal. In der Standardbibliothek gibt es auf jeden Fall> Funktionen, die welche verarbeiten, aber nirgendwo was, wie man sie> erstellt oder deklariert.
Hallo,
Arrays gibt es konzeptionell schon, dynamisch alloziert mit malloc.
1
2
[char] arr = malloc(30);
3
arr[4] = 'k';
ist ein Beispiel.
In den Ordnern stdlib/ bzw. examples/ sollten sich weitere finden :)
Allerdings ist das momentan eher ein Kompromiss,
da ich das Konzept von sizeof noch nicht uebertragen habe.
Man muss sozusagen wissen, wie gross der datentyp ist, von dem man ein
Array allozieren will.
Gruss
Jemand schrieb:> Daniel A. schrieb:>> Oh, ich habe gerade gesehen, mit -> und ~> hast du eine Unterscheidung>> zwischen Idempotenten (ohne Seiteneffekte) und normalen Funktionen (mit>> Seiteneffekten).>> Das Finde ich gut.>> Der Unterschied zwischen „~>“ und „->“ ist beim überfliegen je nach Font> viel zu klein, imo. Da fänd ich ein extra Keyword besser (meinetwegen> „sub“ für Nebeneffekte und „fn“ für reine Funktionen?).>> Ich lese die Doku so, dass die Typinferenz zur Laufzeit geschieht? Das> wäre zumindest unituitiv, sofern da keine Variant-Konstrukte im Spiel> sind.>> Rückgabetypen nach den Namen, aber Variablentypen vor den Namen ist imo> etwas inkonsistent. Zumal Variablentypen hinter den Namen eine deutlich> sauberere Syntax für Inferenz ermöglicht.>> Lambdas in C … Ein Traum ^^>> Die Sprache hätte für mich aktuell allerdings wohl nicht genug „selling> points“ um sie statt C zu benutzen und anderen Alternativen vorzuziehen.>> Aber wenn ich das Projekt richtig verstehe, ist der Fokus aber auch gar> nicht, die komplette Software in der Sprache zu schreiben. Dann wäre> etwas Doku, wie man das Resultat dann in C/C++ einbindet wohl hilfreich.
Hallo,
den Unterschied von "->" zu "~>" beim Lesen ist etwas klein, da hast du
Recht.
Das mit dem extra Keyword finde ich ist eine gute Idee.
Die Typinferenz passiert nicht zur Laufzeit, das war etwas ungluecklich
formuliert. Habe es umformuliert.
Du hast Recht, dass
1
2
uint x = 3;
3
fn main () ~> int {}
den Typ mit dem Bezeichner jeweils andersherum haben.
Das ist inkonsistent, allerdings habe ich mich da
bei den Variablen erstmal fuer die gaengige Konvention enschieden.
Wie meinst du, dass
>Zumal Variablentypen hinter den Namen eine deutlich> sauberere Syntax für Inferenz ermöglicht.
?
Ja, genau, es ist dafuer gedacht dass man es alongside C
bzw auch einmalig verwenden kann.
Wenn man kurz etwas aufschreiben will, oder fuer eine
implementierung besonders viel mit Funktionspointern arbeitet,
dann kann man dafuer smalldragon verwenden,
einen C Code transpilieren, das ganze durch astyle jagen oder
per IDE so formatieren wie man will und dann einfach
nur noch mit dem C Code weitermachen, den man dann
verfeinern und optimieren kann.
Bzgl. der Beispiele hast du Recht, es sollte eine Anleitung geben,
wie man es zusammen mit C verwendet.
Danke fuer dein Feedback :)
Hallo Alexander,
was genau ist deine Intention, SmallDragon zu
entwickeln/zu verwenden? Eine abstrahiertere
Sprache als C verwenden, um trotzdem andere
C-Libs leicht anbinden zu können? Performance-
Vorteile durch Benutzung etablierter C-Compiler?
Dazu habe ich auf den Webseiten nichts gefunden.
merciless
Dirk K. schrieb:> Hallo Alexander,>> was genau ist deine Intention, SmallDragon zu> entwickeln/zu verwenden? Eine abstrahiertere> Sprache als C verwenden, um trotzdem andere> C-Libs leicht anbinden zu können? Performance-> Vorteile durch Benutzung etablierter C-Compiler?>> Dazu habe ich auf den Webseiten nichts gefunden.>> merciless
Hallo Dirk,
beide deine Punkte sind korrekt. ich will eine (fuer C Programmierer)
leicht verstaendliche aber abstrahiertere Programmiersprache bieten,
wo man sowas wie
- Lambdas,
- Lokale-Variablen Typinferenz,
- Generics(spaeter),
- try-catch Exception Handling
...
einfach verwenden kann, und immer weiss, was im Hintergrund passiert.
Momentan kann man die generierten C Sources dazu ja einfach einsehen.
z.b. try-catch verwendet einfach <setjmp.h> und der jmp_buf wird
als pointer weitergereicht.
Die Performance-Vorteile bei Verwendung etablierter C-Compiler
sind natuerlich beachtlich zur Alternative, etwa ein eigenes
Backend fuer x86 zu schreiben.
smalldragon soll leicht in existierende C-Projekte einzubinden sein,
und es soll auch einfach sein, C-Code in ein smalldragon Projekt
einzubinden.
Es ist nicht stabil und hat bestimmt viele Bugs, aber wenn man die C
Sources
einmal generiert hat, braucht man die smalldragon Sources ja auch nicht
mehr ^^.
Allgemeine Ziele sowie Design-Entscheidungen sind hier zu finden:
https://smalldragon.org/html/project-goals.html
Allerdings wird diese Seite auch nicht jede Frage beantworten koennen :)
Daher immer gerne Fragen/Kritik.
Gruss
Alexander H. schrieb:> aber wenn man die C> Sources> einmal generiert hat, braucht man die smalldragon Sources ja auch nicht> mehr ^^.
Du benutzt SmallDragon nur einmalig, um C-Sourcen
zu generieren? Und die Pflege erfolgt dann in den
C-Sourcen?
merciless
Dirk K. schrieb:> Alexander H. schrieb:>> aber wenn man die C>> Sources>> einmal generiert hat, braucht man die smalldragon Sources ja auch nicht>> mehr ^^.> Du benutzt SmallDragon nur einmalig, um C-Sourcen> zu generieren? Und die Pflege erfolgt dann in den> C-Sourcen?>> merciless
Hi Dirk,
so kann man es benutzen. Ich habe darauf geachtet dass die generierten
C Sources noch lesbar sind, die namen von variablen usw. werden 1:1
uebernommen.
Wenn man einen anderen Style/Indentation haben will, kann man es ja noch
durch astyle jagen :). Mit dem -h Flag kann man auch entsprechende
Header generieren.
Ich habe es selbst nicht so benutzt, weil ich momentan eher an
smalldragon
arbeite, anstatt an Projekten mit smalldragon (beides nebenher waere zu
viel fuer mich).