
Bonjour, Habr!
Depuis un certain temps maintenant, j'écris mon cadre de jeu - un tel projet pour l'âme. Et puisque pour l'âme vous devez choisir quelque chose que vous aimez (et dans ce cas, ce que vous aimez écrire), mon choix s'est porté sur nim. Dans cet article, je veux parler spécifiquement de nim, de ses fonctionnalités, de ses avantages et de ses inconvénients, et le thème de gamedev ne définit que le contexte de mon expérience - quelles tâches j'ai résolues, quelles difficultés sont survenues.
Il était une fois, quand l'herbe était plus verte et le ciel plus propre, j'ai rencontré nim. Non, pas comme ça. Il était une fois, je voulais développer des jeux pour écrire mon jeu le plus cool - je pense que beaucoup sont passés par là. À cette époque, Unity et Unreal Engine commençaient tout juste à apparaître à l'audience et n'étaient pas encore gratuits. Je ne les ai pas utilisés, pas tant à cause de la cupidité, mais à cause du désir de tout écrire moi-même, de créer un monde de jeu complètement à partir de zéro, du tout le premier zéro octet. Oui, depuis longtemps, oui, c'est difficile, mais le processus lui-même apporte du plaisir - mais que faut-il d'autre pour le bonheur?
Armé de Straustrup et de Qt, j'ai bu de la merde pour le plus grand bien, parce que, premièrement, je ne faisais pas partie des 10 personnes au monde qui connaissaient bien le C ++, et deuxièmement, les avantages mettaient activement des bâtons dans mes roues. Je ne vois aucune raison de répéter ce que le platoff a déjà remarquablement écrit pour moi:
Comment j'ai trouvé le meilleur langage de programmation au monde. Partie 1
Comment j'ai trouvé le meilleur langage de programmation au monde. 2e partie
Comment j'ai trouvé le meilleur langage de programmation au monde. Yo Part (2,72)
C'est un buzz fou quand vous écrivez du code librement, presque sans réfléchir, sans attendre que le noyau soit vidé avant chaque lancement, lorsque des fonctionnalités sont ajoutées juste sous nos yeux, maintenant nous pouvons le faire, et maintenant c'est comme ça, dites-moi s'il vous plaît, quelle différence cela fait-il pour moi Je n'ai pas de modèles si je ne les ai même pas manqués? La productivité est l'objectif principal du programmeur qui fait les choses et la seule tâche de l'outil qu'il utilise.
Lorsque je travaillais avec C ++, je pensais constamment comment écrire ce que je voulais, et non quoi m'écrire. Je suis donc passé à nim. L'histoire est terminée, permettez-moi de partager mon expérience avec vous après plusieurs années chez nim.
Informations générales pour ceux qui ne sont pas au courant
- Compilateur Open Source (MIT), développé par des passionnés. Le créateur de la langue est Andreas Rumpf (Araq). Le deuxième développeur est Dominik Picheta (dom96), qui a écrit le livre Nim en action . De plus, il y a quelque temps, Status a commencé à parrainer le développement du langage, donc nim a obtenu 2 autres développeurs à temps plein. En plus d'eux, bien sûr, d'autres personnes contribueront.
- La version 1.0 a été récemment publiée , ce qui signifie que le langage est stable et que des "changements de rupture" ne sont plus attendus. Si auparavant, vous ne vouliez pas utiliser la version instable, car les mises à jour pouvaient casser l'application, il est maintenant temps d'essayer nim dans vos projets.
- Nim compile (ou transpose) en C, C ++ (qui sont ensuite compilés en code natif) ou JS (avec certaines limitations). En conséquence, avec l'aide de FFI, toutes les bibliothèques existantes pour C et C ++ sont à votre disposition. S'il n'y a pas de package nécessaire sur nim - recherchez s ou plus.
- Les langues les plus proches sont le python (par syntaxe, à première vue) et le D (par fonctionnalité) - IMHO
La documentation
C'est vraiment mauvais. Les problèmes:
- La documentation est dispersée sur différentes sources
- La documentation
merde ne décrit pas entièrement toutes les fonctionnalités du langage - La documentation est parfois trop concise.
Exemple: si vous voulez écrire des applications multithread, il y a beaucoup de cœurs, mais nulle part où aller.
Voici la section de documentation officielle sur les flux . Non, vous voyez, les threads sont une partie majeure distincte du langage, sa fonctionnalité que vous devez même inclure avec l' --threads:on
lors de la compilation. Là, tas partagé ou thread local en fonction du garbage collector, toutes sortes de mémoire partagée et de verrous, sécurité des threads, modules partagés spéciaux, et l'enfer sait quoi d'autre. Comment ai-je su tout ça? C'est vrai, du livre nim en action, du forum, du débordement de pile, du téléviseur et du voisin, en général, de n'importe où, mais pas de la documentation officielle.
Ou il y a un soi-disant. "do notation" - fonctionne très bien lorsque vous utilisez des modèles, etc., généralement partout où vous devez passer un rappel ou simplement un bloc de code. Où puis-je lire à ce sujet? Oui, dans le manuel des fonctionnalités expérimentales .
D'accord, la collecte d'informations sur diverses sources non informatives est toujours un plaisir. Si vous écrivez en nim, vous devez le faire.
Sur le forum et dans les problèmes de github, il y avait des suggestions pour améliorer la documentation, mais les choses n'ont pas avancé. Il me semble qu'il manque une sorte de main dure, qui dira «tout, la communauté, prends les pelles et va ratisser ce tas de… ingénieux morceaux épars de texte».
Heureusement, j'ai frappé le mien, donc je vous présente la liste des champions nim
La documentation
- Tutoriel 1 , Tutoriel 2 - commencez par eux
- Nim in action est un livre explicatif qui explique vraiment bien de nombreux aspects de la langue, parfois bien mieux que de. documentation
- Manuel Nim - en fait, un manuel - presque tout est décrit, mais pas
- Manuel expérimental Nim - pourquoi ne pas continuer la documentation sur une page séparée?
- L'Index - des liens vers tout sont rassemblés ici, c'est-à-dire en général tout ce qui peut être trouvé dans nim. Si vous n'avez pas trouvé ce dont vous avez besoin dans les didacticiels et le manuel, vous le trouverez certainement dans l'index.
Leçons et tutoriels
- Nim basics - les bases pour les débutants, les sujets complexes non couverts
- Nim Days - petits projets (exemples en direct)
- Code Rosetta - il est très cool de comparer la solution des mêmes tâches sur différents PL, y compris nim
- Exercism.io - ici, vous pouvez aller "chemin nim", effectuer des tâches
- Nim par l'exemple
Aide
- L'IRC est l'habitat principal ... des nimmers?, Qui est diffusé sur Discord et Gitter . Je n'ai jamais utilisé IRC (et ne l'utilise toujours pas). En général, c'est un choix très étrange. Il y a encore du courrier de pigeon pour lui ... d'accord, plaisantant.
- Forum Nim Les fonctionnalités du forum sont minimes, mais 1) ici vous pouvez trouver la réponse 2) ici vous pouvez poser une question si l'élément 1 n'a pas fonctionné 3) la probabilité d'une réponse est supérieure à 50% 4) les développeurs de langage sont assis sur le forum et répondent activement. Par ailleurs, le forum est écrit en nim, et donc il n'y a pas de fonctionnalité
- Groupe télégramme Nim - il est possible de poser une question et de ne pas obtenir de réponse.
- Il y a aussi un groupe de télégrammes russes, si vous êtes fatigué de nim et ne voulez rien entendre à ce sujet, vous devriez y aller :) (en partie une blague)
Aire de jeux
- Aire de jeux Nim - ici, vous pouvez exécuter le programme sur nim directement dans le navigateur
- Nim docker cross-compiling - ici, vous pouvez lire comment lancer une image docker et compiler le programme pour différentes plates-formes.
Forfaits
Passer à nim à partir d'autres langues
Qu'est ce que tu aimes
Cela n'a aucun sens de lister toutes les fonctionnalités du langage, mais voici quelques fonctionnalités:
Complexité fractale
Nim vous offre une «fractale de complexité». Vous pouvez écrire du code de haut niveau. Vous pouvez utiliser des pointeurs bruts et profiter de chaque attempt to read from nil
. Vous pouvez intégrer le code C. Vous pouvez écrire des insertions dans l'assembleur . Vous pouvez écrire des procédures (répartition statique). Pas assez - il y a des "méthodes" (répartition dynamique). Plus? Il existe des génériques et des génériques qui imitent les fonctions. Il existe des modèles (modèles) - un mécanisme de remplacement, mais pas aussi vomissant qu'en C ++ (y a-t-il des macros - est-ce toujours un remplacement de texte, ou est-ce quelque chose de plus intelligent?). Il y a des macros, à la fin - c'est comme IDDQD, elles activent le mode dieu et vous permettent de travailler directement avec AST et de remplacer littéralement des morceaux de l'arbre de syntaxe, ou d'étendre le langage vous-même comme vous le souhaitez.
Autrement dit, à un niveau "élevé", vous pouvez écrire des mots infernaux et du chagrin pour ne pas le savoir, mais personne ne vous interdit de commettre des fraudes de toute complexité.
Vitesse de développement
La courbe d'apprentissage n'est pas une courbe. C'est direct. En installant nim, vous commencerez votre premier monde bonjour dans la première minute, et le premier jour vous écrirez un utilitaire simple. Mais dans quelques mois, vous aurez quelque chose à apprendre. Par exemple, j'ai commencé par les procédures, puis j'ai eu besoin de méthodes, après un certain temps, les génériques m'ont été très utiles, récemment j'ai découvert des modèles dans toute leur beauté, et en même temps je n'ai pas du tout touché aux macros. En comparant avec la même rouille ou c ++, la fusion avec nim est beaucoup plus facile.
Gestion des packages
Il existe un gestionnaire de packages appelé nimble qui peut installer, désinstaller, créer des packages et charger des dépendances. Lorsque vous créez votre package (= projet), vous pouvez écrire différentes tâches dans nimble (à l'aide de nimscript, qui est un sous-ensemble de nim exécutable sur VM), par exemple, générer de la documentation, exécuter des tests, copier des actifs, etc. Nimble met non seulement les dépendances nécessaires, mais vous permet également de configurer l'environnement de travail pour votre projet. Autrement dit, agile est, en gros, CMake, qui a été écrit non par des pervers, mais par des gens normaux.
Lisibilité et expressivité
Extérieurement, nim est très similaire à python avec des annotations de type, bien que nim ne soit pas du tout python. Les pythonistes devront oublier le typage dynamique, l'héritage, les décorateurs et autres joies, et restructurer généralement leur pensée. N'essayez pas de transférer votre expérience python vers nim, car la différence est trop grande. Au début, je veux vraiment des collections hétérogènes et des mixins avec des décorateurs. mais vous vous habituez à vivre des difficultés :)
Voici un exemple de programme nim:
type NumberGenerator = object of Service # this service just generates some numbers NumberMessage = object of Message number: int proc run(self: NumberGenerator) = if not waitAvailable("calculator"): echo "Calculator is unavailable, shutting down" return for number in 0..<10: echo &"Sending number {number}" (ref NumberMessage)(number: number).send("calculator")
Modularité
Tout est divisé en modules que vous pouvez importer à votre guise - pour importer uniquement certains caractères, ou tous sauf certains, ou tous, ou aucun et forcer l'utilisateur à spécifier le chemin d'accès complet à la module.function()
, et également importer sous un nom différent. Bien sûr, toute cette variété est très utile comme autre argument dans le débat "quel langage de programmation est le meilleur", eh bien, dans votre projet, vous écrirez tranquillement un import mymodule
et vous ne vous souviendrez pas d'autres options.
Syntaxe d'appel de méthode
Un appel de fonction peut être enregistré de différentes manières:
double(2) double 2 2.double() 2.double
D'une part, maintenant tout le monde ... écrit comme il veut (et tout le monde l'aime de différentes manières, bien sûr, et de différentes manières même dans le cadre d'un projet). Mais alors toutes les fonctions peuvent être écrites comme un appel de méthode, ce qui améliore considérablement la lisibilité. En python, cela peut être:
list(set(some_list))
Le même code dans nim pourrait être réécrit de manière plus logique:
some_list.set.list #
OOP
La POO, bien qu'elle soit présente, en diffère par ses avantages et python: les objets et les méthodes sont des entités différentes et peuvent bien exister dans différents modules. De plus, vous pouvez écrire vos méthodes pour les types de base comme int
proc double(number: int): int = number * 2 echo $2.double() # prints "4"
En revanche, il y a encapsulation dans nim (la première règle du module dans nim est de ne parler à personne des identifiants sans astérisque). Voici un exemple de module standard:
# sharedtables.nim type SharedTable*[A, B] = object ## generic hash SharedTable data: KeyValuePairSeq[A, B] counter, dataLen: int lock: Lock
Le type SharedTable*
est marqué d'un astérisque, ce qui signifie qu'il est "visible" dans d'autres modules et peut être importé. Mais ici, data
, counter
et lock
sont des membres privés, et sharedtables.nim
ne sont pas accessibles de l'extérieur. Cela m'a fait très plaisir quand j'ai décidé d'écrire quelques fonctions supplémentaires pour le type SharedTable
, comme len
ou hasKey
, et j'ai constaté que je n'avais accès ni au counter
ni aux data
, et la seule façon de "développer" SharedTable
était d'écrire la vôtre , avec bl
En général, l'héritage est utilisé beaucoup moins souvent que dans le même python (par expérience personnelle), car il existe une syntaxe d'appel de méthode (voir ci-dessus) et des variantes d'objet (voir ci-dessous). Le chemin nim est la composition plutôt que l'héritage. Il en va de même pour le polymorphisme: dans nim, il existe des méthodes qui peuvent être remplacées dans les classes successives, mais cela doit être explicitement spécifié lors de la compilation en utilisant l' --multimethods:on
. Autrement dit, par défaut, les méthodes ne fonctionnent pas, ce qui encourage légèrement le travail sans elles.
Exécution à la compilation
Const - la capacité de calculer quelque chose au stade de la compilation et de le "coudre" dans le binaire résultant. C'est cool et confortable. En général, nim a une relation spéciale avec le "temps de compilation", il y a même un mot-clé when
- c'est comme if
, mais la comparaison est au stade de la compilation. Vous pouvez écrire quelque chose comme
when defined(SDL_VIDEO_DRIVER_WINDOWS): import windows ## oldwinapi lib elif defined(SDL_VIDEO_DRIVER_X11): import x11/x, x11/xlib ## x11 lib
C'est très pratique, bien qu'il existe des restrictions sur ce que vous pouvez faire au stade de la compilation (par exemple, vous ne pouvez pas effectuer d'appels FFI).
Type de référence
Type de référence - un analogue de shared_ptr en C ++, dont le garbage collector se chargera. Mais vous pouvez également appeler le garbage collector vous-même aux moments où cela vous convient. Ou vous pouvez essayer différentes options pour les récupérateurs. Ou vous pouvez désactiver complètement le garbage collector et utiliser des pointeurs réguliers.
Idéalement, si vous n'utilisez pas de pointeurs bruts et de FFI, il est peu probable que vous obteniez des erreurs de segmentation. En pratique, jusqu'ici sans FFI nulle part.
Lambdas
Il existe des procédures anonymes (aka lambdas en python), mais contrairement à python dans une procédure anonyme, vous pouvez utiliser plusieurs instructions:
someProc(callback=proc(a: int) -> int = var b = 5*a; result = a)
Exceptions
Il y a des exceptions, elles sont très gênantes à lancer: python raise ValueError('bad value')
, nim raise newException(ValueError, "bad value")
. Rien de plus inhabituel - essayez, sauf que, finalement, tout est comme tout le monde. En tant que partisan des exceptions et non des codes d'erreur, je me réjouis. Soit dit en passant, vous pouvez indiquer pour les fonctions les exceptions qu'elles peuvent lever, et le compilateur vérifiera ceci:
proc p(what: bool) {.raises: [IOError, OSError].} = if what: raise newException(IOError, "IO") else: raise newException(OSError, "OS")
Génériques
Les génériques sont très expressifs, par exemple, vous pouvez limiter les types possibles
proc onlyIntOrString[T: int|string](x, y: T) = discard # int string
Et vous pouvez passer un type en général comme paramètre - cela ressemble à une fonction ordinaire, mais en fait un générique:
proc p(a: typedesc; b: a) = discard # is roughly the same as: proc p[T](a: typedesc[T]; b: T) = discard # hence this is a valid call: p(int, 4) # as parameter 'a' requires a type, but 'b' requires a value.
Modèles
Les modèles sont quelque chose comme des macros en C ++, juste faits correctement :) - vous pouvez transférer en toute sécurité des blocs entiers de code vers des modèles, et ne pensez pas que la substitution va ruiner quelque chose dans le code externe (mais vous pouvez, encore une fois , pour le gâcher, si vous en avez vraiment besoin).
Voici un exemple de modèle d' app
qui, en fonction de la valeur de la variable, appelle l'un des blocs de code:
template app*(serverCode: untyped, clientCode: untyped) = # ... case mode of client: clientCode of server: serverCode else: discard
Avec do
je peux passer des blocs entiers au modèle, par exemple:
app do: # serverCode echo "I'm server" serverProc() do: # clientCode echo "I'm client" clientProc()
Coque interactive
Si vous avez besoin de tester rapidement quelque chose, c'est-à-dire la possibilité d'appeler un "interpréteur" ou un "shell nim" (comme si vous exécutiez python
sans paramètres). Pour ce faire, utilisez la commande nim secret
ou téléchargez le package inim .
Ffi
FFI - la capacité d'interagir avec des bibliothèques tierces en C / C ++. Malheureusement, pour utiliser une bibliothèque externe, vous devez écrire un wrapper expliquant où et quoi importer. Par exemple:
{.link: "/usr/lib/libOgreMain.so".} type ManualObjectSection* {.importcpp: "Ogre::ManualObject::ManualObjectSection", bycopy.} = object
Il existe des outils qui rendent ce processus semi-automatique:
Que n'aime pas
Difficulté
Trop de choses. Le langage a été conçu comme minimaliste, mais maintenant il est très loin de la vérité. Par exemple, pourquoi avons-nous obtenu une réorganisation du code ?!
Redondance
Beaucoup de merde: system.addInt - "Convertit un entier en sa représentation sous forme de chaîne et l'ajoute au résultat". Il me semble que c'est une fonction très pratique, je l'utilise dans tous les projets. En voici une autre intéressante: fileExists et existFile ( https://forum.nim-lang.org/t/3636 )
Pas d'unification
"Il n'y a qu'une seule façon de faire quelque chose" - pas du tout:
- Syntaxe d'appel de méthode - écrivez un appel de fonction comme vous le souhaitez
fmt
vs &
- camelCase et underscore_notation
- ceci et tHiS (spoiler: c'est la même chose)
- fonction vs procédure vs modèle
Bugs (pas de SACS!)
Il y a des bugs, environ 1400 . Ou allez simplement sur le forum - ils trouvent constamment des bugs.
La stabilité
En plus du paragraphe précédent, la v1 implique la stabilité, non? Et ici, le créateur de la langue Araq vole vers le forum et dit: "Mec, j'ai un autre (sixième) ramasse-miettes ici, c'est plus frais, plus rapide, plus jeune, vous donne une mémoire partagée pour les threads (ha ha, et avant cela vous avez souffert et béquilles usagées), téléchargez la branche develop et essayez. " Et tous ces "Wow, comme c'est cool! Et qu'est-ce que cela signifie pour les simples mortels? Devons-nous maintenant changer à nouveau tout le code?" Il semble que non, donc je mets à jour nim, lance un nouveau garbage collector --gc:arc
et mon programme plante quelque part au stade de la compilation du code c ++ (c'est-à-dire pas dans nim, mais dans gcc):
/usr/lib/nim/system.nim:274:77: error: 'union pthread_cond_t' has no member named 'abi' 274 | result = x
Super! Maintenant, au lieu d'écrire du nouveau code, je dois réparer l'ancien. N'est-ce pas ce que je fuyais quand j'ai choisi nim?
Ravi de savoir que je ne suis pas seul
Méthodes et multithreading
Par défaut, les indicateurs multiméthodes et threads sont désactivés - vous n'allez pas 2019 2020 écrire une application multi-thread avec des méthodes primordiales?! Et à quel point c'est génial si votre bibliothèque a été créée sans tenir compte des flux, puis que l' utilisateur les a activés ... Oh oui, il y a de merveilleux pragmas {.inheritable.} Et {.base.} Pour l'héritage afin que votre code ne soit pas trop concis.
Variantes d'objets
Vous pouvez éviter l'héritage en utilisant ce que l'on appelle variantes d'objet:
type CoordinateSystem = enum csCar, # Cartesian csCyl, # Cylindrical Coordinates = object case cs: CoordinateSystem: # cs is the coordinate discriminator of csCar: x: float y: float z: float of csCyl: r: float phi: float k: float
Selon la valeur de cs
, les champs x, y, z ou r, phi et k seront à votre disposition.
Quels sont les inconvénients?
Premièrement, la mémoire est réservée à la "plus grande option" - de sorte qu'elle est garantie de tenir dans la mémoire allouée à l'objet.
Deuxièmement, l'héritage est encore plus flexible - vous pouvez toujours créer un descendant et ajouter plus de champs, et dans la variante d'objet tous les champs sont définis de manière rigide dans une section.
Troisièmement, ce qui est le plus exaspérant, c'est que vous ne pouvez pas «réutiliser» des champs dans différents types:
type # The 3 notations refer to the same 3-D entity, and some coordinates are shared CoordinateSystem = enum csCar, # Cartesian (x,y,z) csCyl, # Cylindrical (r,φ,z) Coordinates = object case cs: CoordinateSystem: # cs is the coordinate discriminator of csCar: x: float y: float z: float # z already defined here of csCyl: r: float phi: float z: float # fails to compile due to redefinition of z
Faire de la notation
Juste pour citer :
- faire avec les parenthèses est un proc anonyme
- se passer de parenthèses n'est qu'un bloc de code
Une expression signifie différentes choses ¯_ (ツ) _ / ¯
Quand utiliser
Nous avons donc des fonctions, des procédures, des génériques, des multiméthodes, des modèles et des macros. Quand est-il préférable d'utiliser un modèle et quand est une procédure? Modèle ou générique? Fonction ou procédure? Et les macros? Je pense que vous comprenez.
Pragma personnalisé
Il existe des décorateurs en python qui peuvent être appliqués même aux classes, même aux fonctions.
Il y a des pragmas en nim pour cela. Et voici ce que:
Agile
Ce qui est mort ne peut pas mourir. En agile, un tas de projets qui n'ont pas été mis à jour depuis longtemps (et en nim c'est comme la mort) - et ils ne sont pas supprimés. Personne ne suit cela. Il est clair, rétrocompatible, "vous ne pouvez pas simplement prendre et retirer le paquet du navet", mais quand même ... D'accord, merci, du moins pas comme npm.
Abstraction qui fuit
Il existe une telle loi des abstractions trouées - vous utilisez une sorte d'abstraction, mais tôt ou tard, vous y trouverez un «trou» qui vous mènera à un niveau inférieur. Nim est une abstraction de C et C ++, et tôt ou tard vous y échouerez. Je parie que vous ne l'aimez pas là-bas?
Error: execution of an external compiler program 'g++ -c -w -w -fpermissive -pthread -I/usr/lib/nim -I/home/user/c4/systems/network -o /home/user/.cache/nim/enet_d/@m..@s..@s..@s..@s..@s..@s.nimble@spkgs@smsgpack4nim-0.3.0@smsgpack4nim.nim.cpp:6987:136: note: initializing argument 2 of 'void unpack_type__k2dhaoojunqoSwgmQ9bNNug(tyObject_MsgStreamcolonObjectType___kto5qgghQl207nm2KQZEDA*, NU&)' 6987 | N_LIB_PRIVATE N_NIMCALL(void, unpack_type__k2dhaoojunqoSwgmQ9bNNug)(tyObject_MsgStreamcolonObjectType___kto5qgghQl207nm2KQZEDA* s, NU& val) { nimfr_("unpack_type", "/home/user/.nimble/pkgs/msgpack4nim-0.3.0/msgpack4nim.nim"); |
tyObject_MsgStreamcolonObjectType ___ kto5qgghQl207nm2KQZEDA * s, NU & val) {nimfr _ ( "unpack_type", "/home/user/.nimble/pkgs/msgpack4nim-0.3.0/msgpack4nim.nim"); Error: execution of an external compiler program 'g++ -c -w -w -fpermissive -pthread -I/usr/lib/nim -I/home/user/c4/systems/network -o /home/user/.cache/nim/enet_d/@m..@s..@s..@s..@s..@s..@s.nimble@spkgs@smsgpack4nim-0.3.0@smsgpack4nim.nim.cpp:6987:136: note: initializing argument 2 of 'void unpack_type__k2dhaoojunqoSwgmQ9bNNug(tyObject_MsgStreamcolonObjectType___kto5qgghQl207nm2KQZEDA*, NU&)' 6987 | N_LIB_PRIVATE N_NIMCALL(void, unpack_type__k2dhaoojunqoSwgmQ9bNNug)(tyObject_MsgStreamcolonObjectType___kto5qgghQl207nm2KQZEDA* s, NU& val) { nimfr_("unpack_type", "/home/user/.nimble/pkgs/msgpack4nim-0.3.0/msgpack4nim.nim"); |
/usr/bin/ld: /home/user/.cache/nim/enet_d/stdlib_dollars.nim.cpp.o: in function `dollar___uR9bMx2FZlD8AoPom9cVY9ctA(tyObject_ConnectMessage__e5GUVMJGtJeVjEZUTYbwnA*)': stdlib_dollars.nim.cpp:(.text+0x229): undefined reference to `resizeString(NimStringDesc*, long)' /usr/bin/ld: stdlib_dollars.nim.cpp:(.text+0x267): undefined reference to `resizeString(NimStringDesc*, long)' /usr/bin/ld: stdlib_dollars.nim.cpp:(.text+0x2a2): undefined reference to `resizeString(NimStringDesc*, long)'
Alors
Je suis un programmeur stupide. Je ne veux pas savoir comment fonctionne le GC, ce qu'il contient et comment il est lié, où il est mis en cache et comment les déchets sont supprimés. C'est comme une voiture - en principe, je sais comment ça fonctionne, un peu sur l'alignement des roues, un peu sur la boîte de vitesses, j'ai besoin de faire le plein d'huile et d'autres trucs, mais en général je veux juste m'asseoir et aller (et vite) à la fête. Une machine n'est pas un but, mais un moyen pour une fin. S'il tombe en panne, je ne veux pas entrer dans le capot, mais prenez-le simplement au service (dans le sens, j'ouvrirai le problème sur le github), et ce serait génial s'ils le réparaient rapidement.
Nim était censé être une telle machine. En partie, il est devenu, mais en même temps, lorsque je me précipite sur l'autoroute sur cette voiture, ma roue tombe et le rétroviseur arrière pointe vers l'avant. Les ingénieurs courent après moi et attachent quelque chose à la volée ("maintenant votre voiture est encore plus rapide avec ce nouveau becquet"), mais de là le coffre tombe. Et tu sais quoi? Je suis encore comme cette voiture, car c'est la meilleure de toutes les voitures que j'ai vues.