How do you go about choosing variable names?
To me this seems to be the atom of abstraction.
So there should be a lot to talk about if we can overcome the scale.
Was recently reading some about DDD [^1] the basic idea being as follows:
- Use ubiquitous language: the language used in the business domain, in coordination with experts
- Divide the language into contexts which assign a unique meaning to each word.
Typically this means division into microservices or separate programs, but it might work okay with modular monoliths.
At a more basic level there is the division of variable names grammatically. [^2]
- transitive verb with direct object: function
- linking verb with predicative adjective: predicate
- transitive verb: method
- singular noun-phrase: non-boolean datum
- predicative adjective: boolean datum
- plural: when multiple entities
- abbreviation: when scope is small
The idea with both of these is to reuse existing language processing for programming.
Anyone have any refinements?
:[^1]
https://www.thoughtworks.com/insights/blog/domain-driven-design-neednt-be-hard-heres-how-start;[^2]
https://dev.to/somedood/a-grammar-based-naming-convention-13jf>>30995There's an interesting occurrence when renaming variables in scope.
We tend to name variables teleologically based on
use rather than identity.
So we might have multiplier and multiplicand rather than a integer representing something.
This is actually somewhat poetic, as if using a stick as a sword made it so.
>>31000Mostly C++ in practice
Now that you mention it though
Guess determining namespaces isn't too far off topic of naming, it's often just a dot.
One approach to DDD is this idea that there should be "a thin service layer and a rich domain model". [^1][^2]
It typically involves more layering, but really find this rather confusing.
Sort of made progress in this direction in a few applications where all the validation etc. was inside of the domain model.
It's also immediately apparent that a traditional ORM with this technique are rather incompatible with functional core.
Overall don't know where to start when it comes conceptualizing the separating out of modules.
:[^1]
https://medium.com/@inzuael/anemic-domain-model-vs-rich-domain-model-78752b46098f:[^2]
https://martinfowler.com/bliki/AnemicDomainModel.html>>31018Hmmm
This is a bit oldschool but it'll save you a fair bit of work later, get some paper and a pen and sketch out your architecture for your project?
>>30995> How do you go about choosing variable names?Honestly, I go 99% just by vibes.
The only rules I follow are:
- Try too append "s" to the collections
- Prefix "has" or "is" to bools
And that's kinda it.
My only real strategy to improve undesirability is to break down the program a lot, in functions and namespaces. Also I try to keep the functions pure, if I can.
>>30997Maybe in C++ and Java where it can not be exactly clear if you're referencing a class-wide or local variable. Tbh I just use this or the equivalent most of the time.
>>31022Agree with pluralising lists and prefixing has/is on bools.
I will add that "count" can be a useful word for integers, e.g. where a list of objects representing different houses might be named "houses" if I wanted to have a function that took the length of that list as a parameter I might name that parameter "house_count" or "houseCount" depending on convention.
>>30996Think found an example of functional core, imperative shell in my ~/Software directory, metainfo.py in my torrent client.
Basically this object which maintains the overall state of the torrent (excluding the message handlers) is initialized by a set of twelve static method calls.
However, theoretically the object could be recreated every time its state would otherwise need to change and it could be frozen.
It still reads from disk and the network which means that it's not easily tested.
Wonder how the network and filesystem aware parts of the protocol could be implemented in such a way as to separate out the actual IO?
For example how do you test that a piece is properly received without performing the IO to write and receive a piece when there's state to be maintained for the connection etc.?
Should think some about a sans-io [^1] torrent client guess.
:[^1]
https://sans-io.readthedocs.io/how-to-sans-io.html#how-to-write-i-o-free-protocol-implementations >>31045For reals, when you deconstruct a list, are the names
- h t (head, tail)
- a d (car, cdr)
- v vs (or a as, x xs, etc.)
or something else?
>>31053Think prefer "x : xs" of these even though it's less descriptive than the other two (what is "x"?), it generalizes to arbitrary collections, and iterators better, and when used generically allows for more description, for example "for file in files".
>>31053Looking at my imageboard arsvia's code it seems like there's room to pull validation logic into the data model
and to factor out this functionality into static methods for testability. This could be done using SQLAlchemy validators [^1] or custom initializers.
:[^1]
https://docs.sqlalchemy.org/en/20/orm/mapped_attributes.html#simple-validators >>31056>pull validation logic into the data model and to factor out this functionality into static methods for testabilityThis is actually really cool to me! Arsvia could have the service layer virtually deleted. The entire program can be written as rendering and object initialization/mutation with static IO free methods handling the bulk of init. There's probably some errors in the attached image, but the idea was applied to a scratch buffer with the image object. The
init method acts as a imperative shell for the functional core of the static private methods.
I try to use descriptive names for functions, global, records and long-lived state variables in an ugly main function. Otherwise i try to avoid long variable names, because they clutter the code and relying on them means your code isn't clear enough on its own. The same for comments, if you need to explain the minutiae of your code, you better have good reason to!
>>31003This, except i use p-w for pointers and occasionally c-e, h, l-m and ll as pseudo hungarian warts.
>>30999>>31004Think this is related to
performativity. Through repeated use an object can be assigned a concept. Like a cave under a rock can be named a home when so used. It's strictly anti-essentialist. Makes me wonder about E-Prime-esque avoidance of essentialist language. Am not sure about
is at the moment, does it denote essential properties? Even something like "it is gray" breaks down and might take on the meaning of old rather than the color. Would it be more correct to say "it feels gray"?
>>31101If you're designing your API in advance or via wishful thinking use is more relevant.
It may be that performativity cautions against "essentialist" programming of using depth-first naming.
This top-down process is however decidedly not how my programming process looks.
How else could you end up exploiting "one weird trick the pros don't want you to know" to implement API's like the following:
class Base(DeclarativeBase):
## converts an object as a dictionary.
def as_dict (self):
return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns}
def to_json (object):
return json.dumps(list(map(lambda a: a[0].as_dict(), object, default=str)))
This is probably not good.
>>30995Recently, I generally follow what these articles lays out:
https://chrisdone.com/posts/german-naming-convention/https://degoes.net/articles/insufficiently-polymorphicI try to go for full names if possible. I use single letters when the code is polymorphic and full names don't make sense. I avoid acronyms almost completely.
>>31109>https://degoes.net/articles/insufficiently-polymorphicNever would have thought to add polymorphism to make a function more specified.
It's not strictly a program refinement because the postcondition is weakened along with the precondition.
>>31098>Am not sure about is at the momentFind it difficult to reconcile an essentialist framework with other aspects of my thinking so as to even start to reason about "is". The problem may be even more general applying to the "gnomic aspect" e.g. "He plays the guitar." Specifically this language appears to describe a process who's constitutive properties do not change.
Removal of the gnomic in general and especially in English appears impossible. You can however reinterpret the gnomic aspect as the habitual, sense they're syntactically nearly identically. This aligns almost exactly with performativity in that what something is (in the gnomic aspect) is how it habitually acts.
The present tense of verbs in English works to express action in a habitual hypothetical present. Even "two plus two is four" might be habitual.
>>31124Was looking for something like Serg Lang's "Basic Mathematics" from V.I. Arnold's perspective.
Found the attached (AbelNew.pdf), which aims to explain group theory to high-schoolers.
Skimming it didn't actually see much of what it sought above a problem orientated approach.
Perhaps the problems are more geometrical than most and thusly more "physical".
The problems don't come before the definitions to establish that our abstractions map to reality.
Such focus as that would mirror TDD or REPL based development.
This of course could be done in an outside-in or top-down manner. [^1]
And if it was it might remind of "A Radical Approach to Real Analysis" which starts with the "why".
Our problems not only map to reality, but our search is
justified.
In software this means we only write what we need to write.
:[^1]
https://outsidein.dev/concepts/outside-in-tdd/ >>31057There's one more method that can be pulled into the functional core in this example. Namely the conditionals that are responsible for generating the thumbnail once the setting of self.thumbnail and the saving of thumbnail_image.save(filename=thumbnail_path) are pulled out. How would you go about testing the thumbnail generation, and the persistence of the images in this example? Guess you'd still need to testing image files, and configure a save location. This is now coupled to the database however, because it's in the initialization of the ORM. Is the idea to pull out even parts of the imperative shell out from the initialization so that all that all it does is really initialization? The init would then simply be tested with the object as a whole. Refactoring in this way gives the following:
@staitcmethod
def __get_image_size (file):
size = file.seek(0, os.SEEK_END)
file.seek(0)
return size
@staticmethod
def __get_data_mime (file):
mime = magic.from_buffer(file.read(2048), mime=True)
file.seek(0)
return mime
@staticmethod
def __get_filename (file_data, file):
extension = ''.join(pathlib.Path(file.filename).suffixes
return sha256(file_data).hexdigest() + extension
@staticmethod
def __get_location (file_name):
return os.path.join(config.IMAGE_RELATIVE_FOLDER, file_name)
@staticmethod
def __generate_thumbnail (image):
height = round(min(240, 240 * image.height / image.width))
width = round(min(240, 240 * image.width / image.height))
image.sample(width, height)
image_format = image.format if image.format != 'PDF' else 'PNG'
thumbnail_name = sha256(image.make_blob()).hexdigest() + "." + image_format
thumbnail_path = os.path.join(config.THUMBNAIL_FOLDER, thumbnail_name)
return config.THUMBNAIL_RELATIVE_FOLDER + thumbnail_name
@staticmethod
def __generate_thumbnail_by_mime(file_name, mime)
thumbnail_path = None
if re.match("image/*", mime):
with Image(filename=os.path.join(config.IMAGE_FOLDER, file_name)) as image:
thumbnail_image, thumbnail_path = File.__generate_thumbnail(page)
thumbnail_image.save(filename=thumbnail_path)
elif re.match("application/pdf", mime):
with Image(filename=os.path.join(config.IMAGE_FOLDER, file_name)) as image:
page = Image(image=image.sequence[0])
page.alpha_channel='remove'
thumbnail_image, thumbnail_path = File.__generate_thumbnail(page)
return thumbnail_image, thumbnail_path
@staticmethod
def __persist_thumbnail (file_name, mime):
thumbnail_image, thumbnail_path = File.__generate_thumbnail_by_mime(file_name, mime)
thumbnail_image.save(filename=thumbnail_path)
return thumbnail_path
@staticmethod
def __persist_image (file_data, file_name):
with open(os.path.join(config.IMAGE_FOLDER, file_name), "wb+") as disk_file:
disk_file.write(file_data)
# XXX: the check that there are the correct number of files must be handled by the constructor for
# the post.
def __init__ (self, file, post, board, user, address, spoiler):
self.post = post
self.board = board
self.user = user
self.address = address
self.spoiler = spoiler
self.size = self.__get_image_size(file)
self.mime = self.__get_data_mime(file)
file_data = await file.read()
self.name = self.__get_filename(file_data, file)
self.location = self.__get_location(self.name)
# XXX: Persistence must be handled after validation of the object in the initialization functions,
# but before generation of the thumbnail.
self.__persist_image(file_data, self.name)
self.thumbnail = self.__persist_thumbnail(self.name, self.mime)
>>31145There are still several bugs in this program because have just edited it in a scratch buffer with no tooling.
Start with Acceptability Tests.# Validation Logic.
def file_too_large_fails ():
def file_wrong_mime_fails ():
def file_too_large_doesnt_persist ():
def file_wrong_mime_doesnt_persist ():
# Persistence Logic.
def file_persists_thumbnail_for_image_at_hash ():
def file_persists_thumbnail_for_pdf_at_hash ():
def file_persists_file_at_hash ():
Narrow using Unit Testsdef file_get_mime ():
def file_get_size ():
def file_get_filename ():
def file_get_location (): # originally missing.
def file_gen_thumbnail_skinny ():
def file_gen_thumbnail_wide ():
def file_gen_thumbnail_pdf (): # originally missing.
Conclusions1. Unit tests are much clearer than the acceptability test.
2. They suggest a different interface than what was actually implemented. Namely this suggests pulling out the filename function.
3. The functional core becomes what the unit tests are applied to while the imperative shell is tested in the acceptability tests.
4. Nowhere is there a test that the proper interface (as the fields) exists. The design of this interface should be arrived at by higher level implementation.
5. The acceptability tests seem too big to properly lead to the implementation unlike the unit tests.
6. The unit tests do not directly encode the requirements unlike the acceptablity tests and so seem too small to get started.
Questions1. Does this actually work in practice?
2. Would this lead to an excessively mocked test suite?
3. Would this lead to an excessively brittle test suite?
4. Am overall still lost…
>>31152The experiment continues!Couldn't the validation logic tests also be unit tests? e.g. in class-validator open the validator class and test the validate method. The persistence tests would also be unit tests, they would simply require more complicated data and call some of the higher level methods.
What does the red-green-refactor loop look like here? Besides that your integration tests basically suck (the purpose is to integrate (modules), not just to test the imperative core). You need to include tests of the module interface otherwise you're going to go green before you've satisfied the requirements. The obvious next question is what are the requirements? For all relevant pages:
def has_uploaded_file_size_in_units ():
def has_link_from_filename_to_uploaded_file_hash_in_file_directory ():
def has_thumbnail_link_to_file_hash_in_file_directory ():
def has_thumbnail_image_displayed ():
def has_thumbnail_image_maintains_aspect_ratio ():
def has_thumbnail_image_within_maximum_size ():
# Strictly UX tests:
def has_thumbnail_placeholder_for_unhandeled_mime ():
def has_thumbnail_spoiler_for_spoilered_files ():
def has_thumbnail_spoiler_click_unspoilers_thumbnail ():
The exact details that the thumbnail transformation be aspect ratio preserving and within certain dimensions seem to be more implementation details.
In other words because your app is so simple the integration tests fold into the UX tests, the only call site is the UX. With these UX tests written it's clear that your application would not run unless you had the correct functionality (with the exception that the thumbnail transformation might be erroneous).
This might be too many tests, but it's how this outside-in would have to work.
>>31155Continued to clean up the source of the sample. Was thinking more about names. Have come to the conclusion that avoiding trivial thoughts is fruitful. Where this is some combination of repetitive, irrelevant, or vacuous thoughts.
- repetitive: includes any repetition of thoughts of perception or feeling in verbal form.
- irrelevant: includes any thought which is based on a false association (including false statements such as those concerning the self) within some broad context (perhaps a set of goals.)
- vacuous: includes any thoughts that neither contain information nor prompt for further like some rhetorical questions.
These apply rather obviously to programming, but applying them to thought, if you can manage, seems to be a rather radical cognitive shift. LLMs rather dislike my second term "irrelevant", but it still seems important to me so long as it's interpreted rather liberally.
Programming to avoid these things ever appearing in the first place might bring us back to
>>31125 "In software this means we write only what we need…" and the converse set of reasoning that perhaps this entails a top-down or outside-in design.
Unique IPs: 15