Als er een ding is dat ik heb geleerd gedurende mijn studietijd tot op heden is dat school je niet alles KAN leren. Je studie tijd is een tijd waarin je juist dit soort (on)zinnige dingen kunt doen, en dat dien je ook te doen imho. Het is een tijd waarin je nog niet vast zit aan het burgerlijk leven, van vrouw, kind en 'dat soort verplichtingen'. Het curriculum van een opleiding kan onmogelijk --alles-- bevatten, en het is aan jou om je ook te verdiepen in dingen die je interesseren. Het curriculum geeft je hooguit de basics om die verdiepingen te kunnen doen. Je voelt je misschien nu wel king of the hill op je opleiding, en dat zal misschien ook wel zo zijn, het is stom om je te meten aan anderen omdat je imho je ontwikkeling dan laat beperken adhv hoe ver de anderen zijn. Gewoon je eigen ontwikkeling maken dus gedurende deze tijd, dat scheidt het koren ook van het kaf.roy-t schreef op maandag 08 september 2008 @ 17:53:
(en jep jij bent ook student, maar voordat ik aan mijn master begin ben ik nog wel even bezig. Het niveau van andere beginnende Inf. studenten hier op de uni valt me trouwens erg tegen, bijna niemand heeft al eens iets gemaakt, laat staan iets wat nu echt gebruikt wordt door een bedrijf ofzo... (ik dacht dat iedereen al een stuk verder zou zijn, maar somehow ben ik van mijn klas veruit het verste, achja ze zullen wel snel bij gewerkt worden aangezien ik al wat eindejaars vakken gehad heb (wegens studiewisseling halverwege) en die waren toch op redelijk niveau).
Laat ik mezelf completeren; want ik bedoelde namelijk globale variabelen.
Het is waar dat alles wat je in globale/initial namespace kan worden beschouwd en aangesproken als global. Toch zit er een significant verschil tussen data die binnen verschillende scopes berschikbaar moet zijn en een globaal object. In de scope van een klasse (welke zich bijv. in een module bevindt) wil ik helemaal niets te maken hebben met eventuele globals van de 'main'. Wanneer de ontwikkelaar behoefte heeft aan gedeelte configuratiedata dan is het zijn taak om die onder te brengen in een centrale klasse - waar inderdaad wat mee gezeuld moet worden. Dit is altijd nog beter dan globals te vaak te onpas ergens op komen dagen.
Wat dat betreft mis ik een beetje het java import-statement icm packages/plugins/modules (you name it). Dit heeft niet zozeer met de taal alswel met het beoogde platform te maken.
Het is waar dat alles wat je in globale/initial namespace kan worden beschouwd en aangesproken als global. Toch zit er een significant verschil tussen data die binnen verschillende scopes berschikbaar moet zijn en een globaal object. In de scope van een klasse (welke zich bijv. in een module bevindt) wil ik helemaal niets te maken hebben met eventuele globals van de 'main'. Wanneer de ontwikkelaar behoefte heeft aan gedeelte configuratiedata dan is het zijn taak om die onder te brengen in een centrale klasse - waar inderdaad wat mee gezeuld moet worden. Dit is altijd nog beter dan globals te vaak te onpas ergens op komen dagen.
Wat dat betreft mis ik een beetje het java import-statement icm packages/plugins/modules (you name it). Dit heeft niet zozeer met de taal alswel met het beoogde platform te maken.
Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!
Namespaces dus, was al genoemd
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Je hebt imo een namespace en een executionspace en dat zijn verschillende dingen. Een bepaalde klasse kan met zijn functies in een aparte namespace vallen maar qua executionspace overlappen: het verschil tussen 'code time' en execution time. Maargoed het punt is gemaakt. Ik ben benieuwd naar de implementatie.
Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!
Sorry, maar ik snap echt geen zak van wat je nou probeert te zeggen. Eerst zeg je dat je java's import en packages mist. Packages zijn gewoon namespaces, en niets meer dan dat. Ze zorgen totaal niet voor thread-safety.
Als jij dat niet wilt, dan moet je die globals niet accessen, en als main() dat niet wilt dan moet main() die globals niet exposen. Ik snap echt niet waarom je zou moeten verplichten de global namespace leeg te houden. En aangezien dat impliceert dat freeform code zoals de meeste scripttalen dat kennen dan niet meer mogelijk is wil ik er niet eens aan. Als jij je variabelen wilt afschermen dan moet je dat gewoon doen. En dit alles staat compleet los van thread-safety.In de scope van een klasse (welke zich bijv. in een module bevindt) wil ik helemaal niets te maken hebben met eventuele globals van de 'main'
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
@LauPro:
Misschien ten overvloede, maar 'k denk dat het goed is om te onderstrepen dat in een totaal object georienteerde taal zoals Ruby (en hopelijk ook poisyn/pinp/pimp), classes/functies/modules in feite ook objecten zijn. En wel constante objecten die in de global namespace leven.
Java's import statement doet niks anders eigenlijk dan de namespace importen, wat inhoudt dat het naast de bijbehorende classes loaden ervoor zorgt dat je niet de fully qualified name hoeft te gebruiken (tenzij er een namespace clash ontstaat als gevolg ervan en je zelf voor disambiguity dient te zorgen middels b.v. fully qualified names).
m.a.w. ook ik zie niet hoe dit te maken heeft met thread safety.
Misschien ten overvloede, maar 'k denk dat het goed is om te onderstrepen dat in een totaal object georienteerde taal zoals Ruby (en hopelijk ook poisyn/pinp/pimp), classes/functies/modules in feite ook objecten zijn. En wel constante objecten die in de global namespace leven.
Java's import statement doet niks anders eigenlijk dan de namespace importen, wat inhoudt dat het naast de bijbehorende classes loaden ervoor zorgt dat je niet de fully qualified name hoeft te gebruiken (tenzij er een namespace clash ontstaat als gevolg ervan en je zelf voor disambiguity dient te zorgen middels b.v. fully qualified names).
m.a.w. ook ik zie niet hoe dit te maken heeft met thread safety.
Ja daar zat ik aan te denken. Moeten ze wel constant zijn? Zou het niet cool zijn om te kunnen doen:prototype schreef op dinsdag 09 september 2008 @ 12:45:
classes/functies/modules in feite ook objecten zijn. En wel constante objecten die in de global namespace leven.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| function someAPIFunction() { // do something } // ... // lalala, somewhere in my private script auto oldSomeAPIFunction = someAPIFunction; someAPIFunction = function() { output("someAPIFunction() is about to be called"); oldSomeAPIFunction(); output("someAPIFunction() was called"); } someAPIFunction(); // calls my own wrapper |
En vanaf dan worden alle calls naar someAPIFunction() gereroute naar je eigen wrapper
[ Voor 13% gewijzigd door .oisyn op 09-09-2008 13:58 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Dat opent deuren voor heel veel handige constructs die ook heel erg goed de soep in kunnen lopen. Het doet me een beetje denken aan Aspect Oriented Programming.
Mjah, ze hoeven idd niet constant te zijn. Wat jij nu in feite probeert te doen met zo'n situatie is eigenlijk aspect orientatie proberen toe te passen op API functies. Hoog monkeypatch gehalte is mogelijk, maar goede programmeurs zouden hier idd wel veel uit kunnen halen denk ik. In java/C# lossen ze zoiets op door annotations/attributes he? Die meta data kan dan ge-accessed worden door een runner, wat b.v. JUnit 4 ook doet..oisyn schreef op dinsdag 09 september 2008 @ 13:55:
[...]
Ja daar zat ik aan te denken. Moeten ze wel constant zijn? Zou het niet cool zijn om te kunnen doen:
code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function someAPIFunction() { // do something } // ... // lalala, somewhere in my private script auto oldSomeAPIFunction = someAPIFunction; someAPIFunction = function() { output("someAPIFunction() is about to be called"); oldSomeAPIFunction(); output("someAPIFunction() was called"); } someAPIFunction(); // calls my own wrapper
En vanaf dan worden alle calls naar someAPIFunction() gereroute naar je eigen wrapper. Dat zou ook heerlijk zijn voor profiling en debugging.
Wat dacht je trouwens van een "catch all" methode te hebben in Object b.v., genaamd method_missing(name, args...) welke geinvoked wordt als je een methodnaam naar het object send/invoked die niet bestaat? Dien je te overriden dus in je subclasses om zo b.v. methodes on the fly genereren of zelf method dispatch te regelen. Vergelijk 't maar met PHP's magic __call function of Ruby's method_missing(name, *args) ;-)
Let wel op, wanneer je zoiets toestaat wordt een interface definitie alleen maar nog meer nutteloos.
[ Voor 5% gewijzigd door prototype op 09-09-2008 14:25 ]
Voor late bound objects wou ik eigenlijk een apart type introduceren waarvan je moet overerven. Een beetje a la COM's IDispatch. Om op die manier on the fly proxies te kunnen genereren voor bijv. COM, .Net of Java objecten.
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Je kent neem ik aan het begrip autothreading van Perl? Je kan daar met het pure keyword zorgen dat sommige functies (in de toekomst) automatisch worden gethread. Op dat moment is het weldegelijk van belang dat bijv. je globals (in welke context dan ook) thread safe zijn..oisyn schreef op dinsdag 09 september 2008 @ 11:17:
Sorry, maar ik snap echt geen zak van wat je nou probeert te zeggen. Eerst zeg je dat je java's import en packages mist. Packages zijn gewoon namespaces, en niets meer dan dat. Ze zorgen totaal niet voor thread-safety.
Het is gewoon een bepaalde mentaliteit. Compatibiliteit houden met bijv. PHP lijkt mij schier onmogelijk. Je zou mogelijk een converter kunnen schrijven maar ik vrees dat die even bruikbaar zijn als de asp->php converters. PHP heeft veel ruimte, ik denk dat je juist bij zo'n nieuw taal de kans moet aangrijpen om je developers ook een beetje op te voeden, en dus geen (super)globals te gebruiken (dus onmogelijk maken).Als jij dat niet wilt, dan moet je die globals niet accessen, en als main() dat niet wilt dan moet main() die globals niet exposen. Ik snap echt niet waarom je zou moeten verplichten de global namespace leeg te houden. En aangezien dat impliceert dat freeform code zoals de meeste scripttalen dat kennen dan niet meer mogelijk is wil ik er niet eens aan. Als jij je variabelen wilt afschermen dan moet je dat gewoon doen. En dit alles staat compleet los van thread-safety.
Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!
En wat we nou al de hele tijd duidelijk proberen te maken is dat het niet thread safe zijn niet de schuld is van globals, dus door globals te voorkomen verhelp je de problemen niet. Zelfs al zou er geen enkele statische variabele meer bestaan en kun je alleen nog maar members van objects accessen, dan nog houd je het probleem omdat dat ene object dan geshared gaat worden tussen de verschillende threads.LauPro schreef op dinsdag 09 september 2008 @ 14:45:
[...]
Je kent neem ik aan het begrip autothreading van Perl? Je kan daar met het pure keyword zorgen dat sommige functies (in de toekomst) automatisch worden gethread. Op dat moment is het weldegelijk van belang dat bijv. je globals (in welke context dan ook) thread safe zijn.
Thread safety heeft te maken met concurrent access naar een bepaalde state. Het doet er daarbij niet toe of die state global is of niet.
Ik had het over het supporten van freeform code. Er zit nogal een grote afstand tussen het supporten van een bepaalde feature en volledige PHP compatibiliteitHet is gewoon een bepaalde mentaliteit. Compatibiliteit houden met bijv. PHP lijkt mij schier onmogelijk.
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Autothreading heb ik wat van meegepikt op de Perl 6 mailinglist, maar daar zijn ze ten eerste nog lang niet uit wat het precies moet doen. Wat ik je in ieder geval wel kan zeggen adhv wat ik gelezen heb is dat autothreading de compiler laat bepalen dat de functie multithreaded gedraaid mag worden. Van een formele methoden standpunt, mag dit alleen plaatsvinden als thread-safety gegerandeerd kan worden. Een van die manieren is dat de threads geen waarneembare side-effects mogen bewerkstelligen mbt tot elkaar en dat kan --heel kort door de bocht-- door niks te delen. Globals zijn idd accessible vanuit de threads, MAAR, het is hier aan de programmeur imho om hier zelf locking mechanismen voor te bedenken (i.e. zelf maar een semafoor hiervoor gebruiken). Je zou ook kunnen zeggen dat code dat gemarked wordt voor autothreading gewoon geen enkele side-effect mag bewerkstelligen op de global scope, maar dan kan je eigenlijk niet echt bijzonder nuttige software schrijven imhoLauPro schreef op dinsdag 09 september 2008 @ 14:45:
[...]
Je kent neem ik aan het begrip autothreading van Perl? Je kan daar met het pure keyword zorgen dat sommige functies (in de toekomst) automatisch worden gethread. Op dat moment is het weldegelijk van belang dat bijv. je globals (in welke context dan ook) thread safe zijn.
Je denkt teveel op niveau van een hogere taal naar een hogere taal vertaling. In het geval van PHP zouden we er een parser voor kunnen schrijven een een code generator die deze omzet naar de bytecode die onze VM begrijpt. Maar dan heb je alleen de grammatica en moet je nog die shitloads aan libraries doen... daar waag ik me liever niet aan[...]
Het is gewoon een bepaalde mentaliteit. Compatibiliteit houden met bijv. PHP lijkt mij schier onmogelijk. Je zou mogelijk een converter kunnen schrijven maar ik vrees dat die even bruikbaar zijn als de asp->php converters. PHP heeft veel ruimte, ik denk dat je juist bij zo'n nieuw taal de kans moet aangrijpen om je developers ook een beetje op te voeden, en dus geen (super)globals te gebruiken (dus onmogelijk maken).
Dit is mogelijk in Python..oisyn schreef op dinsdag 09 september 2008 @ 13:55:
[...]
Ja daar zat ik aan te denken. Moeten ze wel constant zijn? Zou het niet cool zijn om te kunnen doen:
code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function someAPIFunction() { // do something } // ... // lalala, somewhere in my private script auto oldSomeAPIFunction = someAPIFunction; someAPIFunction = function() { output("someAPIFunction() is about to be called"); oldSomeAPIFunction(); output("someAPIFunction() was called"); } someAPIFunction(); // calls my own wrapper
En vanaf dan worden alle calls naar someAPIFunction() gereroute naar je eigen wrapper. Dat zou ook heerlijk zijn voor profiling en debugging.
Sterker nog, ik heb dit een keer toegepast omdat xmlrpclib niet aan mijn wensen voldeed. Het serializen van een object was nog al primitief (=dict dump), dus ik voegde wat toe om te zorgen dat objecten met een serialize/deserialize methode netjes automagisch werden omgezet.
Ipsa Scientia Potestas Est
NNID: ShinNoNoir
Dit werkt ook in JavaScript... Daar kan je een functie als "variabele" gebruiken. En dan kan je daarna variabele ook nog aanroepen.RayNbow schreef op dinsdag 09 september 2008 @ 17:20:
[...]
Dit is mogelijk in Python.
Sterker nog, ik heb dit een keer toegepast omdat xmlrpclib niet aan mijn wensen voldeed. Het serializen van een object was nog al primitief (=dict dump), dus ik voegde wat toe om te zorgen dat objecten met een serialize/deserialize methode netjes automagisch werden omgezet.
code:
1
2
3
4
5
6
7
8
9
10
| function MyFunction(x) { } var oldFunction = MyFunction; function MyFunction(x) { //This is the new function //Call the old one oldFunction(x); } |
Werkt perfect. Heb dit in .NET 1.1 een paar keer moeten gebruiken om checks/functionaliteit in te bouwen voordat de postback werd aangeroepen. Minder leuk was het toen Microsoft besloot in 2.0 de postback functies pas later op in de pagina aan te maken waardoor mijn scripts de mist in gingen.
The #1 programmer excuse for legitimately slacking off: "My code's compiling"
Firesphere: Sommige mensen verdienen gewoon een High Five. In the Face. With a chair.
Big deal, dat kan in zoveel talen.Gertjan. schreef op dinsdag 16 september 2008 @ 15:12:
[...]
Dit werkt ook in JavaScript... Daar kan je een functie als "variabele" gebruiken. En dan kan je daarna variabele ook nog aanroepen.
JavaScript:
1
2
| function bla() { } bla = 4; |
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Nee, maar .oisyn heeft een fulltime job als game developer en ik studeer en draai een zaak ernaastVerwijderd schreef op dinsdag 16 september 2008 @ 15:01:
Is dit een stille dood gestorven?
Verwijderd
Wat mij betreft is kwaliteit in dit geval een stuk belangrijker dan kwantiteit. Niemand zit te wachten op nòg zo'n gedrocht als PHP.
Niet dat ik me zorgen maak over de kwaliteit overigens, voor zover ik er iets van begrijp.
Even een kleine update:
Heb het helaas momenteel ontzettend druk, maar ik wil jullie wel even laten weten dat ik hier nog mee bezig ben: in m'n vrije tijd zit ik me in te lezen op LLVM. Handig om te lezen denk ik en te doen om je C++ op te poetsen
Indien er ook nog mensen hier zijn die geinteresseerd zijn in hoe je uberhaupt moet beginnen met een programmeertaal te implementeren (wat de LLVM tutorial opzich ook al doet, maar beter teveel dan te weinig>?
), heb ik een uitgebreid artikel geschreven op m'n blog over hoe je dit zou moeten doen voor de esoterische taal Brainfuck.
Ook is m'n onderneming benaderd door een zeker IT bedrijf waarvan de meesten wel een muziekspeler hebben om te kijken of er een mogelijkheid is om ons te laten werken aan een VM voor hun besturingssysteem, gebruikmakend van LLVM. Dit zal hoogstwaarschijnlijk als open source gepubliceerd mogen worden en biedt me dus eventueel de kans om hier fulltime aan te werken. Er staat nog niks vast though, maar stay tuned :-)
Heb het helaas momenteel ontzettend druk, maar ik wil jullie wel even laten weten dat ik hier nog mee bezig ben: in m'n vrije tijd zit ik me in te lezen op LLVM. Handig om te lezen denk ik en te doen om je C++ op te poetsen
Indien er ook nog mensen hier zijn die geinteresseerd zijn in hoe je uberhaupt moet beginnen met een programmeertaal te implementeren (wat de LLVM tutorial opzich ook al doet, maar beter teveel dan te weinig>?
Ook is m'n onderneming benaderd door een zeker IT bedrijf waarvan de meesten wel een muziekspeler hebben om te kijken of er een mogelijkheid is om ons te laten werken aan een VM voor hun besturingssysteem, gebruikmakend van LLVM. Dit zal hoogstwaarschijnlijk als open source gepubliceerd mogen worden en biedt me dus eventueel de kans om hier fulltime aan te werken. Er staat nog niks vast though, maar stay tuned :-)
[ Voor 27% gewijzigd door prototype op 31-10-2008 18:39 ]
Om er maar eens in te springen, =D.
In mijn tijd (opa vertelt) had ik een controller gemaakt in het Zend Framework die z'n superclass ook een __call()-functie had, die niet-bestaande actions opving en (mijn fout) de persoon terugstuurde naar de index van die controller, wat leidde tot een oneindige loop toen ook de index niet bestond,
.
Nee, laat het programma of op z'n spreekwoordelijke bek gaan, of laat het programma een (unchecked?) exception gooien, zodat een catch-all voor exceptions op z'n minst nog een fout kan weergeven.
Over het 'encapsulaten' van een functie binnen een andere functie, dat lijkt mij een soort van inline AOP, wat overigens niet eens zo'n slecht idee zou zijn. Ik heb me niet kunnen verdiepen in AOP, maar wat ik er van begreep (in de Java-implementatie) is dat je een class schrijft die via via maar tussen een client en een ander gepropt moest worden, maar het lijkt mij dat je het overzicht dan snel verliest, zeker in grotere projecten, zeker als je het veel gebruikt. Als je het gewoon inline kunt doen, danwel zo dicht mogelijk bij het geencapsuleerde stuk code, zou het een stuk beter zijn. Maar zoals gezegd, geen ervaring mee.
Alsjeblieft niet, als je een method probeert uit te voeren die niet bestaat, moet je script een fout geven omdat jij als programmeur dan een fout gemaakt hebt. Als je netjes af wilt vangen dat een niet-bestaande methode aangeroepen wordt, gooi dan een MethodNotFoundException (als je taal OO moet worden, danwel exceptions heeft), zodat de ontwikkelaar die nog af kan vangen.quote: prototypeWat dacht je trouwens van een "catch all" methode te hebben in Object b.v., genaamd method_missing(name, args...) welke geinvoked wordt als je een methodnaam naar het object send/invoked die niet bestaat? Dien je te overriden dus in je subclasses om zo b.v. methodes on the fly genereren of zelf method dispatch te regelen. Vergelijk 't maar met PHP's magic __call function of Ruby's method_missing(name, *args) ;-)
In mijn tijd (opa vertelt) had ik een controller gemaakt in het Zend Framework die z'n superclass ook een __call()-functie had, die niet-bestaande actions opving en (mijn fout) de persoon terugstuurde naar de index van die controller, wat leidde tot een oneindige loop toen ook de index niet bestond,
Nee, laat het programma of op z'n spreekwoordelijke bek gaan, of laat het programma een (unchecked?) exception gooien, zodat een catch-all voor exceptions op z'n minst nog een fout kan weergeven.
Over het 'encapsulaten' van een functie binnen een andere functie, dat lijkt mij een soort van inline AOP, wat overigens niet eens zo'n slecht idee zou zijn. Ik heb me niet kunnen verdiepen in AOP, maar wat ik er van begreep (in de Java-implementatie) is dat je een class schrijft die via via maar tussen een client en een ander gepropt moest worden, maar het lijkt mij dat je het overzicht dan snel verliest, zeker in grotere projecten, zeker als je het veel gebruikt. Als je het gewoon inline kunt doen, danwel zo dicht mogelijk bij het geencapsuleerde stuk code, zou het een stuk beter zijn. Maar zoals gezegd, geen ervaring mee.
Alsjeblieft wel. Ja, het is een opening voor vieze hacks, maar het is tevens een mogelijkheid om late dispatching te implementeren. Denk bijvoorbeeld aan objecten uit een andere taal / platform (COM, .Net, Java, ActiveScript), zonder dat je daar at compile-time al type definities voor nodig hebt.YopY schreef op vrijdag 31 oktober 2008 @ 21:16:
Alsjeblieft niet, als je een method probeert uit te voeren die niet bestaat, moet je script een fout geven omdat jij als programmeur dan een fout gemaakt hebt. Als je netjes af wilt vangen dat een niet-bestaande methode aangeroepen wordt, gooi dan een MethodNotFoundException (als je taal OO moet worden, danwel exceptions heeft), zodat de ontwikkelaar die nog af kan vangen.
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
SmalltalkWhen a message is sent to a Smalltalk-80 object, the method dictionaries associated with that object's class and its superclasses are searched at runtime. If none of these classes implement a method for a given message, the Smalltalk virtual machine sends the object the message doesNotUnderstand:. The original message selector and message arguments are bundled together in a Message object and passed as the argument to doesNotUnderstand:. The default method for this message is stored in class Object. This method invokes the Smalltalk debugger, since sending an object a message it does not implement is usually a sign of programmer error. However, objects that override doesNotUnderstand: can intercept unimplemented message at runtime, and process them as they see fit.
Addendum:
Smalltalk met een type-sausje: Strongtalk: Typechecking Smalltalk in a Production Environment (OOPSLA '93)
[ Voor 10% gewijzigd door RayNbow op 31-10-2008 22:35 ]
Ipsa Scientia Potestas Est
NNID: ShinNoNoir
Zie .oisyn en RayNbow. Bovendien, de default implementatie van method_missing ziet er dan ongeveer als volgt uit:YopY schreef op vrijdag 31 oktober 2008 @ 21:16:
Om er maar eens in te springen, =D.
[...]
Alsjeblieft niet, als je een method probeert uit te voeren die niet bestaat, moet je script een fout geven omdat jij als programmeur dan een fout gemaakt hebt. Als je netjes af wilt vangen dat een niet-bestaande methode aangeroepen wordt, gooi dan een MethodNotFoundException (als je taal OO moet worden, danwel exceptions heeft), zodat de ontwikkelaar die nog af kan vangen.
code:
1
2
3
4
5
| class Object { public functon method_missing(name, args...) { throw new MethodNotFoundException(); } } |
Wanneer een programmeur kiest dit te overriden, dan kan hij z'n eigen gedrag eraan toe kennen wat uitermate handig is voor meta programming. Men kan op deze wijze dan dynamisch methoden "genereren". Mensen die hier niks van willen weten moeten deze methode gewoon niet overriden dan
Tsjah, with great power comes great responsibility. Ik zeg hier trouwens niet mee dat PHP great powers heeft, integendeelIn mijn tijd (opa vertelt) had ik een controller gemaakt in het Zend Framework die z'n superclass ook een __call()-functie had, die niet-bestaande actions opving en (mijn fout) de persoon terugstuurde naar de index van die controller, wat leidde tot een oneindige loop toen ook de index niet bestond,.
Zie code example. Default behaviour voorziet hierin. Ik ben ook van de filosofie, if code fails, it should fail hard. Tegelijkertijd daarentegen zie ik het belang erin van de dynamische aard van een programmeertaal er in te houden.Nee, laat het programma of op z'n spreekwoordelijke bek gaan, of laat het programma een (unchecked?) exception gooien, zodat een catch-all voor exceptions op z'n minst nog een fout kan weergeven.
Closures hebben niet veel met AOP te maken. Aspect orientation is gedrag op bestaande code plakken zonder deze bestaande code te hoeven aan te passen, om zodoende cross cutting concerns, tangling en scattering tegen te gaan. Dit "plakken", i.e. superimposen, gebeurd veelal in een aparte weave fase en dit leidt vaak tot het probleem van "obliviousness". Hierbij ontstaat er dan een fout tijdens runtime die een bepaalde lijn aan code aanhaalt als zijnde de schuldige, echter is deze lijn aan code lastig te traceren in de originele code omdat het een lijntje code uit het "opgeplakte" gedrag kan zijn. Daarom wil ik geen AOP op deze wijze introduceren aan de taal. Then again, de taal kan me eigenlijk nog steeds bijster weinig schelen, het gaat me er echt om dat die VM uber vet gebouwd kan worden.Over het 'encapsulaten' van een functie binnen een andere functie, dat lijkt mij een soort van inline AOP, wat overigens niet eens zo'n slecht idee zou zijn. Ik heb me niet kunnen verdiepen in AOP, maar wat ik er van begreep (in de Java-implementatie) is dat je een class schrijft die via via maar tussen een client en een ander gepropt moest worden, maar het lijkt mij dat je het overzicht dan snel verliest, zeker in grotere projecten, zeker als je het veel gebruikt. Als je het gewoon inline kunt doen, danwel zo dicht mogelijk bij het geencapsuleerde stuk code, zou het een stuk beter zijn. Maar zoals gezegd, geen ervaring mee.
Closures is meer bedoeld om scope bindings te bewerkstelligen, het liefst op zo'n wijze dat dit kan voortvloeien uit de lexicale context ipv dat je dit expliciet moet opgeven (zoals dat 't geval is bij PHP). Zo kan je lekker blokken code schrijven die je binnen een methode misschien vaker gebruikt of die je kan gebruiken bij het aanroepen van een andere methode.
* kick * 
Het zou IMHO heel tof zijn om een dynamische taal als Python of Ruby te hebben, waarbij je optioneel types kunt opgeven. Dat kun je dan in de CPU-intensieve functies van het programma gebruiken, zodat de JIT betere code kan genereren. Volgens mij zie ik dat in .oisyn's ontwerp terug, dat maakt het erg interessant en kan deze taal best een succes maken
Het zou IMHO heel tof zijn om een dynamische taal als Python of Ruby te hebben, waarbij je optioneel types kunt opgeven. Dat kun je dan in de CPU-intensieve functies van het programma gebruiken, zodat de JIT betere code kan genereren. Volgens mij zie ik dat in .oisyn's ontwerp terug, dat maakt het erg interessant en kan deze taal best een succes maken
Verwijderd
.oisyn en prototype, kunnen jullie niet beter je tijd en kennis besteden aan Ruby of Python?
Jullie zijn nu bezig aan 'yet another script language' waar niemand op zit te wachten, terwijl de meeste dingen die jullie willen al in Ruby of Python kunnen.
Jullie zijn nu bezig aan 'yet another script language' waar niemand op zit te wachten, terwijl de meeste dingen die jullie willen al in Ruby of Python kunnen.
Verwijderd
offtopic:
They do it because they can! Het lijkt me toch wel een vrij nuttige tijdsbesteding hoor, wellicht dat de taal zelf niet ontzettend veel praktisch nut heeft, maar dommer zullen ze er sowieso niet van worden en dat is opzich al heel wat waard.
They do it because they can! Het lijkt me toch wel een vrij nuttige tijdsbesteding hoor, wellicht dat de taal zelf niet ontzettend veel praktisch nut heeft, maar dommer zullen ze er sowieso niet van worden en dat is opzich al heel wat waard.
Verwijderd
Worteltaart, volledig mee eens, maar ze zijn nu tijd en kennis aan het investeren in een nieuw project terwijl dat m.i. zinvoller is in bv. Ruby of Python.
'Because you can' is leuk voor thuis.
'Because you can' is leuk voor thuis.
Verwijderd
Wow mooi verhaal op je blog over de Brainfuck compiler. Ik heb zelf nog nooit met Ruby gewerkt, en de concepten die achter een parser/compiler zitten waren me volledig onbekend. Maar erg interessantprototype schreef op vrijdag 31 oktober 2008 @ 18:29:
Even een kleine update:
Heb het helaas momenteel ontzettend druk, maar ik wil jullie wel even laten weten dat ik hier nog mee bezig ben: in m'n vrije tijd zit ik me in te lezen op LLVM. Handig om te lezen denk ik en te doen om je C++ op te poetsen
Indien er ook nog mensen hier zijn die geinteresseerd zijn in hoe je uberhaupt moet beginnen met een programmeertaal te implementeren (wat de LLVM tutorial opzich ook al doet, maar beter teveel dan te weinig>?), heb ik een uitgebreid artikel geschreven op m'n blog over hoe je dit zou moeten doen voor de esoterische taal Brainfuck.
Ook is m'n onderneming benaderd door een zeker IT bedrijf waarvan de meesten wel een muziekspeler hebben om te kijken of er een mogelijkheid is om ons te laten werken aan een VM voor hun besturingssysteem, gebruikmakend van LLVM. Dit zal hoogstwaarschijnlijk als open source gepubliceerd mogen worden en biedt me dus eventueel de kans om hier fulltime aan te werken. Er staat nog niks vast though, maar stay tuned :-)
Denk dat ik je parser maar eens in C# probeer te bouwen, leuke oefening
Verwijderd
Brainfuck is in principe in iedere taal te maken die direct een byte array aan kan spreken. Maar waarom zou je dat willen?
Afterlife, zoals hij zelf al zegt: leuke oefening. Volgens mij zie je uberhaupt niets in alles wat ofwel nieuw ofwel al gedaan ofwel geen praktisch nut heeft. Maar al die dingen mogen dan misschien uiteindelijk geen nut naar jou idee, maar dat hebben ze echt wel, zoals hierboven ook al een keer gezegd is. Door de hele tijd te zeggen 'wat voor zin heeft het' leer je natuurlijk nooit iets en kom je nooit een stap verder.
Een zeer valide vraag Afterlife, en het is dan ook om deze reden dat ik al meerdere keren heb aangegeven "niet zoveel te geven om de taal zelf" en me vooral wil focussen op wat de virtual machine moet ondersteunen. Dan zul je snel in kunnen zien dat diezelfde virtual machine voor mijn geliefde Ruby gebruikt kan worden. En dat is ook eigenlijk mijn ware intentie (mijn design keuzes zijn bijna alleen gebaseerd op smalltalk en ruby, waarbij ik de syntax zo vertrouwd mogelijk aan PHP'ers wil laten overkomen). De huidige VM van Ruby 1.9 laat nog echt veel te veel te wensen over en het is daarom om deze reden dat een zeker bedrijf me hierover heeft gecontact. Python kan op gelijke wijze ondersteund worden aangezien ze qua language features pretty much overeenkomen. Het kan hoe dan ook op het simpele feit dat ze beiden turing complete zijn.Verwijderd schreef op zaterdag 08 november 2008 @ 15:22:
.oisyn en prototype, kunnen jullie niet beter je tijd en kennis besteden aan Ruby of Python?
Jullie zijn nu bezig aan 'yet another script language' waar niemand op zit te wachten, terwijl de meeste dingen die jullie willen al in Ruby of Python kunnen.
Als je dat artikel bekijkt beschrijf ik een 3-stage compiler, van handmatig een lexer bouwen, parser bouwen en treewalker. Ik beschrijf daarbij hoe je zowel een interpreter kan bouwen evenals een target code emitter. Het is jammer dat je niet voorbij brainfuck zelf kan kijken, maar wat ik hier beschrijf zijn de general stages van compilation/interpretation die je in modern day compilers aantreft. Dat ik als voorbeeldtaal toevallig brainfuck heb gekozen om de vertaler te implementeren is simpelweg om het voor mij compacter te maken op gebied van "instructies" en "uitleg" ;-) Als ik een mini PHP ofzo had uitgelegd (om maar wat te noemen), dan was het meer lexer/parse/walk werk, maar in grote lijnen nog steeds hetzelfde. Je zou dan nog concepten als symbolic tables langs zien komen en garbage collection, maar die concepten wil ik liever uitleggen en bestuderen adhv mijn eigen VM implementatieVerwijderd schreef op zaterdag 08 november 2008 @ 19:18:
Brainfuck is in principe in iedere taal te maken die direct een byte array aan kan spreken. Maar waarom zou je dat willen?
[ Voor 37% gewijzigd door prototype op 08-11-2008 20:31 ]
Verwijderd
OK, prima reden. Maar ook een reden om helemaal vanaf 0 te starten terwijl je ook al de VMs van Java, .NET/Mono, LLVM, etc. hebt?prototype schreef op zaterdag 08 november 2008 @ 20:22:
De huidige VM van Ruby 1.9 laat nog echt veel te veel te wensen over en het is daarom om deze reden dat een zeker bedrijf me hierover heeft gecontact.
Ik kan wel degelijk voorbij Brainfuck zelf kijken, en vond jouw blog erg interessant, maar gaf alleen aan dat je dat in elke compiler die byte arrays aankan kunt doen. C/C++, C#, VB.NET, Delphi, noem maar op...Als je dat artikel bekijkt beschrijf ik een 3-stage compiler, van handmatig een lexer bouwen, parser bouwen en treewalker. Ik beschrijf daarbij hoe je zowel een interpreter kan bouwen evenals een target code emitter. Het is jammer dat je niet voorbij brainfuck zelf kan kijken, maar wat ik hier beschrijf zijn de general stages van compilation/interpretation die je in modern day compilers aantreft.
Voor mij persoonlijk is het ook gewoon voor thuis. Ik ben een gamedeveloper en heb helemaal geen ambities me professioneel met language development bezig te houden. Ik vind het echter wel een interessant onderwerp en daarom houd ik me ermee bezig in een hobbysfeer. Om diezelfde reden lees ik ook boeken over oa quantum mechanica en string theory e.d.. Het mooie van dit project is dan echter dat ik er ook nog praktisch mee bezig kan zijn, wat bij natuurkunde wat lastiger wordt. En als er iets serieus uit voortvloeit dan is dat wel cool maar dat is allerminst m'n primaire doel - want dat is gewoon "doing the things I like to do". Persoonlijke ontwikkeling komt daarbij op de tweede plek, maar zelfs dat is dus niet de primaire redenVerwijderd schreef op zaterdag 08 november 2008 @ 15:45:
Worteltaart, volledig mee eens, maar ze zijn nu tijd en kennis aan het investeren in een nieuw project terwijl dat m.i. zinvoller is in bv. Ruby of Python.
'Because you can' is leuk voor thuis.
Overigens gaat momenteel al mijn aandacht uit naar de laatste GoT programming contest
[ Voor 12% gewijzigd door .oisyn op 08-11-2008 22:46 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Doe jij eens zoeken op LLVM en prototype in dit topicVerwijderd schreef op zaterdag 08 november 2008 @ 21:23:
[...]
OK, prima reden. Maar ook een reden om helemaal vanaf 0 te starten terwijl je ook al de VMs van Java, .NET/Mono, LLVM, etc. hebt?
Ik zou eerder geneigd zijn om over "een lint/tape" te spreken dan "byte arrays"Ik kan wel degelijk voorbij Brainfuck zelf kijken, en vond jouw blog erg interessant, maar gaf alleen aan dat je dat in elke compiler die byte arrays aankan kunt doen. C/C++, C#, VB.NET, Delphi, noem maar op...
[ Voor 47% gewijzigd door prototype op 08-11-2008 23:56 ]
Even een kleine bump. Enkele weken geleden ben ik bezig geweest met het bestuderen van LLVM en alhoewel iedereen er wel wat lovends over te zeggen heeft, heb ik er eigenlijk steeds minder lovende dingen over te zeggen. Met name de documentatie en compleetheid van code, waarvan de eerste zwaar achterhaald is ten opzichte van het laatste. Het is me nu al vele keren overkomen dat bij het spelen van LLVM aan de hand van documentaties ik gewoon regelrechte leugens achterhaal over beweringen van dingen die geimplementeerd zijn die in werkelijkheid __niet__ geimplementeerd zijn. Dit heeft bij mij geleid tot het alombekende de code is de documentatie syndroom wat mij geenszins bevalt...
Met dit in het achterhoofd ben ik vorige week begonnen met de eerste paar AST klassen te schrijven voor dit project, voorlopig zonder LLVM. Nu echter Parrot 1.0 ook gereleased is is het misschien grappig om hier ook naar te kijken. Punt is alleen dat ik stiekem de VM eigenlijk wel van scratch zou willen implementeren om een betere en diepere kennis te krijgen over dit onderwerp.
Vrije tijd heb ik niet veel, maar ik probeer er zoveel mogelijk van wat ik heb in dit project in te stoppen en ook verslag te doen van wat er momenteel gedaan wordt: leuk voor mensen om te lezen denk ik die zich ook altijd al bezig hebben willen houden met het bouwen van een programmeertaal van scratch en het helpt denk ik eventueel ook bij het discussieren van language features en hoe deze geimplementeerd dienen te worden. Hierbij kan ik niet op elk punt ingaan omdat dat meer tijd zou kosten om te bespreken dan om iets geimplementeerd te krijgen. Bij deze zou ik dan ook eventueel een informele oproep willen doen aan mensen die graag mee zouden willen helpen: misschien niet geheel verassend, maar daarbij vraag ik voor de "core" op z'n minst een redelijk goede kennis van formele talen/compiler construction, "algoritmen/datastructuren en complexiteit", source version control en C++. Zelf ben ik geen expert in allen van deze (ik ben pas 23 ;-)) , en het is dan ook dat ik dit beschouw als een leerzame ervaring waarbij we van elkaar het e.e.a. zouden kunnen leren.
Mijn streven is vooralsnog een Ruby/Smalltalk/Python verpakt in PHP/java achtige syntax (het curly brace syndroom ;-)) daar ik de eerstgenoemde talen gewoon praktisch familie van elkaar beschouw en superieur ook in featureset tov laatstgenoemde
. Dit betekent dus in eerste instantie een hybride strong- en weaktyped dynamische taal: typehinten zoals dat in PHP b.v. mogelijk is zal ook hier toegestaan moeten zijn waarbij we dan zelfs nog statische optimalisaties zouden kunnen doen. Je zou het echte doel dan ook misschien kunnen beschouwen als PHP opnieuw ontwerpen zonder rekening te hoeven houden met backwards compatibility en in dit geval dus ook de inconsistenties weg kunnen halen. Achterlijke dingen zoals bij lambda's nog aan moeten geven aan wat voor scope hij moet binden moeten daarbij ook "oprotten" ;-)
Additionele dingen zoals Ahead of time compilation evenals just in time compilatie vind ik ook interessant om naar te kijken, zeker als blijkt dat tegen de tijd dat we hier eventueel aan toe zouden komen LLVM ook gewoon in orde is, die biedt namelijk faciliteiten voor deze dingen. Of we het afkrijgen weet ik niet, maar leerzaam zal het zeker moeten zijn denk ik. Dus laat ik maar eerst eens vragen of er een paar knappe koppen hier zijn die hier eventueel aan mee zouden willen werken. Uiteindelijk hoop ik namelijk op dit of een gerelateerd onderwerp af te kunnen studeren :-)
Met dit in het achterhoofd ben ik vorige week begonnen met de eerste paar AST klassen te schrijven voor dit project, voorlopig zonder LLVM. Nu echter Parrot 1.0 ook gereleased is is het misschien grappig om hier ook naar te kijken. Punt is alleen dat ik stiekem de VM eigenlijk wel van scratch zou willen implementeren om een betere en diepere kennis te krijgen over dit onderwerp.
Vrije tijd heb ik niet veel, maar ik probeer er zoveel mogelijk van wat ik heb in dit project in te stoppen en ook verslag te doen van wat er momenteel gedaan wordt: leuk voor mensen om te lezen denk ik die zich ook altijd al bezig hebben willen houden met het bouwen van een programmeertaal van scratch en het helpt denk ik eventueel ook bij het discussieren van language features en hoe deze geimplementeerd dienen te worden. Hierbij kan ik niet op elk punt ingaan omdat dat meer tijd zou kosten om te bespreken dan om iets geimplementeerd te krijgen. Bij deze zou ik dan ook eventueel een informele oproep willen doen aan mensen die graag mee zouden willen helpen: misschien niet geheel verassend, maar daarbij vraag ik voor de "core" op z'n minst een redelijk goede kennis van formele talen/compiler construction, "algoritmen/datastructuren en complexiteit", source version control en C++. Zelf ben ik geen expert in allen van deze (ik ben pas 23 ;-)) , en het is dan ook dat ik dit beschouw als een leerzame ervaring waarbij we van elkaar het e.e.a. zouden kunnen leren.
Mijn streven is vooralsnog een Ruby/Smalltalk/Python verpakt in PHP/java achtige syntax (het curly brace syndroom ;-)) daar ik de eerstgenoemde talen gewoon praktisch familie van elkaar beschouw en superieur ook in featureset tov laatstgenoemde
Additionele dingen zoals Ahead of time compilation evenals just in time compilatie vind ik ook interessant om naar te kijken, zeker als blijkt dat tegen de tijd dat we hier eventueel aan toe zouden komen LLVM ook gewoon in orde is, die biedt namelijk faciliteiten voor deze dingen. Of we het afkrijgen weet ik niet, maar leerzaam zal het zeker moeten zijn denk ik. Dus laat ik maar eerst eens vragen of er een paar knappe koppen hier zijn die hier eventueel aan mee zouden willen werken. Uiteindelijk hoop ik namelijk op dit of een gerelateerd onderwerp af te kunnen studeren :-)
hoe dynamisch word de taal trouwens? Bij sommige talen kan je je eigen infix operators toevoegen, wat ik echt een immens handige feature vind:
Geweldige toevoeging voor een taal IMO
Zullen vast nog wel meer talen zijn die dit bieden, maar ik ben het nog niet vaak tegen gekomen
code:
1
2
| and := (a,b) => {a? {b? true : false} : false}; infix 2 left _&&_ => and; |
Geweldige toevoeging voor een taal IMO
Zullen vast nog wel meer talen zijn die dit bieden, maar ik ben het nog niet vaak tegen gekomen
[ Voor 14% gewijzigd door link0007 op 29-03-2009 16:09 ]
IF IF = THEN THEN THEN = ELSE ELSE ELSE = IF;
Is rechtsassociatief niet zinniger (vanwege evt. short circuiting indien de argumenten niet strict geevalueerd worden)?link0007 schreef op zondag 29 maart 2009 @ 16:06:
code:
1 2 and := (a,b) => {a? {b? true : false} : false}; infix 2 left _&&_ => and;
De Haskell definitie:
Haskell:
1
2
3
| infixr 3 && True && x = x False && _ = False |
Ipsa Scientia Potestas Est
NNID: ShinNoNoir
Maakt eigenlijk geen zak uit, of a && b && c nou geparsed wordt als a && (b && c) of (a && b) && c, in alle twee de gevallen wordt eerst a geëvalueerd, dan b, dan c.
.edit: hoewel het wel zo is dat als a false is, 'false && c' nog een extra aanroep van de operator is, terwijl hij bij 'false && (b && c)' al direct kan stoppen. Maar in gegenereerde code zullen de twee manieren identiek zijn.
.edit: hoewel het wel zo is dat als a false is, 'false && c' nog een extra aanroep van de operator is, terwijl hij bij 'false && (b && c)' al direct kan stoppen. Maar in gegenereerde code zullen de twee manieren identiek zijn.
[ Voor 41% gewijzigd door .oisyn op 29-03-2009 17:03 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Aangezien alles een object zal worden, kun je de infix operator die jij hier voorstelt gewoon zien als een methode die je aanroept op 't object aan de linkerkant van jouw "infix operator". Als argument krijgt het dan het "rechtergedeelte". Hier zou nog eventueel syntactisch suiker voor geboden kunnen worden, e.g. voo rmethoden die aangeduidt worden als zijnde operatoren, is het toegestaan dan om de record notation dot en eventueel de haakjes weg te laten. Moeten we alleen nog even heel goed over nadenken of dat wenselijk is. Verder hoef je je geen zorgen te maken over evaluatie van dit soort expressies, dat zal gewoon shortcircuited gebruiken by default.link0007 schreef op zondag 29 maart 2009 @ 16:06:
hoe dynamisch word de taal trouwens? Bij sommige talen kan je je eigen infix operators toevoegen, wat ik echt een immens handige feature vind:
code:
1 2 and := (a,b) => {a? {b? true : false} : false}; infix 2 left _&&_ => and;
Geweldige toevoeging voor een taal IMO
Zullen vast nog wel meer talen zijn die dit bieden, maar ik ben het nog niet vaak tegen gekomen
Ondersteun je de bekende cast expressions? En zo ja, hoe?
Ze zijn namelijk zo ambigu als de pest als je nog niet weet of een identifier een type aangeeft.
(a)+b
Is dat een optelling, of de expressie +b gecast naar een a. In C++ kom je nog weg door type info tijdens het parsen terug te voeren aan de lexer, zodat je voor types andere productieregels kunt gebruiken. Als je wilt dat je types kunt gebruiken voor je ze gedefinieerd hebt kan dat niet meer, en zul je een superset van de grammatica moeten verzinnen, waarna je in de AST gaat kijken wat er daadwerkelijk bedoeld wordt (en daarbij mogelijk nog parse errors kunt geven omdat je gebruikte grammatica meer toestaat dan de taal zelf). Maar zo'n supergrammatica verzinnen is al een moeilijkheid op zich. Je zou bijv. de productieregel '(' expression ')' expression kunnen gebruiken, zodat (a) een expression is en +b ook, maar dat conflicteert weer met de binaire + operator.
(a)+b
Is dat een optelling, of de expressie +b gecast naar een a. In C++ kom je nog weg door type info tijdens het parsen terug te voeren aan de lexer, zodat je voor types andere productieregels kunt gebruiken. Als je wilt dat je types kunt gebruiken voor je ze gedefinieerd hebt kan dat niet meer, en zul je een superset van de grammatica moeten verzinnen, waarna je in de AST gaat kijken wat er daadwerkelijk bedoeld wordt (en daarbij mogelijk nog parse errors kunt geven omdat je gebruikte grammatica meer toestaat dan de taal zelf). Maar zo'n supergrammatica verzinnen is al een moeilijkheid op zich. Je zou bijv. de productieregel '(' expression ')' expression kunnen gebruiken, zodat (a) een expression is en +b ook, maar dat conflicteert weer met de binaire + operator.
[ Voor 15% gewijzigd door .oisyn op 29-03-2009 22:08 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Is inderdaad buitengewoon interessante materieprototype schreef op zaterdag 28 maart 2009 @ 17:42:
Punt is alleen dat ik stiekem de VM eigenlijk wel van scratch zou willen implementeren om een betere en diepere kennis te krijgen over dit onderwerp.
* unclero is stiekem ook al een tijdje bezig met zijn eigen VM'tje
Wellicht tijd voor een "Show je programmeertaal" topic?
Quelle chimère est-ce donc que l'homme? Quelle nouveauté, quel monstre, quel chaos, quel sujet de contradiction, quel prodige!
FWIW, in Java werkt het zo:.oisyn schreef op zondag 29 maart 2009 @ 20:42:
Ondersteun je de bekende cast expressions? En zo ja, hoe?Ze zijn namelijk zo ambigu als de pest als je nog niet weet of een identifier een type aangeeft.
code:
1
2
3
| CastExpression: (PrimitiveType Dimsopt) UnaryExpression (ReferenceType) UnaryExpressionNotPlusMinus |
En voor C#.
Volgens beiden is (a)+b alleen een cast als a een primitive/predefined type is. Dat lijkt mij ook de enige mooie oplossing voor een dynamische taal: het is goed gedefinieerd en je hoeft niet at runtime nog eens te kijken wat de bedoeling is...From the disambiguation rule it follows that, if x and y are identifiers, (x)y, (x)(y), and (x)(-y) are cast-expressions, but (x)-y is not, even if x identifies a type. However, if x is a keyword that identifies a predefined type (such as int), then all four forms are cast-expressions (because such a keyword could not possibly be an expression by itself).
Hmm ok interessant
. Ik ben momenteel aan het spelen met Coco/R, daarmee kun je custom predicates voor productieregels schrijven (die bijvoorbeeld de tokenstream uitlezen om te kijken wat er komen gaat), waardoor hij feitelijk LL(inf) wordt ipv de LL(1) die hij normaal is. M'n huidige implementatie kijkt of er alleen tokens voorkomen die in een type mogen zitten, maar dat is niet genoeg - dan zal hij een expressie als (a, b)+c ook als cast zien. Wat ik nu probeer te doen is de parser iets aan te passen zodat hij simpelweg probeert of het gedeelte tussen haakjes is te parsen als type en zo niet te backtracken (wat het effectief een GLL parser maakt).
En m'n regel is dus: als het te parsen is als een type, dan is het een cast expression. Maar ik heb er even niet bij stilgestaan dat je op die manier nooit meer een optelling of aftrekking kan schrijven. Ik denk dat ik de regel uit Java en C# dus maar overneem
En m'n regel is dus: als het te parsen is als een type, dan is het een cast expression. Maar ik heb er even niet bij stilgestaan dat je op die manier nooit meer een optelling of aftrekking kan schrijven. Ik denk dat ik de regel uit Java en C# dus maar overneem
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Ik ben momenteel bezig met me in te lezen in Parser combinatoren omdat een van de dingen waar ik me altijd aan ergerde bij het specificeren van een taal was dat ik het niet goed kon opdelen in kleinere beter onderhoudbare stukken qua grammatica. Bij deze dus even kijken naar of ik kleinere parser programma's kan combineren en zo wat meer flexibiliteit kan krijgen misschien: ook zou het schijnbaar ambigue taal parsen eenvoudiger moeten maken en daar ben ik eigenlijk ook wel in geinteresseerd omwille van een paar taal constructies. Misschien niet de beste redenen om dit te bestuderen, maar misschien komt er nog wat interessants uit. Een wat uitgebreidere verslaggeving volgt dan ook snel als RayNbow al niet ondertussen verteld heeft hoe gaaf 't in haskell is ;-)
[ Voor 14% gewijzigd door prototype op 30-03-2009 22:23 ]
Als je werkt met list of successses, dan kan je op sommige punten tijdens het parsen wat ambiguiteit tegengaan (denk bijv. aan de problemen in LR(k) met shift/reduce conflicten). Je houdt in feite gewoon alle mogelijke parses tot nu toe bij en soms vallen er een paar mogelijkheden af en soms komen er paar mogelijkheden erbij.prototype schreef op maandag 30 maart 2009 @ 22:21:
[...] ook zou het schijnbaar ambigue taal parsen eenvoudiger moeten maken en daar ben ik eigenlijk ook wel in geinteresseerd omwille van een paar taal constructies.
Als echter de gehele grammatica ambigu is, dan heb je na afloop van het parsen dus niet 1 enkele mogelijke parse. De vraag is of dit wenselijk is.
(Aan welke taalconstructies zat je trouwens te denken?)
Het is gewoon gaaf in Haskell vanwege de lichte syntax om dingen aan elkaar te knopen.als RayNbow al niet ondertussen verteld heeft hoe gaaf 't in haskell is ;-)
Ipsa Scientia Potestas Est
NNID: ShinNoNoir
Dat doen de generalized parsers dus. Bij ambiguïteiten parsen ze gewoon alle mogelijkheden. Tijdens of na het parsen kun je vervolgens de conflicts resolven adhv een semantische analyse van de rest van het programma.RayNbow schreef op maandag 30 maart 2009 @ 23:19:
Als echter de gehele grammatica ambigu is, dan heb je na afloop van het parsen dus niet 1 enkele mogelijke parse. De vraag is of dit wenselijk is.
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Ik ben nog niet zo bekend met generalized parsers, maar ik wel o.a. nog Scannerless Generalized-LR Parsing lezen..oisyn schreef op maandag 30 maart 2009 @ 23:23:
[...]
Dat doen de generalized parsers dus. Bij ambiguïteiten parsen ze gewoon alle mogelijkheden. Tijdens of na het parsen kun je vervolgens de conflicts resolven adhv een semantische analyse van de rest van het programma.
Vieze dubbelposter.oisyn schreef op maandag 30 maart 2009 @ 23:30:
Kunnen we niet beter gewoon eerst een eigen parser generator maken?
Ipsa Scientia Potestas Est
NNID: ShinNoNoir
Parse error at line 1
Ik heb de Coco/R parser generator nu zo gehakt dat je tussentijds de parser state op kunt slaan en de error handler wordt vervangen door een dummy hander die eigenlijk alleen bijhoudt of er een error is opgetreden. Aangezien hij een recursive descent parser genereert kun je daarna gewoon de functie aanroepen van de non-terminal die je wilt parsen, zodat je daarna kunt checken of het gelukt is. Dit geheel kun je weer gebruiken in een resolve predicate zodat ik nu goed uit kan vinden of de reeks tokens die komen gaan te parsen zijn als een cast expression. Omslachtig (vooral omdat er nu eigenlijk dubbel geparsed wordt, eerst om te kijken of het lukt, zo ja, het nog een keer doen voor het eggie), maar het werkt wel
M'n grammatica ziet er dan alsvolgt uit (dit is nog een heel erg simpele WIP, hij doet alleen binary en unary '+', haakjes en casts, maar het was dan ook vooral om uit te zoeken hoe ik het moest gaan resolven)
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| // wat parser support code bool IsCastExpression() { TryParse tryer(this); CastExpression(); return tryer; } // in de grammer definition Expression = AddExpression . AddExpression = AddTerm { "+" AddTerm } . AddTerm = "+" AddTerm | CastTerm . CastExpression = "(" Type ")" CastTerm . CastTerm = IF(IsCastExpression()) CastExpression | "(" Expression ")" | Literal . Literal = ident | number | string . |
Ter info, Coco/R rebruikt een EBNF notatie waarbij [A] staat voor een optionele A en {A} voor 0 of meer A's
.edit: dit klopt nog niet helemaal, nu werkt (a)(b)c niet (een dubbele cast)
.edit2: zo, dit klopt meer. Let niet meer op m'n naamgeving trouwens
[ Voor 113% gewijzigd door .oisyn op 31-03-2009 02:59 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Kijk eens een keertje naar ASF+SDF.RayNbow schreef op maandag 30 maart 2009 @ 23:55:
[...]
Ik ben nog niet zo bekend met generalized parsers, maar ik wel o.a. nog Scannerless Generalized-LR Parsing lezen.
Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info
Ik heb een klein beetje naar SDF gekeken, maar dat was omdat ik bezig was met Stratego/XT voor een practicum Program Transformation & Generation.
Ipsa Scientia Potestas Est
NNID: ShinNoNoir
Wat ik wel grappig vind aan dit topic is dat het lijkt alsof er aan het grotere probleem voorbij wordt gegaan:
Waarom een nieuwe taal? En als je die vraag al beantwoord, welke paradigm past dan het beste?
Het probleem bij hedendaagse talen en VMs is volgens mij namelijk niet de feature-set. Als ik kijk naar Java, PHP en C# dan zie ik een zeer rijke set aan functionaliteit die uitgebreid worden met libraries. Hetzelfde geldt voor Ruby en Python(om maar even over te stappen naar Dynamic typing).
Het probleem waar hedendaagse language designers mee lijken te zitten lijkt toch echt hoe je uit te drukken: imperatief of declaratief en hoe blijf je productief? Daarbij: hoe ga je deze combi vertalen naar iets wat schaalbaar is? Je wilt immers niet in de age of paralellism blijven nadenken over de exacte hardware mapping van één specifieke thread.
Persoonlijk zie ik veel in een combinatie tussen een imperatieve definitie taal voor objecten en een declaratieve manier van het code schrijven(de logic).
Ik denk dat de gedachtengang belangrijker is dan welk parsing algoritme dan ook.
Waarom een nieuwe taal? En als je die vraag al beantwoord, welke paradigm past dan het beste?
Het probleem bij hedendaagse talen en VMs is volgens mij namelijk niet de feature-set. Als ik kijk naar Java, PHP en C# dan zie ik een zeer rijke set aan functionaliteit die uitgebreid worden met libraries. Hetzelfde geldt voor Ruby en Python(om maar even over te stappen naar Dynamic typing).
Het probleem waar hedendaagse language designers mee lijken te zitten lijkt toch echt hoe je uit te drukken: imperatief of declaratief en hoe blijf je productief? Daarbij: hoe ga je deze combi vertalen naar iets wat schaalbaar is? Je wilt immers niet in de age of paralellism blijven nadenken over de exacte hardware mapping van één specifieke thread.
Persoonlijk zie ik veel in een combinatie tussen een imperatieve definitie taal voor objecten en een declaratieve manier van het code schrijven(de logic).
Ik denk dat de gedachtengang belangrijker is dan welk parsing algoritme dan ook.
Deze post is bestemd voor hen die een tegenwoordige tijd kunnen onderscheiden van een toekomstige halfvoorwaardelijke bepaalde subinverte plagiale aanvoegend intentioneel verleden tijd.
- Giphart
Maar er kleeft wel een nadeel aan grammatica's als LL en LALR: ze zijn namelijk niet gesloten onder compositie.Alex schreef op dinsdag 31 maart 2009 @ 10:03:
Ik denk dat de gedachtengang belangrijker is dan welk parsing algoritme dan ook.
SGLR grammatica's daarentegen zijn wel gesloten onder compositie. Dit is handig als je andere talen wilt embedden in een bestaande taal, bijv. SWUL in Java.
Ipsa Scientia Potestas Est
NNID: ShinNoNoir
Je schetst hier precies wat ik niet uitdrukteRayNbow schreef op dinsdag 31 maart 2009 @ 10:46:
[...]
Maar er kleeft wel een nadeel aan grammatica's als LL en LALR: ze zijn namelijk niet gesloten onder compositie.
SGLR grammatica's daarentegen zijn wel gesloten onder compositie. Dit is handig als je andere talen wilt embedden in een bestaande taal, bijv. SWUL in Java.
Als je voorbij deze globale gedacht bent, heb je de technische requirements voor je parsing algoritme.
Deze post is bestemd voor hen die een tegenwoordige tijd kunnen onderscheiden van een toekomstige halfvoorwaardelijke bepaalde subinverte plagiale aanvoegend intentioneel verleden tijd.
- Giphart
Heb je het begin van dit afgeplitste topic wel gelezen
Ace of Base vs Charli XCX - All That She Boom Claps (RMT) | Clean Bandit vs Galantis - I'd Rather Be You (RMT)
You've moved up on my notch-list. You have 1 notch
I have a black belt in Kung Flu.
Waarom gaan we niet met z'n allen in afkortingen communiceren? Leest zo lekker weg: IMHO eh, google.... i.m.h.o. ..... oh ja, in my humble opinion, en dan heb je uiteraard ook nog IMNSHO..... wie bedenkt dat toch... AFAIK..???? A- wat?? ach, weer zo een...
Waar is die vent die programmeren wil, het wiel opnieuw wilt uitvinden? Laat hem of haar in afkortingen programmeren, kijken hoe snel ze gek worden...
Tja, een beetje boos ben ik wel, we hebben toch allemaal netjes leren lezen en schrijven op school? Kijk dan alsjeblieft iets verder, als je een afkorting gebruikt, zet dan tenminste 1 keer de betekenis neer, dat leest verdomde makkelijker.
En nee, ik ben er niet op tegen, het gebruik ervan, maar meer de laksheid die op fora's wordt gehandhaaft en het gebrek aan netjes Nederlands te schrijven of het nalezen ervan. Er worden veel typ- en spelfouten gemaakt, laten we dan ook niet nog eens beginnen met afkortingen te kust en te keur te gebruiken. We hebben het al moeilijk genoeg!
Waar is die vent die programmeren wil, het wiel opnieuw wilt uitvinden? Laat hem of haar in afkortingen programmeren, kijken hoe snel ze gek worden...
Tja, een beetje boos ben ik wel, we hebben toch allemaal netjes leren lezen en schrijven op school? Kijk dan alsjeblieft iets verder, als je een afkorting gebruikt, zet dan tenminste 1 keer de betekenis neer, dat leest verdomde makkelijker.
En nee, ik ben er niet op tegen, het gebruik ervan, maar meer de laksheid die op fora's wordt gehandhaaft en het gebrek aan netjes Nederlands te schrijven of het nalezen ervan. Er worden veel typ- en spelfouten gemaakt, laten we dan ook niet nog eens beginnen met afkortingen te kust en te keur te gebruiken. We hebben het al moeilijk genoeg!
Ja en daar haal ik wel een 2 aanleidingen uit:BtM909 schreef op dinsdag 31 maart 2009 @ 11:10:
Heb je het begin van dit afgeplitste topic wel gelezen
- Leerdoel VM programmeren
- Taal PHP heeft wat beperkingen
Mag ik dit nihil vinden om nog direct met een nieuwe taal te komen?
Deze post is bestemd voor hen die een tegenwoordige tijd kunnen onderscheiden van een toekomstige halfvoorwaardelijke bepaalde subinverte plagiale aanvoegend intentioneel verleden tijd.
- Giphart
EensAlex schreef op dinsdag 31 maart 2009 @ 10:03:
Wat ik wel grappig vind aan dit topic is dat het lijkt alsof er aan het grotere probleem voorbij wordt gegaan:
Waarom een nieuwe taal? En als je die vraag al beantwoord, welke paradigm past dan het beste?
...
(ik weet ook niet waar EBNF voor staat, "uh-feek"
[ Voor 4% gewijzigd door Zoijar op 31-03-2009 13:01 ]
Wat doe je in een topic als dit als je termen als EBNF, LL, LALR, SGLR niet kent? En wie kent in godsnaam standaard afkortingen als imho en afaik niet? Serieus, onder welke steen heb jij gelegen?avandel schreef op dinsdag 31 maart 2009 @ 11:24:
Tja, een beetje boos ben ik wel, we hebben toch allemaal netjes leren lezen en schrijven op school? Kijk dan alsjeblieft iets verder, als je een afkorting gebruikt, zet dan tenminste 1 keer de betekenis neer, dat leest verdomde makkelijker.
Over zeuren gesproken, ik weet bijna zeker dat Word gaat miepen over "En nee, ik ben er niet op tegen, het gebruik ervan, maar meer de laksheid ...". Iets in de trant van "Fragment, consider revising".
[ Voor 18% gewijzigd door Grijze Vos op 31-03-2009 12:40 ]
Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info
Elke keer 1 keer de betekenis erbij zetten terwijl vrijwel iedereen hier op het forum, behalve jij blijkbaar, weet wat ermee bedoeld wordt? Afkortingen zijn juist handig. Het verminderd ruis zodat je je meer kunt concentreren op de daadwerkelijke inhoud.avandel schreef op dinsdag 31 maart 2009 @ 11:24:
Kijk dan alsjeblieft iets verder, als je een afkorting gebruikt, zet dan tenminste 1 keer de betekenis neer, dat leest verdomde makkelijker.
En wat de hier gebezigde wetenschappelijke termen betreft, het is niet dat als we het over extended Backus-Naur form hebben dat je dan wel ineens snapt wat ermee bedoelt wordt. En degenen die het wel snappen kennen de afkorting ook. Compleet nutteloos om dat helemaal uit te typen dus.
Dus, samenvattend, IIRC kent zo'n beetje elke GoTer wel al die afkortingen en AFAIK heeft zelden daar iemand problemen mee, dus IMHO ben je gewoon een beetje aan het zuren, ITT de anderen in de topic die IIG wel een relevante bijdrage leveren DMV constructieve posts MBV afkortingen ICM bekende termen
@hieronder: shut up, I'm trying to make a point here
[ Voor 4% gewijzigd door .oisyn op 31-03-2009 14:04 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Verwijderd
Moeten dergelijke afkortingen niet met kleine letters?.oisyn schreef op dinsdag 31 maart 2009 @ 13:33:
[...]
Dus, samenvattend, IIRC kent zo'n beetje elke GoTer wel al die afkortingen en AFAIK heeft zelden daar iemand problemen mee, dus IMHO ben je gewoon een beetje aan het zuren, ITT de anderen in de topic die IIG wel een relevante bijdrage leveren DMV constructieve posts MBV afkortingen ICM bekende termen
Ik kom in de problemen met m'n tuples
Ik kan natuurlijk voor een andere syntax gaan, zoals [[a,b]] of <:a,b:> of (.a,b.) (ik noem maar wat). De mogelijkheden zijn eindeloos
. Ik heb denk ik niet zo'n zin om heel moeilijk te gaan doen door te kijken of een > een groter-dan is of een einde-tuple. Momenteel verslikt de parser zich zelfs over <1, 2, 3> omdat hij bij de laatste 3 wil parsen als '3 groter-dan ERROR'
.edit: hmm, ik kan denk het denk ik wel zo krijgen dat een groter-dan nooit die betekenis heeft binnen een tuple, tenzij er haakjes om die expressie staan. <1, 2, 3 > 5> is dus een parse error, dan moet je <1, 2, (3 > 5)> gebruiken.
code:
1
2
| var a = <1, 2, 3>; var b = <1, 2, 3 > 5>; // en nu? |
Ik kan natuurlijk voor een andere syntax gaan, zoals [[a,b]] of <:a,b:> of (.a,b.) (ik noem maar wat). De mogelijkheden zijn eindeloos
.edit: hmm, ik kan denk het denk ik wel zo krijgen dat een groter-dan nooit die betekenis heeft binnen een tuple, tenzij er haakjes om die expressie staan. <1, 2, 3 > 5> is dus een parse error, dan moet je <1, 2, (3 > 5)> gebruiken.
[ Voor 19% gewijzigd door .oisyn op 31-03-2009 18:32 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Verwijderd
Lijkt me ook duidelijker! ik vind <1, 2, 3 > 5> niet echt leesbaar. Ik moet twee keer kijken om 't goed te begrijpen. <1, 2, 3, (3 > 5) > vind ik veel duidelijker..oisyn schreef op dinsdag 31 maart 2009 @ 18:28:
Ik kom in de problemen met m'n tuples
code:
1 2 var a = <1, 2, 3>; var b = <1, 2, 3 > 5>; // en nu?
Ik kan natuurlijk voor een andere syntax gaan, zoals [[a,b]] of <:a,b:> of (.a,b.) (ik noem maar wat). De mogelijkheden zijn eindeloos. Ik heb denk ik niet zo'n zin om heel moeilijk te gaan doen door te kijken of een > een groter-dan is of een einde-tuple. Momenteel verslikt de parser zich zelfs over <1, 2, 3> omdat hij bij de laatste 3 wil parsen als '3 groter-dan ERROR'
.edit: hmm, ik kan denk het denk ik wel zo krijgen dat een groter-dan nooit die betekenis heeft binnen een tuple, tenzij er haakjes om die expressie staan. <1, 2, 3 > 5> is dus een parse error, dan moet je <1, 2, (3 > 5)> gebruiken.
Zoiets is een best lastige taak voor de compiler(bouwer). Welke informatie heeft de compiler nodig om te kunnen bepalen dat iets in parallel kan worden gedraaid? Welke informatie heeft de compiler nodig om te bepalen of het waard is om iets te parallelliseren? Met welke evaluatiestrategie?Zoijar schreef op dinsdag 31 maart 2009 @ 12:32:
Handmatig parallel programmeren is te moeilijk. Dat is bij uitstek iets dat een compiler/taal op zou moeten kunnen lossen.
* RayNbow kan het nu niet laten om een sprongetje naar Haskell te maken...

In Haskell heeft de compiler informatie of een functie puur of side effects heeft. Deze informatie is namelijk beschikbaar in de types. Een functie met arbitraire side effects heeft IO a als result type.**
Verder kan je in Haskell met de primitieve par en pseq combinatoren pure code annoteren om te bepalen wat er eventueel in parallel moet worden uitgevoerd. Met deze combinatoren kun je verder uitgebreidere evaluatiestrategieen creeren, bijvoorbeeld:
Haskell:
1
2
3
| someList `using` parListChunk 64 rnf -- knip de lijst in stukjes van 64 lang en evalueer de chunks in parallel -- (rnf = reduce normal form) |
** Waarschuwing, het is mogelijk om de Haskell compiler/typechecker te bedonderen...

Gebruik op eigen risico
Heb je ook overwogen om (a,b,c) te gebruiken voor de tupelsyntax?.oisyn schreef op dinsdag 31 maart 2009 @ 18:28:
Ik kom in de problemen met m'n tuples
code:
1 2 var a = <1, 2, 3>; var b = <1, 2, 3 > 5>; // en nu?
Ik kan natuurlijk voor een andere syntax gaan, zoals [[a,b]] of <:a,b:> of (.a,b.) (ik noem maar wat).
[ Voor 10% gewijzigd door RayNbow op 31-03-2009 20:00 ]
Ipsa Scientia Potestas Est
NNID: ShinNoNoir
Ja - in een taal als C++ betekent dat namelijk evalueer in volgorde a, b en c, en retourneer c.
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Volgende probleem: m'n function reference syntax.
Ik kan natuurlijk met haakjes gaan werken, dan is bovenstaande b een functie die int[] returnt, en is een array van functies die int returnen zo:
Het punt is alleen dat m'n grammar dan echt heel moeilijk te parsen wordt op verschillende punten. Met name: (a)b. Is dat nou een variabele-definitie, of een cast-expression.
Ik neig ernaar om het return-type naar het midden te verplaatsen, dus zo:
De vraag is dan, hoe wordt een named function definition dan:
Welke vinden jullie mooier?
.edit: ik denk dat ik zelf #2 mooier vindt. Als ik een spatie toevoeg lijkt het ook veel meer op een C-style functie definitie waar toevallig "function:" voor staat.
Daarentegen is #1 makkelijker te parsen.
code:
1
2
| function(int):int a; // een functie die int returnt function(int):int[] b; // een functie die int[] returnt, of een array van functies die int returnen? |
Ik kan natuurlijk met haakjes gaan werken, dan is bovenstaande b een functie die int[] returnt, en is een array van functies die int returnen zo:
code:
1
| (function(int):int)[] a; |
Het punt is alleen dat m'n grammar dan echt heel moeilijk te parsen wordt op verschillende punten. Met name: (a)b. Is dat nou een variabele-definitie, of een cast-expression.
Ik neig ernaar om het return-type naar het midden te verplaatsen, dus zo:
code:
1
2
3
| function:int(int) a; function:int[](int) b; // b returnt int[] function:int(int)[] c; // c is array, c[0] returnt int |
De vraag is dan, hoe wordt een named function definition dan:
code:
1
2
3
4
5
6
7
8
9
10
11
| // #1 function add : int (int a, int b) { return a + b; } // #2 function:int add(int a, int b) { return a + b; } |
Welke vinden jullie mooier?
.edit: ik denk dat ik zelf #2 mooier vindt. Als ik een spatie toevoeg lijkt het ook veel meer op een C-style functie definitie waar toevallig "function:" voor staat.
code:
1
| function: int add(int a, int b) { } |
Daarentegen is #1 makkelijker te parsen.
[ Voor 10% gewijzigd door .oisyn op 01-04-2009 00:09 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
#3
Misschien is het trouwens ook handig om een soort simpel generics/template-systeempje intebouwen, hoewel dat misschien het dynamic-typing wat in de weg zit:
voorbeeldje:
.oisyn, waarom verander je niet gewoon die ideeen voor casting-syntax. Ik vind die haakjes eigenlijk gigantisch irritant.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| function add(a:int,b:int):int { return a + b } addFunc:int->int = add function int_array():[int] { } function func_array():[int->int] { } |
Misschien is het trouwens ook handig om een soort simpel generics/template-systeempje intebouwen, hoewel dat misschien het dynamic-typing wat in de weg zit:
voorbeeldje:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
| fn max[T](a:T,b:T):T { if (a<b) return b return a } fn max[T](a:T,b:T,...):T { return max(max(a,b),...) } output("max: ",max(4,5,3,8,3)); // 8 |
.oisyn, waarom verander je niet gewoon die ideeen voor casting-syntax. Ik vind die haakjes eigenlijk gigantisch irritant.
[ Voor 104% gewijzigd door JeromeB op 01-04-2009 01:04 ]
PC load letter? What the fuck does that mean?
Ik vind een functiestijl cast sowieso lekkerder werken, bv static_cast<int>(x), omdat de haken dan om het stuk dat je wilt casten heen staan. Anders is het altijd zo onduidelijk hoe hard de cast bind... (float) 3 / 4, is dat 0.0 of is dat 0.75 ? Alleen is het een beetje veel typen soms. Misschien iets als: <float>(3) / 4 ?
Inderdaad, dat vind ik in C++ bijvoorbeeld een stuk duidelijker dan de C-style-casts.
Wat is er trouwen mis met constructor-style casts? value:float = float(3)/4
Wat is er trouwen mis met constructor-style casts? value:float = float(3)/4
PC load letter? What the fuck does that mean?
Hoe maak je hieruit op dat addFunc 2 parameters heeft?
[type] gaat niet werken, die gebruik ik al voor array literals: a = [1, 2, 3, 4]. Daarnaast, hoe had jij hashmaps voorgesteld? Ik definieer die nu als int[string] (een hashmap van int values met string keys)code:
1 function int_array():[int]
Kan best, maar het lost vrij weinig op als je dat soms dacht.oisyn, waarom verander je niet gewoon die ideeen voor casting-syntax. Ik vind die haakjes eigenlijk gigantisch irritant.
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Excuses, dat had natuurlijk addFunc:int->int->int moeten zijn. Dit zie je ook veel in ML-afgeleide-talen..oisyn schreef op woensdag 01 april 2009 @ 02:11:
[...]
Hoe maak je hieruit op dat addFunc 2 parameters heeft?
Misschien een domme vraag, maar waarom gaat dat niet werken? Ondanks dat je de [] ook gebruikt voor array-literals zie ik niet waarom je die [] niet zou kunnen gebruiken binnen je type-declaratie..oisyn schreef op woensdag 01 april 2009 @ 02:11:
[...]
[type] gaat niet werken, die gebruik ik al voor array literals: a = [1, 2, 3, 4]. Daarnaast, hoe had jij hashmaps voorgesteld? Ik definieer die nu als int[string] (een hashmap van int values met string keys)
Over hashmaps heb ik verder nog niet nagedacht.
Geen idee, hoe doet men dat in C++?.oisyn schreef op woensdag 01 april 2009 @ 02:11:
[...]
Kan best, maar het lost vrij weinig op als je dat soms dacht. Verder: a(b), Is dat een functie aanroep, of een cast (en doet dat er eigenlijk toe?).
PC load letter? What the fuck does that mean?
Omdat ik C-style variabel definities gebruikJeromeB schreef op woensdag 01 april 2009 @ 03:50:
Ondanks dat je de [] ook gebruikt voor array-literals zie ik niet waarom je die [] niet zou kunnen gebruiken binnen je type-declaratie.
code:
1
2
3
4
| int a; [int] b; [mytype] c; [1]; // oeps, array literal |
Het lost dus geen problemen op, het vervangt ze alleen door nieuwe
code:
1
2
| for (i : [1, 2, 3]) // ... |
Dus dan moet ik die ook gaan aanpassen.
C++ is geen goede indicatie omdat je tijdens het parsen al kunt weten wat types zijn en wat niet. En voor de situatie waarin je dat niet kunt weten, zoals in templates, moet je dan ook het keyword typename gebruiken:Geen idee, hoe doet men dat in C++?
C++:
1
2
3
4
5
| template<class T> void foo() { T::bar(34); // nested type, or static function? Compiler assumes function typename T::bar(34); // now it's a type } |
[ Voor 45% gewijzigd door .oisyn op 01-04-2009 10:53 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Volgens mij niet. Een cast is gewoon een functie van het ene type naar het andere. Maar sommige casts moeten build-in zijn omdat de taal het soms niet op een andere manier kan. Dan nog is het gewoon een build-in functie. In C++ kan je alle pointer casts zo schrijven (uit mn hoofd...):.oisyn schreef op woensdag 01 april 2009 @ 02:11:
Kan best, maar het lost vrij weinig op als je dat soms dacht. Verder: a(b), Is dat een functie aanroep, of een cast (en doet dat er eigenlijk toe?).
template <typename To, typename From>
To cast(From x) {
union {To to, From from} tmp;
tmp.from = x;
return tmp.to;
}
Constructor cast werkt volgens mij ook prima. C# doet dat toch?
Ik weet niet of je van plan was pointers te ondersteunen, maar klassiek kan dat niet als functie of ctor stijl cast omdat de * niet in een normale identifier voor mag komen.
[ Voor 10% gewijzigd door Zoijar op 01-04-2009 11:16 ]
Geen pointers. Overigens heb ik ook de "as" operator geleend uit C# (een dynamic_cast op pointer niveau zeg maar)
Dat is heerlijk om te parsen. Voor harde casts (dynamic_cast met reference) zou ik ook iets als dit kunnen doen:
Ik heb nog even na zitten denken aan die function-style casts, en het punt is dat ik die dingen dan moet gaan zitten resolven in m'n VM, en dus niet puur aan de AST alleen kan zien of iets een cast is of niet.
Het voordeel van een cast keyword is bovendien dat je erop kunt zoeken
Het is trouwens built-in
code:
1
| MyClass a = b as MyClass; |
Dat is heerlijk om te parsen. Voor harde casts (dynamic_cast met reference) zou ik ook iets als dit kunnen doen:
code:
1
2
3
| MyClass a = b -> MyClass // of MyClass a = b cast MyClass |
Ik heb nog even na zitten denken aan die function-style casts, en het punt is dat ik die dingen dan moet gaan zitten resolven in m'n VM, en dus niet puur aan de AST alleen kan zien of iets een cast is of niet.
Het voordeel van een cast keyword is bovendien dat je erop kunt zoeken
Het is trouwens built-in
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
ik vind het gebruik van het cast keyword wel een mooie oplossing. De -> notatie zou ook wel kunnen, maar dat levert denk eerder verwarring op.
“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”
Ok, denk er maar over na. Alles behalve die ronde haken C cast.oisyn schreef op woensdag 01 april 2009 @ 12:35:
Geen pointers. Overigens heb ik ook de "as" operator geleend uit C# (een dynamic_cast op pointer niveau zeg maar)
code:
1 MyClass a = b as MyClass;
Dat is heerlijk om te parsen. Voor harde casts (dynamic_cast met reference) zou ik ook iets als dit kunnen doen:
code:
1 2 3 MyClass a = b -> MyClass // of MyClass a = b cast MyClass
Ik heb nog even na zitten denken aan die function-style casts, en het punt is dat ik die dingen dan moet gaan zitten resolven in m'n VM, en dus niet puur aan de AST alleen kan zien of iets een cast is of niet.
Het voordeel van een cast keyword is bovendien dat je erop kunt zoeken
Ja...Het is trouwens built-in

Om het nog moeilijker te maken:
Wat Zoijar aanhaalt is iets waar ik me ook aan stoor: wat is de scope van een C-style cast? Daarom zijn C++ casts ook zoveel cleaner, behalve hun lange naam.
Of nog een voorstel:
Deze kent wel wat obvious nadelen, maar ik wou jullie hem toch niet onthouden...
code:
1
| MyClass a = dyncast(MyClass, b); |
Wat Zoijar aanhaalt is iets waar ik me ook aan stoor: wat is de scope van een C-style cast? Daarom zijn C++ casts ook zoveel cleaner, behalve hun lange naam.
Of nog een voorstel:
code:
1
| MyClass a = MyClass.From(b) |
Deze kent wel wat obvious nadelen, maar ik wou jullie hem toch niet onthouden...
ASSUME makes an ASS out of U and ME
En vervolgens heb je dan ook ?
code:
1
| if a is MyClass {} |
Uiteraard, maar dan met haakjes eromheen zoals vaker het geval is bij if statements
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Zo, ik heb een aantal wijzigingen doorgevoerd. Tuples zijn nu met normale haakjes, de cast gebeurt nu met het cast keyword zoals boven aangegeven, en de functie syntax is nu fn foo(int a, float b) -> (int) { }
Met dank aan JeromeB voor het fn keyword, ik zat er idd over te denken om "function" in te korten naar "f" of "func" oid, maar "fn" vind ik wel een goede
Het volgende parset nu prima
Met dank aan JeromeB voor het fn keyword, ik zat er idd over te denken om "function" in te korten naar "f" of "func" oid, maar "fn" vind ik wel een goede
Het volgende parset nu prima
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| namespace ns { class MyClass : MyInterface { private static int myVar = 34; public fn foo() -> (int, float, string, fn(int)->(int)) { print("hi there"); int[] i = new int[34]; try { for (int j : [1, 2, 3, 4]) alert(j * 3); throw i[34].toString(); } catch(Exception e) { print("Caught exception"); } finally { something(); } return (1, 2 cast float, "hoi", fn(int a)->(int){ return a + 4; }); } } } |
[ Voor 68% gewijzigd door .oisyn op 01-04-2009 18:20 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Heb je het keyword "fn" echt nodig?.oisyn schreef op woensdag 01 april 2009 @ 18:15:
Met dank aan JeromeB voor het fn keyword, ik zat er idd over te denken om "function" in te korten naar "f" of "func" oid, maar "fn" vind ik wel een goede
Het volgende parset nu prima
code:
1 public fn foo() -> (int, float, string, fn(int)->(int))
<access-specifier> <identifier> (<arguments>) -> (<return-types>)
lijkt me uniek. Lijkt me ook te werken als argument type
(<arguments>) -> (<return types>)
En als lambda functie lijkt me ook uniek parseable:
(<arguments>) -> (<return types>) { <implementatie> }
Het parsed waarschijnlijk wel een stuk makkelijker
[ Voor 15% gewijzigd door MLM op 01-04-2009 19:22 ]
-niks-
De argumenten komen na de identifier, na de -> komen de return-values
. Maar op die plek is het idd niet meteen nodig, maar voor lambda's midden in code zeker wel. En houdt er rekening mee dat het een free-form taal is, in de global scope kunnen dus ook gewone statements staan (en dan heb je nog function definitions *in* functies). En dan wordt het ook vrij lastig om een functie-definitie te herkennen.
[ Voor 7% gewijzigd door .oisyn op 01-04-2009 19:20 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Mja, was al aant editten, soz.oisyn schreef op woensdag 01 april 2009 @ 19:20:
De argumenten komen na de identifier, na de -> komen de return-values. Maar op die plek is het idd niet meteen nodig, maar voor lambda's midden in code zeker wel. En houdt er rekening mee dat het een free-form taal is, in de global scope kunnen dus ook gewone statements staan (en dan heb je nog function definitions *in* functies). En dan wordt het ook vrij lastig om een functie-definitie te herkennen.
Waar gebruik je nog meer pijltjes voor dan?
Ik heb een hekel aan overbodige keywords in welke taal dan ook.
Aan de andere kant, misschien word het wel onleesbaarder zonder dat keyword...
[ Voor 10% gewijzigd door MLM op 01-04-2009 19:24 ]
-niks-
feature request:
iteration is niet zozeer mijn requirement, maar is zeker een mooie addition: als ze er is, wordt ze telkens uitgevoerd tot de class gedestruct wordt. Eventueel kun je nog een initialize() en cleanup() erbij gooien die je kan overriden om de functionaliteit in te vullen. Anderzijds kun je de constructor en de destructor deze functionaliteit laten vervullen. Deze 3 (1?) functies zijn optioneel, in de basis moeten ze niet aanwezig zijn,
in dat geval doet de de thread niet meer dan gedispatchete-calls uitvoeren
Waar het vooral om draait is dat een active class een thread associeert met het object (of put uit een threadpool?), die thread runt alle code van de class (incl constructor/desctructor) en alle externe function calls worden automatisch gedispatched op die thread.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| active class Someclass { public fn foo(int a) -> (int) { return a*2; } protected fn iteration() -> (int) { alert(foo(3)); return 10; // time to wait in ms before calling iteration() again } }; SomeClass global; fn runA() -> () { alert(global.foo(10)); } thread a = new thread(runA); a.start(); |
iteration is niet zozeer mijn requirement, maar is zeker een mooie addition: als ze er is, wordt ze telkens uitgevoerd tot de class gedestruct wordt. Eventueel kun je nog een initialize() en cleanup() erbij gooien die je kan overriden om de functionaliteit in te vullen. Anderzijds kun je de constructor en de destructor deze functionaliteit laten vervullen. Deze 3 (1?) functies zijn optioneel, in de basis moeten ze niet aanwezig zijn,
in dat geval doet de de thread niet meer dan gedispatchete-calls uitvoeren
Waar het vooral om draait is dat een active class een thread associeert met het object (of put uit een threadpool?), die thread runt alle code van de class (incl constructor/desctructor) en alle externe function calls worden automatisch gedispatched op die thread.
ASSUME makes an ASS out of U and ME
Die 'active' class me een vrij nutteloze taalfeature, aangezien het ook prima met een library opgelost kan worden. Dan wordt het zoiets:
(Let erop dat een functie die niets returnt ook geen ->() hoeft te hebben
)
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
| class Someclass { private Timer timer; public fn Someclass() { timer = new Timer(10, iteration); } protected fn iteration() { alert(foo(3)); } } |
(Let erop dat een functie die niets returnt ook geen ->() hoeft te hebben
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Zoals ik zelf al aangaf is die iteration() niet zozeer de kern van de zaak.
Het gaat hem over het dispatchen van calls vanuit externe threads naar een andere thread. Dit is in veel talen nu een probleem. Je moet maar eens in C++ een eenvoudig mechanisme te verzinnen waar:
Er automatisch voor zorgt dat de x.foo() vanuit A een rechtstreekse function call is en x.foo() vanuit B een gedispatchete function call. Daar heb je gewoon language support voor nodig. boost::bind of std::bind1st of std::bind2nd kunnen je misschien een klein beetje helpen, maar die gegenereerde types zijn zo complex dat je ervan afziet om het te gaan gebruiken.
In C# heb ik zoiets met delegates, wat al een stuk lekkerder werkt, maar toch moet ik nog steeds generic delegates en reflection gebruiken om te raken waar ik wil raken.
Het gaat hem over het dispatchen van calls vanuit externe threads naar een andere thread. Dit is in veel talen nu een probleem. Je moet maar eens in C++ een eenvoudig mechanisme te verzinnen waar:
C++:
1
2
3
4
5
6
7
| class X; // thread A is owner // thread A x.foo(); // thread B x.foo() |
Er automatisch voor zorgt dat de x.foo() vanuit A een rechtstreekse function call is en x.foo() vanuit B een gedispatchete function call. Daar heb je gewoon language support voor nodig. boost::bind of std::bind1st of std::bind2nd kunnen je misschien een klein beetje helpen, maar die gegenereerde types zijn zo complex dat je ervan afziet om het te gaan gebruiken.
In C# heb ik zoiets met delegates, wat al een stuk lekkerder werkt, maar toch moet ik nog steeds generic delegates en reflection gebruiken om te raken waar ik wil raken.
ASSUME makes an ASS out of U and ME
Wat dan als thread B die call doet en thread A is heel wat anders aan het doen?
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Dan blokkeert threadB netjes tot A die call kan afhandelen.
Voor je erover begint: er zullen vast wel manieren zijn om deadlocks te voorkomen tussen 2 active classes.
Zoals bijvoorbeeld ipv te blokkeren, gedispatchete calls naar de eigen thread te gaan afhandelen en na elk van die calls pollen of de operatie gedaan is. Dit heeft natuurlijk wel enkele nadelen, nl dat de atomiciteit van een operatie verbroken wordt bij elke call naar een andere active class.
anderzijds kun je met een async keyword ala
er voor zorgen dat de call asynchroon uitgevoerd wordt. Welke syntax je eraan geeft is natuurlijk bijzaak hier.
Je kan er dan bvb ook statisch voor zorgen dat er geen synchrone call loops zijn tussen active classes. Doet de gebruiker dit toch, dan klaagt de compiler.
Voor je erover begint: er zullen vast wel manieren zijn om deadlocks te voorkomen tussen 2 active classes.
Zoals bijvoorbeeld ipv te blokkeren, gedispatchete calls naar de eigen thread te gaan afhandelen en na elk van die calls pollen of de operatie gedaan is. Dit heeft natuurlijk wel enkele nadelen, nl dat de atomiciteit van een operatie verbroken wordt bij elke call naar een andere active class.
anderzijds kun je met een async keyword ala
code:
1
2
3
| fn asynchandler(int result) async x.foo() asynchandler |
er voor zorgen dat de call asynchroon uitgevoerd wordt. Welke syntax je eraan geeft is natuurlijk bijzaak hier.
Je kan er dan bvb ook statisch voor zorgen dat er geen synchrone call loops zijn tussen active classes. Doet de gebruiker dit toch, dan klaagt de compiler.
[ Voor 9% gewijzigd door H!GHGuY op 03-04-2009 19:06 ]
ASSUME makes an ASS out of U and ME
Wanneer kan A die call afhandelen dan? Ik bedoel, is het niet gewoon een thread die ook code runt? Of wil je dat ie een soort van (handmatige) message loop doet, wachtend op commando's van andere threads.H!GHGuY schreef op vrijdag 03 april 2009 @ 19:05:
Dan blokkeert threadB netjes tot A die call kan afhandelen.
Klinkt een beetja als C++0x's futures (let wel, dit is niet de final proposal, maar wel het beste artikel dat ik kon vinden met de meeste achtergrond-informatie, in plaats van wat droge bewoording uit de C++ standaardanderzijds kun je met een async keyword ala
code:
1 2 3 fn asynchandler(int result) async x.foo() asynchandler
er voor zorgen dat de call asynchroon uitgevoerd wordt. Welke syntax je eraan geeft is natuurlijk bijzaak hier.
Je kan er dan bvb ook statisch voor zorgen dat er geen synchrone call loops zijn tussen active classes. Doet de gebruiker dit toch, dan klaagt de compiler.
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Juist..oisyn schreef op vrijdag 03 april 2009 @ 23:15:
Wanneer kan A die call afhandelen dan? Ik bedoel, is het niet gewoon een thread die ook code runt? Of wil je dat ie een soort van (handmatige) message loop doet, wachtend op commando's van andere threads.
En eventueel kun je die thread nog exposen dmv die iteration call.
Weeral juistKlinkt een beetja als C++0x's futures (let wel, dit is niet de final proposal, maar wel het beste artikel dat ik kon vinden met de meeste achtergrond-informatie, in plaats van wat droge bewoording uit de C++ standaard)
Het verschil is echter dat je bij futures verdere processing kan doen en op een gegeven moment future<int>.getValue() doet. Bij deze implementatie geef je een handler mee die met het resultaat aangeroepen wordt.
Het gedrag kan je dan ook zo specifieren dat wanneer je 2 active classes hebt en de ene een async call doet naar de andere met een handler uit zijn eigen class:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| active class B { public fn foo() -> (int) { ... } } active class A { public fn asynchandler(int) { // doe iets } public fn foo() { async b.foo() asynchandler } } |
dat de asynchandler dan ook gedispatchet wordt op de thread van A, want deze wordt opgeroepen vanuit thread B.
Je kan dit vergelijken met het doorbreken van een boost::shared_ptr loop dmv het gebruik van een weak_ptr.
Alleen kan je hier statisch en at compile time bepalen of er geen call-loops zijn tussen active classes.
Een andere, en misschien betere, oplossing is dat wanneer active object B een functie oproept van active object A en er een callback is naar active object B (wat dus een deadlock zou opleveren) je die call gewoon synchroon, zonder dispatching uitvoert. thread B is immers aan het wachten op de call van A en er is dus geen gevaar voor thread-unsafe handelingen.
Mocht je dit in C++ willen uitvoeren dan heb je gewoon language support nodig:
- automatische type-generatie voor het dispatchen van de calls
anders moet je zelf per call een struct/class maken die je kan posten in een msgQ, wat teveel werk is.
- Van veel calls (niet alle) kan je statisch afleiden of je zal moeten dispatchen of als je de functie rechtstreeks kan oproepen. Door een call-graph op te stellen kan je per functie bepalen of ze vanuit 1 of meer threads aangeroepen kunnen worden. Eventueel kun je zelfs verschillende versies van een call laten genereren die dan ook andere calls dispatched of juist rechtstreeks aanroept.
- het wrappen van de echte call in een
code:
1
2
3
4
| if (currentThread() == objectAssignedThread) direct_call() else dispatch_call() |
kan door de compiler gedaan worden, inclusief het effectief dispatchen. Mogelijk wordt de stack van de huidige thread gebruikt door de andere thread, enz.
- Een compiler kan heel veel optimalisatiestappen doen die je met library code niet kan doen.
- Parallellisme uitdrukken vereist 1 of 2 keywords: active en eventueel async.
[ Voor 21% gewijzigd door H!GHGuY op 04-04-2009 11:50 ]
ASSUME makes an ASS out of U and ME
Je feature request lijkt me te specifiek. Het helpt verder ook niet voor algemene parallellisatie van code - je runt namelijk nog steeds die calls in 1 specifieke thread. Ik zeg niet dat het niet zinnig is, maar het aantal usecases is imho te klein om daar een taal omheen te ontwerpen. Ik zie meer heil in taalsupport voor dingen als transactional memory e.d.
Anyway, even een update over Proton. Ik heb momenteel een parser die van het geheel een abstract syntax tree maakt, met een bijbehorende visitor interface. Next up is semantic checking (en het bouwen van een type library) en daarna bytecode generation
Anyway, even een update over Proton. Ik heb momenteel een parser die van het geheel een abstract syntax tree maakt, met een bijbehorende visitor interface. Next up is semantic checking (en het bouwen van een type library) en daarna bytecode generation
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Was er al sprake geweest van iets van generics of template-achtige constructies?
Wat ik namelijk wel een handige feature zou vinden is een soort generic typing voor exceptions. Wat eigenlijk belachelijk is in heel veel talen, is dat je in de meeste talen voor elk type exception een nieuwe class aan moet maken, alleen maar om daar in een catch onderscheid op te kunnen maken. Logisch, zou je zeggen, want een class is nu eenmaal een type. Maar in heel veel gevallen is de enige reden dat je een nieuwe exception wilt helemaal niet dat die exception zo anders is, maar dat je anders geen manier hebt om in een catch() onderscheid te maken t.o.v. het meer generieke type exception.
Even afgezien van de syntax dacht ik aan zoiets. Oud:
nieuw:
Wat ik namelijk wel een handige feature zou vinden is een soort generic typing voor exceptions. Wat eigenlijk belachelijk is in heel veel talen, is dat je in de meeste talen voor elk type exception een nieuwe class aan moet maken, alleen maar om daar in een catch onderscheid op te kunnen maken. Logisch, zou je zeggen, want een class is nu eenmaal een type. Maar in heel veel gevallen is de enige reden dat je een nieuwe exception wilt helemaal niet dat die exception zo anders is, maar dat je anders geen manier hebt om in een catch() onderscheid te maken t.o.v. het meer generieke type exception.
Even afgezien van de syntax dacht ik aan zoiets. Oud:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| class MyException extends Exception {} class MyMoreSpecificException extends MyException {} function something () { throw new MyMoreSpecificException ("Message"); } try { something(); } catch ( MyMoreSpecificException specific ) { // ... } catch ( MyException generic ) { // ... } |
nieuw:
code:
Ik moet toegeven dat ik er niet heel goed over nagedacht heb, maar daar is dit topic voor, toch? 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| exception MyException<const string type> { const string TYPE_A = "A"; const string TYPE_B = "B"; } fn something () { throw MyException<MyException.TYPE_A> ( "Message" ); } try { something (); } catch (MyException<MyException.TYPE_B> specific) { } catch (MyException generic) { } |
Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz
Is 't echt zo'n probleem? Je twee voorbeelden verschillen niet heel veel van elkaar in aantal regels code namelijk
. Maar dat is waarschijnlijk ook omdat je geen constructors in de exceptions hebt gedefinieerd, en volgens mij is dat precies waar het probleem ligt. Het blijft niet bij gewoon een "class B extends A { }" tikken. Maar zou het dan niet een betere oplossing zijn als je het wel daarbij kon laten, en nog steeds B kon constructen met argumenten uit de ctor van A, zonder dat je de ctor van B expliciet moet definieren? 
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| class A { public fn A(string s) { } } class B : A { } class C : A { public fn C(int i) { } } class D : A { using A; public fn D(int i) { } } B b = new B("woei"); // ok, B ctor default inherit van A C c = new C("woei"); // error, de definitie van C(int) hide de ctors van A D d = new D("woei"); // ok, de ctors van A worden expliciet geinherit door de using declaration |
[ Voor 28% gewijzigd door .oisyn op 28-04-2009 18:03 ]
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.
Het zou inderdaad mooi zijn als constructors default geërfd worden. Al is dat op het moment ook niet zo'n groot probleem met de juiste ontwikkelomgeving, dan worden ze immers automatisch gegenereerd als je dat vraagt.
Maar het komt zo vaak voor dat je constructors 1 op 1 doorlust, dat het geen verkeerde feature zou zijn.
Maar het komt zo vaak voor dat je constructors 1 op 1 doorlust, dat het geen verkeerde feature zou zijn.
“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”
Nou, probleem niet, maar het is op twee manieren eleganter vind ik. Het voorbeeld is misschien niet zo goed, misschien is dit duidelijker:.oisyn schreef op dinsdag 28 april 2009 @ 17:58:
Is 't echt zo'n probleem?
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| fn doQuery() { try { // do some query } catch(SqlException<SqlException.DUPLICATE_KEY> e) { // doQuery knows how to fix this one } } fn runApp() { try { doQuery(); } catch(SqlException<SqlException.CONSTRAINT_ERROR> e) { // runApp knows how to fix this one } } try { runApp(); } catch(SqlException<SqlException.CONNECTION_ERROR> e) { // etc... } |
Je hebt dus geen expliciet nieuw type (class) nodig om alle soorten SQL exceptions afzonderlijk af te kunnen vangen, én je kan je try/catch eenvoudig houden zonder exception te hoeven rethrowen als het generic type bekend is. Als je nagaat hoeveel verschillende soorten SQL errors er zijn, is het begrijpelijk dat er niet zoveel verschillende SQL exceptions zijn, maar het is in heel veel gevallen wel praktisch om te weten wat voor soort sql exception het is. Het gaat me dus niet zozeer om het typen van meer code, want daar ben ik opzich niet zo vies van, maar meer het principe dat je een nieuwe class moet definieren om een exception specifiek af te kunnen vangen, terwijl dat theoretisch met bovenstaand verhaal op te lossen moet zijn.
Misschien is de denkrichting wel verkeerd hoor, maar ik kon me zo voorstellen dat je ueberhaupt al wel ideeën had over generic typing.
Misschien kun je het ook anders oplossen door een catch() een soort boolean expressie te laten evalueren, waarbij catch (Type a) een soort suiker is voor (Exception a, a instanceof Type ? a = (Type)a : false), zodat je iets zou kunnen zeggen in de geest van:
code:
1
2
3
4
5
| try { // } catch (SqlException e, e.type == SqlException.DUPLICATE_KEY) { } |
edit:
Bovenstaande komma zou dan een && moeten zijn, denk ik, maar dat even terzijde
Bovenstaande komma zou dan een && moeten zijn, denk ik, maar dat even terzijde
Maar dat lijkt me veel lastiger in een grammatica vast te leggen?
Dat vind ik sowieso een goed ideeJe twee voorbeelden verschillen niet heel veel van elkaar in aantal regels code namelijk. Maar dat is waarschijnlijk ook omdat je geen constructors in de exceptions hebt gedefinieerd, en volgens mij is dat precies waar het probleem ligt. Het blijft niet bij gewoon een "class B extends A { }" tikken. Maar zou het dan niet een betere oplossing zijn als je het wel daarbij kon laten, en nog steeds B kon constructen met argumenten uit de ctor van A, zonder dat je de ctor van B expliciet moet definieren?
Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz
Maar is dat zoveel anders dan gewoon door throwen?drm schreef op woensdag 29 april 2009 @ 09:38:
[...]
code:
1 2 3 4 5 try { // } catch (SqlException e, e.type == SqlException.DUPLICATE_KEY) { }
edit:
Bovenstaande komma zou dan een && moeten zijn, denk ik, maar dat even terzijde
Maar dat lijkt me veel lastiger in een grammatica vast te leggen?
code:
1
2
3
4
5
| try{ // } catch(SqlException e) { if( e.type != SqlException.DUPLICATE_KEY ) throw; } |
Als je met 1 regel code een nieuwe exception kunt maken dan zou ik het overigens gewoon zo doen
code:
1
2
3
4
5
6
7
| class DuplicateKeySqlException : SqlException {} try{ // } catch(DuplicateKeySqlException e) { } |
Dat is net zoveel type werk, en ik vind het er netter uit zien. Je zult toch ergens vast moeten leggen welke exception types er zijn, waarom dan niet in een class definition? Op die manier kun je ook makkelijker meerdere nivo's van exceptions maken. Want hoe wil je het anders doen als je nog een sub-class van DuplicateKeySqlException krijgt?
code:
1
2
3
4
5
6
7
| class SpecialDuplicateKeySqlException : DuplicateKeySqlException {} try{ // } catch(DuplicateKeySqlException e) { } |
Deze vangt een SpecialDuplicateKeySqlException ook op, als je dat in jouw geval ook wilt doen zou SqlException.SPECIAL_DUPLICATE_KEY een child moeten zijn van SqlException.DUPLICATE_KEY. Maar dan ben je het probleem imho alleen aan het verleggen.
[ Voor 23% gewijzigd door Woy op 29-04-2009 09:57 ]
“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”
Woy onderschrijft idd het punt dat ik probeerde te maken. Regels code heb je sowieso nodig - of je nou verschillende classes hebt of verschillende enum waarden oid. En dan lijkt het me met verschillende classes toch duidelijker. En wat als nou blijkt dat je voor SqlException<SqlException.CONNECTION_ERROR> toch wat meer attributen op wilt nemen dan voor de overige SqlException<T>s? Als het een ConnectionErrorSqlException was kun je gemakkelijk de class aanpassen. Nu zul je ervoor moeten kiezen om het of alsnog een aparte class te maken, of elke SqlException<T> die eigenschappen mee te geven.
Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.