mleku on Nostr: i'm not surprised with the state of the programmer business what a bunch of cowboys ...
i'm not surprised with the state of the programmer business what a bunch of cowboys they all are, with absolutely zero formal training, but it's something i am gonna call out
what prompts this is trawling through fiatjaf (nprofile…n6nn) 's code
yes i atted him because he should get some feedback and idgaf what anyone thinks of my opinion but it's based on a long time (since 1985) studying programming - no i didn't get a BSc or other but i'm not far from having that level of understanding of the subject
FIRST: stick to a simple, logical structure and don't mix metaphors up for no reason
this one number one reason why Go didn't have generics until last year, and seeing what a hash of it fiatjaf is making by using them willy-nilly for no particular reason when a type specific version takes less space for 2 or 3 implementations than the generic version by itself, because of the fact it's covering all those cases redundantly when they have specifics to them
don't go reaching for type parameters if you only need 3 different implementations of a functionality that only takes a few lines
why?
the very same principle applies to interfaces - some people think they give you organisation to your code but honestly if you are only making two implementations it's not really worth making an interface unless you anticipate making a third later, and it's not difficult to recognise them by the common function signatures
Ctrl-C Ctrl-V are your friends
learn how to use multiple carets
another example i found has a singleton for listeners while the rest of the relay type (in Khatru) is designed to be able to run multiple instances in one application
if one part of it doesn't fit the singleton pattern, don't use it with a component of the first part, what happens when someone naively tries to use your Relay type in Khatru with two independent relay instances as it seems like it's possible?
SECOND: when you use constant number codes, like the event kinds, make them into a type so they are compile time validated and make special ones if you want to use ranges as a typing for them
10000-19999 are metadata events
20000-29999 are ephemeral events
you make your code hard to read and brittle when in the future you change the definitions somehow, maybe add subtypes, for example, and then you are scratching around looking for references to a number that has no contextual meaning
THIRD: think about how the names of your packages look when you use them
nostr.Filter is a very poor context name - where is Filter defined?
oh yes, in NIP-01 - alongside events, for which Filters are search queries
FOURTH: don't mix up directions with comparison operations for no reason
a range boundary check should look like this:
element >= startOfRange || element < endOfRange
if you don't use a consistent ordering it's hard to read what you are saying
30000 <= evt.Kind && evt.Kind < 40000
is not as clear as
evt.Kind >= kind.ParameterizedReplaceableStart &&
evt.Kind < kind.ParameterizedReplaceableEnd
just combining that with the second point because this is what I see in Khatru
just because you can do this in math:
lower < x < upper
doesn't mean it makes sense to try and mimic this in code, it's clumsy and ugly as code without this funny abbreviated notation
the math notation leaves out the implicit separate context of these two comparisons, and flips the precedence of each side - it is neat to write it this way on a whiteboard but to implement it without ambiguity you are breaking the simplicity of the way that the machine does in, and the simplicity of how reading left to right you see "start ... end" which is lexicographic, not telegraphic like so much math notation is
the more people poison models with other models, the more difficult they are to learn and the more difficult they are to build as implementations for dumb, simple machines
computer programs are left to right, top to bottom, infixes are best kept consistent between related, ANDed pairs, names of Go packages are a useful place to put context, and don't reach for convenience tools to abstract a task that has only two or three related instances of, because it's just a waste of space, they are almost never identical across types except in very narrow ways even if they are similar (this is why Go has interfaces, and why generics have been reluctantly and in a very limited way added)
number one problem i see with much of the code i see from amateurs these days is they are just trying to rush things out and the whole thing ends up being spaghetti scripting nonsense with muddled names and poor structure
if you are a bitcoiner, you should understand that taking a little extra time is always better - measure twice, cut once
low time preference is doubly important for programming as it is for life in general, because every bit of code you write is gonna be someone else's problem in the future and you are racking up bad karma by writing shitty code
what prompts this is trawling through fiatjaf (nprofile…n6nn) 's code
yes i atted him because he should get some feedback and idgaf what anyone thinks of my opinion but it's based on a long time (since 1985) studying programming - no i didn't get a BSc or other but i'm not far from having that level of understanding of the subject
FIRST: stick to a simple, logical structure and don't mix metaphors up for no reason
this one number one reason why Go didn't have generics until last year, and seeing what a hash of it fiatjaf is making by using them willy-nilly for no particular reason when a type specific version takes less space for 2 or 3 implementations than the generic version by itself, because of the fact it's covering all those cases redundantly when they have specifics to them
don't go reaching for type parameters if you only need 3 different implementations of a functionality that only takes a few lines
why?
the very same principle applies to interfaces - some people think they give you organisation to your code but honestly if you are only making two implementations it's not really worth making an interface unless you anticipate making a third later, and it's not difficult to recognise them by the common function signatures
Ctrl-C Ctrl-V are your friends
learn how to use multiple carets
another example i found has a singleton for listeners while the rest of the relay type (in Khatru) is designed to be able to run multiple instances in one application
if one part of it doesn't fit the singleton pattern, don't use it with a component of the first part, what happens when someone naively tries to use your Relay type in Khatru with two independent relay instances as it seems like it's possible?
SECOND: when you use constant number codes, like the event kinds, make them into a type so they are compile time validated and make special ones if you want to use ranges as a typing for them
10000-19999 are metadata events
20000-29999 are ephemeral events
you make your code hard to read and brittle when in the future you change the definitions somehow, maybe add subtypes, for example, and then you are scratching around looking for references to a number that has no contextual meaning
THIRD: think about how the names of your packages look when you use them
nostr.Filter is a very poor context name - where is Filter defined?
oh yes, in NIP-01 - alongside events, for which Filters are search queries
FOURTH: don't mix up directions with comparison operations for no reason
a range boundary check should look like this:
element >= startOfRange || element < endOfRange
if you don't use a consistent ordering it's hard to read what you are saying
30000 <= evt.Kind && evt.Kind < 40000
is not as clear as
evt.Kind >= kind.ParameterizedReplaceableStart &&
evt.Kind < kind.ParameterizedReplaceableEnd
just combining that with the second point because this is what I see in Khatru
just because you can do this in math:
lower < x < upper
doesn't mean it makes sense to try and mimic this in code, it's clumsy and ugly as code without this funny abbreviated notation
the math notation leaves out the implicit separate context of these two comparisons, and flips the precedence of each side - it is neat to write it this way on a whiteboard but to implement it without ambiguity you are breaking the simplicity of the way that the machine does in, and the simplicity of how reading left to right you see "start ... end" which is lexicographic, not telegraphic like so much math notation is
the more people poison models with other models, the more difficult they are to learn and the more difficult they are to build as implementations for dumb, simple machines
computer programs are left to right, top to bottom, infixes are best kept consistent between related, ANDed pairs, names of Go packages are a useful place to put context, and don't reach for convenience tools to abstract a task that has only two or three related instances of, because it's just a waste of space, they are almost never identical across types except in very narrow ways even if they are similar (this is why Go has interfaces, and why generics have been reluctantly and in a very limited way added)
number one problem i see with much of the code i see from amateurs these days is they are just trying to rush things out and the whole thing ends up being spaghetti scripting nonsense with muddled names and poor structure
if you are a bitcoiner, you should understand that taking a little extra time is always better - measure twice, cut once
low time preference is doubly important for programming as it is for life in general, because every bit of code you write is gonna be someone else's problem in the future and you are racking up bad karma by writing shitty code