Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
KillHour
Oct 28, 2007


RPATDO_LAMD posted:

If you just wanna look at langs that clearly differentiate values and references, C is your friend.

Yes

RPATDO_LAMD posted:

If you wanna actually program in it... maybe not so much

No

Adbot
ADBOT LOVES YOU

Foxfire_
Nov 8, 2010

nielsm posted:

It's not an answer at all, but "making primitive values a reference" is something that happens in Python in an odd way:
All integers between 0...256 are singleton objects, but larger integers are created as individual objects.

>>> a = 34
>>> b = 34
>>> c = 834
>>> d = 834
>>> a == b, a is b, c == d, c is d
(True, True, True, False)

Where the == operator does a value comparison, and the 'is' operator does a reference equality comparison.
That is done because python only has pointer/reference types. There are no values, everything is internally a PyObject*.

Constructing a new PyObject is expensive because it takes a heap allocation plus a bunch of bookkeeping, and they also have lots of ram overhead.

So there's an implementation-defined size table of pre-built PyLong instances immune to garbage collection for integers near zero, and the PyLong_FromLong() function that makes PyLong instances from C long values returns those sometimes.

PyLong are immutable (python operators like += are lying to you, they return a different object instead of modifying), so it doesn't practically matter that they're shared, and there's not really a way to tell that they aren't values except asking about object identity explicitly

KillHour
Oct 28, 2007


ultrafilter posted:

jfc go post that in coding horrors.

That's nothing. Because numbers in Python are objects, you can change them.

https://dev.to/artyomkaltovich/how-the-integer-type-is-implemented-in-cpython-8ei

Python code:
import ctypes

# PyLongObject structure
class IntStruct (ctypes.Structure):
    # declaration of fields
    _fields_ = [("ob_refcnt", ctypes.c_long),
                ("ob_type", ctypes.c_void_p),
                ("ob_size", ctypes.c_long),
                ("ob_digit", ctypes.c_int)]

    def __repr __ (self):
        return (f"IntStruct (ob_digit = {self.ob_digit}, "
                f"ob_size = {self.ob_size}, "
                f"refcount = {self.ob_refcnt})")


if __name__ == '__main__':
    # get the address of number 42
    int42 = IntStruct.from_address (id (42))
    print (int42)

    int_minus_2 = IntStruct.from_address (id (-2))
    print (int_minus_2) # ob_digit = 2, ob_size = -1

    # change the value in the list of preallocated numbers
    int42.ob_digit = 4
    print (4 == 42) # True
    print (1 + 41) # 4
:unsmigghh:

Anyways, yeah, I want the programming language that's the opposite of... that.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

nielsm posted:

It's not an answer at all, but "making primitive values a reference" is something that happens in Python in an odd way:
All integers between 0...256 are singleton objects, but larger integers are created as individual objects.

>>> a = 34
>>> b = 34
>>> c = 834
>>> d = 834
>>> a == b, a is b, c == d, c is d
(True, True, True, False)

Where the == operator does a value comparison, and the 'is' operator does a reference equality comparison.

So many things about Python are well thought out, but I’m always amazed at how incompetent the basic stuff like this is. Like, even JavaScript gets this right.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Java does the same thing, although you don't typically use the boxed versions for anything other than putting them in a generic data structure where you need to give it a non-primitive.

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...

KillHour posted:

It's not so much that I want to make it impossible to do (since what does that even mean), but more that I want to see if there are languages that have a stronger syntactical distinction to make the fundamental difference more clear to a less technical person. In the database world, there has been a movement away from developers making schemas towards business users making ontologies. The line between data structure and business concept model is becoming blurry. This means that the people coming up with these things don't understand (or want to understand) how the underlying queries function. I'm not planning on adopting some esoteric language. I just want some studying material on how you might design a model framework that has a clear separation of concerns between "this is a specific thing" and "this is an abstract idea."

Maybe a more concrete example would be good. Let's say I have a database of houses for sale. Some properties of those houses are just values - square footage, number of bathrooms, etc. Some properties could be references to other things - school district, previous owner, etc. Now pretend I have executive "Moron A" who declares that they want to do a feature on houses owned by famous people, so they have their pet data scientist pull down the DBpedia graph for famous people and run some cursed SPARQL query to match the previous owner field to the graph and grab a report for their feature. That's great. I want them to be able to do that - it's a useful application that drives business value, whatever. But next week, Moron A gets it in their head that they want to add "owned by famous person" as a permanent filter on their real estate website. That cursed SPARQL took like 40 minutes to run, so that's not happening in real time, and when they go to their DBA to add an "owned by famous person" column to the table, they're told to get hosed. So one thing leads to another, and it's decided that schemas are stupid and everything needs to be an ontology to "enable the business." Before you know it, you have "0 sqft" as a node in a graph with about 10 million relationships and the database backing the website shits itself whenever anyone sorts by square footage.

Really, I'm just looking for interesting examples of where a programming language has a strong separation of property vs related object. This might not actually exist - I don't know - but I thought with all the weird languages out there, someone might have come up with something interesting I can crib from, even for just a logical framework.

Edit: I'm not trying to prevent someone from doing anything with technical means - I know that's impossible. Instead, I'm looking for prior art on conceptual approaches that I can use to build recommendations.

That's exactly the issue - that the data behaves the same way in both situations with the same operators. But from a conceptual perspective, different things are happening. Languages abstract this away from you and expect you, as the programmer, to know the difference. But from an outcome perspective, I generally want to do different kinds of things with objects vs values. Comparison is a good example. Comparisons like < or > only generally make sense between values (ignoring overloading or lambda functions). This is really hard to explain if you're not used to it, so I'm trying to find a language that makes it explicit that a reference to an object and a value are fundamentally different things.

So, what exactly are you going to do, even if you find or make this language that doesn't seem to exist? Do you just want some kind of thing to show off to execs, and if so, why would they actually listen to this when they're inviting the rest of it?

I think I can understand what the problem is, I'm just not sure what you feel is going to happen. I wrote a whole metaphor about how objects are forms, primitives are simple fields entries on that form, and object references are "see/attach form 6052" on it. But I'm not sure what that would ultimately accomplish. Are you trying to get them to understand how computers actually work with pointers and stuff?

... Actually, going the opposite direction and showing them C might be a good way to do it. Technically, it's all just Numbers, but you actually mean things with those Numbers, and things like objects adding another object really means that you go into this Add method, and it does THESE specific bits of arithmetic on THESE fields here, and really you're not adding two houses at much as you are telling the computer what you want it to do when you tell it to "add" these houses, which might be more prone to error than just taking a sqft number and adding another one. Also, it might end up building another house altogether just to tell you how large those two houses are if you can't just inspect those two fields.

It feels like you're hoping someone has invented a language metaphor to explain your point, instead of working on articulating it yourself. You could probably create a toy language with this kind of functionality, but I don't know what it will get you.

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

rjmccall posted:

So many things about Python are well thought out, but I’m always amazed at how incompetent the basic stuff like this is. Like, even JavaScript gets this right.

While they did fix this in python3 so it's unfair to beat them up for this, I still love redefining Boolean constants in python2:

code:
key$ python2
Python 2.7.18 (default, Jul  1 2022, 12:27:04)
[GCC 9.4.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> (True, False) = (False, True)
>>> 1 == 2
False
>>> (1 == 2) == False
False
>>>

KillHour
Oct 28, 2007


Volmarias posted:

So, what exactly are you going to do, even if you find or make this language that doesn't seem to exist? Do you just want some kind of thing to show off to execs, and if so, why would they actually listen to this when they're inviting the rest of it?

I think I can understand what the problem is, I'm just not sure what you feel is going to happen. I wrote a whole metaphor about how objects are forms, primitives are simple fields entries on that form, and object references are "see/attach form 6052" on it. But I'm not sure what that would ultimately accomplish. Are you trying to get them to understand how computers actually work with pointers and stuff?

... Actually, going the opposite direction and showing them C might be a good way to do it. Technically, it's all just Numbers, but you actually mean things with those Numbers, and things like objects adding another object really means that you go into this Add method, and it does THESE specific bits of arithmetic on THESE fields here, and really you're not adding two houses at much as you are telling the computer what you want it to do when you tell it to "add" these houses, which might be more prone to error than just taking a sqft number and adding another one. Also, it might end up building another house altogether just to tell you how large those two houses are if you can't just inspect those two fields.

It feels like you're hoping someone has invented a language metaphor to explain your point, instead of working on articulating it yourself. You could probably create a toy language with this kind of functionality, but I don't know what it will get you.

I work for a company that makes database software. It's not about the language itself, or even translating that to a metaphor to explain to the customers that are getting this wrong. I work directly with customers, but also have a good amount of influence when it comes to our tooling and developer experience. A lot of our tools are low-code and our model definitions are proprietary, so I'm really just looking for inspiration on things we could do, and see if some lessons can be learned.

To go back to my example of SPARQL, when the original draft was written, literals (as value types are called in SPARQL) didn't exist - everything was an IRI (reference). The reason for this is that the IRIs are actually supposed to be dereferenceable to XML documents, and those documents are supposed to have all the literal values themselves. Nobody does this. Instead, literals were (poorly) shoehorned in later when the W3C realized nobody wanted to use RDF the way they intended it to be used (as a way to connect web pages via relationships), and instead wants to put everything in the graph. It's important to say that this is in the context of graphs because there is actually a real, fundamental difference between things you can do with references and things you can do with values in a graph. IRIs can have relationships to other IRIs or literals, but literals can't have relationships to anything. The way they are used is fundamentally different, but instead of understanding that, people do the X/Y problem thing of saying "ugh I can't make literals do what I want so I'm going to make all my literals IRIs!" Or they want to put a graph in a table, but since a graph is an inherently unbounded shape, and a table is a more constrained shape, you have to do dumb poo poo like make your graph entirely out of abstracted "hasProperty" "hasValue" relationships, so you can fit it into a table with those columns (don't do this, good lord).

There's been a ton of ink spilled on this kind of thing in the graph/ontology world that is directly applicable, but the problem with nearly all of it is it was written by either a theoretical mathematician who values conceptual beauty over all else and has never heard the words "computational complexity," or a consulting firm that desperately wants you sell you a bridge to nowhere (*cough* Gartner *cough*).

So I was hoping to find an analogue in the programming language world where people actually care about things like "performance" and "usability" that I could basically look at the conceptual design of how everything works together and take the broad ideas.

Edit: The key of what I'm trying to get to is less on the "value means x and reference means y" side and more on the "these are the ways values are used, and so they're declared, managed, and worked with in this way." SPARQL does this - it's just designed horribly. So I guess you could say I'm specifically asking for a language that tries to do the same thing SPARQL does, but less poo poo.

Double Edit: Here's an example of the insanity you get when you reify everything



This is a single graph. Each shape is an object and each arrow is a relationship. The labels on the arrows refer to the objects with the same name - relationship types themselves (predicates) are IRIs in RDF. It's "logically" separated into conceptual (T-Box) and concrete (A-Box) entities, but in reality it's all just objects. So the schema of your data and the data itself are just in the same place, and you have to do a huge amount of joins to actually cobble together query results. If you're unfamiliar with the data, you actually have to run a query on the graph just to know what the shape of your data should be so you can write the real query. There are whole standards for how to build the T-Box stuff so it's interoperable (SKOS, OWL), and other standards for validating that your A-Box data actually conforms to the T-Box definitions (SHACL), because there is no way to do it at the transaction level. This is by far the most common model design in the industry - it's as slow as you think it is.

Triple Edit: Also, part of me just thinks it would be a neat idea. Would it be that different if you had = to assign by reference and := to assign by value? No, not really. But it would lead to a lot of other design consequences, and it's those that I'm interested in. Maybe you'd really just end up with a slightly different version of C though :shrug:

KillHour fucked around with this message at 06:22 on Oct 25, 2023

KillHour
Oct 28, 2007


Since you sat through my long rant, I'll entertain you with a "hilarious" fact about how bad SPARQL is:

It's written in the spec that if an extension function call within the query has an error, you're not allowed to return an error to the user. You MUST return the empty set as the "result" of the function. You can imagine how much fun this makes debugging complex queries.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

KillHour posted:

That's nothing. Because numbers in Python are objects, you can change them.

https://dev.to/artyomkaltovich/how-the-integer-type-is-implemented-in-cpython-8ei

Python code:
...
:unsmigghh:

Anyways, yeah, I want the programming language that's the opposite of... that.

I am happy to criticise Python but it seems to me that this is in "if you go out of your way to break the language, the language breaks! what the gently caress!" territory. "Doctor, it hurts when I do this!"

It's not just Python that interns objects that are (intended to be) immutable. For example C# does it with strings. But has no need to do it with primitive numeric types because unlike Python it has a concept of value types that live on the stack. I don't know if there is a way to break the string interning like that Python example but maybe there is if you try hard enough?

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

KillHour posted:

Since you sat through my long rant, I'll entertain you with a "hilarious" fact about how bad SPARQL is:

It's written in the spec that if an extension function call within the query has an error, you're not allowed to return an error to the user. You MUST return the empty set as the "result" of the function. You can imagine how much fun this makes debugging complex queries.

Glad I have nothing to do with this environment tbqh

If you have further entertaining examples of how it is bad you should definitely share them in the "programming horrors" thread

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
thinking about it some more... Your inquiry about whether there are languages that have different syntax for assigning value vs reference types

There probably aren't many languages that do this because depending on what the language is intended for, there are design factors that discourage it.

If your language is intended for technical people to use - programmers with a good understanding of the semantics of the language - then it is going to be assumed that knowing the difference between value and reference types is par for the course. A programmer who knows the language can be expected to know the difference and not necessarily need that sort of syntactical distinction to be made for them.

If on the other hand your language is intended for not-so-technical people to use - managers who are going to use it to write reports - then you don't want to bother them with highly technical distinctions like the difference between a value and a reference type. You want to hide that from them, make it so they don't have to worry about it.

so whichever type of language you have, there's a reason to not do what you're suggesting.

It seems like the root of the problem you have is that the technology platform you're using doesn't do a good job of hiding the technical details from people who don't need to understand them. You want the programmers to have to worry about the nerd poo poo, so that the managers just get their reports and are happy. Doesn't sound like that happens in whatever your domain is. "business users making ontologies" is that what they want to do?

Or I could be talking gibberish because I didn't take the time to understand properly, that's possible too.

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

KillHour posted:

So I was hoping to find an analogue in the programming language world where people actually care about things like "performance" and "usability" that I could basically look at the conceptual design of how everything works together and take the broad ideas.

Edit: The key of what I'm trying to get to is less on the "value means x and reference means y" side and more on the "these are the ways values are used, and so they're declared, managed, and worked with in this way."
If you're okay with a rough analogue, I agree with whomever above suggested looking at how Rust does things. I don't think this will help if you're trying to strictly model your domain ontologically because, as you say, languages are interested in things like "performance" and "runtime representations of structures" and other lower-level concerns. The difference with Rust is that the type system makes the programmer think about, per your quote, "these are the ways values are used, and so they're declared, managed, and worked with", rather than it being structly a notational difference.

Per your A-node and T-node example: Here are two structure definitions for a university and a student who studies at some university:

Rust code:
struct Uni {
    name: String   
}

struct Student {
    name: String,
    studies_at: Uni
}
Everything you see here is a value; there are no references in these data definitions here. So, you'd construct a Student by assigning `studies_at` not a reference or a pointer to a Uni but by initializing the structure embedded within the larger structure:

Rust code:
fn main() {
    let s = Student { 
        name: String::new("Lavdim"),
        studies_at: Uni {
            name: String::new("Bonn")
        }
    };
    println!("s = {:?}", s);    
}
Here's a fact about Rust that might surprise you: this code does the same thing:

Rust code:
fn main() {
    let uni_of_bonn = Uni {
        name: String::from("Bonn")
    };
        
    let s = Student { 
        name: String::from("Lavdim"),
        studies_at: uni_of_bonn
    };
    
    println!("s = {:?}", s);    
}
Again, we only have values here, no references. In Rust, in addition to creating and using values, we can move the value. This means the value previously assigned to `uni_of_bonn`, which means after we define `s` `uni_of_bonn` no longer has a legal value, and the compiler will yell at us if we try to make use of it again:

Rust code:
fn main() {
    let uni_of_bonn = Uni {
        name: String::from("Bonn")
    };
        
    let s = Student { 
        name: String::from("Lavdim"),
        studies_at: uni_of_bonn // `uni_of_bonn`'s value is contained here and the variable is undefined now.
    };
    let s2 = Student { 
        name: String::from("Midval"),
        studies_at: uni_of_bonn // error[E0382]: use of moved value: `uni_of_bonn`
    };
    
    println!("s = {:?}", s);    
}
OK, so how do references look in Rust? Rust is a language that cares a lot about memory safety, which means any time you have a reference to some other value the compiler needs to know things like "how long will this data live before it is deallocated" and "is this reference safe for me to mutate"?

Since presumably we want all Students to reference the "same" Uni structure, and we can have any number of Students, a reference-counting container would do the job for us (there are many other ways to do it but I'm just choosing one (rust people yes I could have talked about & but I don't want to talk about lifetimes)):

An Rc is a data structure that owns a value, but lets you assign as many references to that value as you want:

Rust code:
struct Student {
    name: String,
    studies_at: Rc<Uni> // studies_at is a value that owns a ref-counted reference to a Uni
}

fn main() {
    let uni_of_bonn = Uni {
        name: String::from("Bonn")
    };
    let reference_to_bonn = Rc::new(uni_of_bonn);
    
    // Because we have moved `uni_of_bonn` into the Rc, we can only
    // access it through the reference-counted container; trying to use
    // `uni_of_bonn` would give us the same "moved value" compiler error
    // as before.
        
    let s = Student { 
        name: String::from("Lavdim"),
        studies_at: reference_to_bonn // Will these assignments work?
    };
    let s2 = Student { 
        name: String::from("Midval"),
        studies_at: reference_to_bonn //Will these assignments work?
    };
    
    println!("s = {:?}", s);    
    println!("s = {:?}", s2);
}
Here's where I think you would start to get interested since you're interested in notational differences between values and this particular kind of reference: we get the same compiler error, "use of moved value", when we try to construct s2. Why is this? Rc is a reference, isn't it? Remember, we would have _moved_ the Rc into s, but we want to share it between s and s2. The magic incantation to make this happen would be:

Rust code:
    let s = Student { 
        name: String::from("Lavdim"),
        studies_at: Rc::clone(&reference_to_bonn) // Assigns a new reference that points back to the original value...
    };
    let s2 = Student { 
        name: String::from("Midval"),
        studies_at: Rc::clone(&reference_to_bonn)  // Assigns a new reference that points back to the original value...
    };
That's the syntactic difference that you were maybe thinking about when you talked about custom assignment operators for values versus references: In order to satisfy the typechecker I have to correctly make use of the reference count API so I can't confuse "is this a value" or "is this a reference".

That was a lot of words but you can play around with it here https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=37bbd36a42ff4fda919809ce0e7f2ded

KillHour
Oct 28, 2007


Dijkstracula posted:

If you're okay with a rough analogue, I agree with whomever above suggested looking at how Rust does things. I don't think this will help if you're trying to strictly model your domain ontologically because, as you say, languages are interested in things like "performance" and "runtime representations of structures" and other lower-level concerns. The difference with Rust is that the type system makes the programmer think about, per your quote, "these are the ways values are used, and so they're declared, managed, and worked with", rather than it being structly a notational difference.

Thanks for this - yes, this is exactly the kind of thing I'm looking for. It actually does help a lot because "strictly model your domain ontologically" is how management thinks about these things, but they actually do have to run on real databases and return query results, and that's where everything breaks down. The industry has run full-steam towards "have your business users model their domains" without considering that there still needs to be a performant data model underlying the ontology.

The Rust example is actually really good because a huge issue with ontologies is that it's practically impossible to validate that a graph stays internally consistent at the transaction level - your T-Box can change, making your A-Box data invalid. A schema would normally prevent this because it would throw an error on reindex and roll back, but graphs can't do that with the tooling that exists. Instead, you have to regularly run expensive SHACL testing to report on what is broken in your data after the fact.

I'll have to do more digging to see what concepts transfer, but thanks for that detail!

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

Glad it was useful!

KillHour posted:

it's practically impossible to validate that a graph stays internally consistent at the transaction level - your T-Box can change, making your A-Box data invalid.

Yikes, sounds like you don’t really have “transactions” in any real sense here. I assume you have the same problem even with A-boxes: what happens if I delete a Student concurrently with you associating that Student with a School?

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Rust isn’t doing anything in this area that C isn’t. The way you make a reference type is by indirecting a value type with one of the generic pointer types (e.g. Rc). Basic value operations on the reference still look like any other value manipulation, though.

The data design question OP is raising is something you can do in basically any database — you can always denormalize more, store more metadata about individual observations, etc., until you’re way up your own rear end storing metadata about metadata. Generally you get out of this spiral by recognizing that it’s possible to do but has costs (which of course it does in any database), then tie it back to a product to ask what this stuff is enabling.

some kinda jackal
Feb 25, 2003

 
 
I'd like some advice on best practices and patterns for knowing how to model objects in software, balancing detail and complexity.

I've re-written this post like nine times because I'm either going into too much detail, or not enough detail, or I start to ramble and go on tangents. I'm trying again with succinctness in mind and can provide any additional info to help steer my research as needed.

I want to create an app that represents a real world item, maintains a parameter state for it, and interacts with that object.

For all intents and purposes a virtual machine is the perfect example of what I'm trying to do:
- models real world item: computer
- maintains parameter state: define cpu, disk, network, etc
- interact with it: launch vm, terminate vm, interact with vm

I can create one giant model that is just

class computer {
- id
- name
- cpu1Type
- cpu1Speed
..
- disk1Capacity
- disk1BackingFile
..
- nic1Speed
- nic1Network
..
}

but I think we'd all agree this is horrible.

I can pull cpu out into its own class:

class cpu {
- id
- type
- speed
}

and then instantiate as many cpus as I want in my computer object which is probably what I want inn almost all cases.

These examples are pretty clear cut, but I'm falling down this hole where I feel like I want to extract literally every property from my Computer class to ridiculous extremes like having a "name" class or a "colour" class rather than just using strings. These examples are also pretty clear cut overkill, but I guess the real struggle I have is that I don't understand the best practice or oop patterns when it makes sense to externalize a property vs not. At least not in any way that I can consciously apply to anything that isn't "very obvious".

Does this kind of make sense? I'm still not sure I articulated a clear question, but I'm not rewriting this a tenth time so i'm more than happy to expand on anything I wrote.


For some context:
- I am not a developer except when bash being too clunky forces my hand into writing small googled code snippets of python or go for small utility apps
- My goal project is to write an API which will launch a docker container with a "configuration" variable that is a json representation of the object I'm trying to describe above. I can have N number of configurations and I would pass a configuration Id as a variable to launch this container, or I could modify any given configuration via the API to change its parameters.
- Target language is golang but I haven't actually put any pen to paper yet since I'm struggling with the fundamentals.

KillHour
Oct 28, 2007


Dijkstracula posted:

Glad it was useful!

Yikes, sounds like you don’t really have “transactions” in any real sense here. I assume you have the same problem even with A-boxes: what happens if I delete a Student concurrently with you associating that Student with a School?

Transactions are lower level than the graph and I'm not going to get into how we specifically do it because it would make it very obvious where I work, but that specific example can't happen in RDF.

The graph is made up of "triples" which are a set (in the mathematical sense) of facts of the form [subject] [predicate] [object], where subject and predicate are both IRIs* and object is either an IRI or a literal.

So the smallest piece of data you could have is something like
code:
<www.example.com/obj#student1> <www.example.com/def#attends> <www.example.com/obj#school1>
That triple existing by itself means that student1, attends and school1 are all objects in the graph. The only way to "delete" an object is to delete all the triples that reference that object. Technically, all of those are supposed to dereference to an xml document, but they often don't and the graph doesn't care. There's actually no way in a graph to define an object without also defining a relationship of some kind (although that relationship could simply be a type definition like "student1 is a Student" - hence types being data in the graph).

Yes, that DOES mean it's very easy to end up with facts about a specific object going all over the place, including split across hosts in a cluster! How astute of you to notice. :v:

*Technically, subject can also be a "blank node," which is anonymous (similar to an anonymous function in other languages except it's an object - kind of like a tuple, I guess?), but ignore that here.

KillHour fucked around with this message at 17:56 on Oct 25, 2023

nielsm
Jun 1, 2009



rjmccall posted:

Rust isn’t doing anything in this area that C isn’t. The way you make a reference type is by indirecting a value type with one of the generic pointer types (e.g. Rc). Basic value operations on the reference still look like any other value manipulation, though.

The thing I see different is that in C you can just assign a pointer to another pointer type variable, and now you have two pointers to the same thing. While in the Rust example Dijkstracula gives, making two references to the same object requires being explicit in the code about them being copied references.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

nielsm posted:

The thing I see different is that in C you can just assign a pointer to another pointer type variable, and now you have two pointers to the same thing. While in the Rust example Dijkstracula gives, making two references to the same object requires being explicit in the code about them being copied references.

Right, ownership and the decision to not have implicit copies does make some difference here. But when you’re just moving the reference around, including as a function argument, the syntax is all the same; and the way you compose them as types is absolutely the same, just with more knobs because of ownership.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Some things you could do in trying to grapple with this problem:

- think about the levels of abstraction in your program and in its problem domain
- think about the distinct areas of responsibility that exist at each level of abstraction
- recognize when you are writing repetitive code

Figuring out how to divide up programs in a way that makes them capable, but easily maintainable, is a difficult skill to learn and not everyone has the same opinions on how to do it! And it's important as soon as you start writing programs of non-trivial size.

In your computer example it is certainly bad to have those "cpu1speed, cpu1type, cpu2speed, cpu2type..." properties, like you said. You can figure that out in the first place because you notice that you're writing repetitive code. You can address the problem by recognizing that there's an area of responsibility, which is to say the responsibility of knowing about the details of a CPU, and extracting a class that has that responsibility.

On the other hand if you have a "name" class that's just a string and nothing more, that's not adding anything above using a string. But if it does more than just what a string can do, it still might make sense to have that "name" class - if it has more responsibilities. For example, if the name is a code that encodes some information about the named thing. Does that make sense?

ultrafilter
Aug 23, 2007

It's okay if you have any questions.



You're trying to build a domain model, and that's more an art than a science. There are some resources out there, but ultimately it's something you have to practice in order to get. There are a couple principles that I find useful:
  1. The types in your model are the nouns that you can use in your sentences, and their properties are adjectives that you can use to describe them. What do you want to be able to talk about? Make those things into their own classes.
  2. We can only keep about seven things in mind at any given time. If you find yourself making objects that have many more properties than that, it's a sign that you need to find some way to group them.

some kinda jackal
Feb 25, 2003

 
 
OK great insight so far. Let me get your thoughts on this, still following my computer example, but using maybe less ridiculous examples like extracting name.

In a real world situation, would it be reasonable to have something like this?

class computer {
- id
- name
- desc
- hardware[]: hardware
- options[]: option
}

class hardware {
- id
- device[]
}

abstract class device {
- id
- device type
} -> extended by cpu, video, disk, etc

class option {
- id
- description
- hypervisorFlag
}

where you've now abstracted hardware into its own class, but that class is functionally just a container for device objects. What I'm hearing is that this is all really dependant on how you plan to interact with your domains. So if you could have different configurations for each "computer" where you say "launch computer with hardware configuration 1 vs 2" then it may make sense to extricate hardware into its own class, but if your hardware configuration is fixed then maybe it makes more sense to just have hardare[] be a container of "device" objects instead?

In my example above I just realized it's probably not too logical to extract options/flags into its own class vs just using a dictionary object or something but I'll leave it there.




@ultrafilter, the decomposition into verb/adj/topic makes a lot of sense to me and I think reinforces some thinking I have about the above in terms of what exactly am I trying to DO with each of these items.

@Hammerite Also extremely sensible. I almost wished I'd used the above instead of my name example because it's probably more representative of the nuance I'm grappling with,


In my instance I have a complicated mainframe I'm trying to replicate in object form so I have a pretty complicated tree like mainframe.configurationID.hardware.ioAddress.attachedDevice.deviceFileAsset.deviceFileAssetOptions to describe a read/write flag (example) on a disk attached to an address attached to my instance of a fake mainframe. This whole post was made because I had the thought "yeah that makes sense in the real world because that's the chain, but is it being overly complex?" like.. I could easily get away with mainframe.configurationID.hardware.device and have that device object specify its assigned address, its assets on disk, and the flags associated with that asset.

After reading above I think I need to understand what I want to do.

If I want to be able to attach and detach different devices from specific addresses, or to have addresses with no devices attached, then it makes sense to make those separate. If I want to attach that device to a different mainframe instance then it makes sense to not attach it to a specific address which may or may not conflict with another device somewhere.

I can describe this mythical mainframe down to the atomic level but does it make sense? And I'm now understanding that (common sense aside) absent a good reason to, no.. it doesn't.

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

rjmccall posted:

Rust isn’t doing anything in this area that C isn’t. The way you make a reference type is by indirecting a value type with one of the generic pointer types (e.g. Rc). Basic value operations on the reference still look like any other value manipulation, though.

The data design question OP is raising is something you can do in basically any database — you can always denormalize more, store more metadata about individual observations, etc., until you’re way up your own rear end storing metadata about metadata. Generally you get out of this spiral by recognizing that it’s possible to do but has costs (which of course it does in any database), then tie it back to a product to ask what this stuff is enabling.
I wasn't interpreting their question to be about data modeling, but specifically the "what would a language look like if operations references were syntactically distinct from values" - and I did make it clear that it's a separate matter from the ontological modeling that their product is doing...

Foxfire_
Nov 8, 2010

rjmccall posted:

So many things about Python are well thought out, but I’m always amazed at how incompetent the basic stuff like this is. Like, even JavaScript gets this right.
I don't know that I'd call it wrong wrong.

From the historical context of its original scope of "one person project as a shell script type replacement with no aspirations to execute quickly", I think going with the uniformity of everything is a python object and there's no Java style boxing is an ok decision.

Python's never going to get Javascript style hidden class interpreter optimization heroism effort, so it'll always run like a potato because of its type system, so favoring consistency over speed/ram seems reasonable. If boxing overhead matters, you probably shouldn't be using python anyway.

It should have probably tried harder to stop you from modifying 'immutable' objects, but you still have to be pretty actively trying to blow things up

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.
Programming language often recommended as the first for a newbie to learn: = and is both work for small numbers but not for big numbers.

Python defender: why are you actively trying to blow things up???

RPATDO_LAMD
Mar 22, 2013

🐘🪠🍆
using C pointer-arithmetic fuckery to overwrite read only data you're not supposed to have access to can blow things up in any language with a runtime

hell you can even blow things up without a runtime by corrupting your stack frame

12 rats tied together
Sep 7, 2006

pokeyman posted:

Programming language often recommended as the first for a newbie to learn: = and is both work for small numbers but not for big numbers.

Python defender: why are you actively trying to blow things up???

the person who said that was quoting the guy who was redefining what numbers are by reading where they were in memory and changing them

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

12 rats tied together posted:

the person who said that was quoting the guy who was redefining what numbers are by reading where they were in memory and changing them

Earlier they were backing up the thing I was talking about.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Foxfire_ posted:

Python's never going to get Javascript style hidden class interpreter optimization heroism effort, so it'll always run like a potato because of its type system, so favoring consistency over speed/ram seems reasonable. If boxing overhead matters, you probably shouldn't be using python anyway.

Python is not some minor hobbyist language. It has a huge user base, including a lot of projects that very much care about Python, which is why a ton of effort has gone into reimplementing Pytthon over and over again. The problem really is just that CPython is an exceptionally lazy language implementation which has also managed to make almost every imaginable mistake to permanently lock in its shittiness.

Foxfire_
Nov 8, 2010

At the point where "Every python object will be a full Python object with all the functionality and overhead that implies. There will be no Java-style distinction between a primitive type that does not inherit from object and boxed wrapper type that does" got decided it was a minor hobbyist language. They certainly could have gone back and changed that in one of the big breaking revisions, but I think leaving it alone is defensible.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

some kinda jackal posted:

OK great insight so far. Let me get your thoughts on this, still following my computer example, but using maybe less ridiculous examples like extracting name.

In a real world situation, would it be reasonable to have something like this?

class computer {
- id
- name
- desc
- hardware[]: hardware
- options[]: option
}

class hardware {
- id
- device[]
}

abstract class device {
- id
- device type
} -> extended by cpu, video, disk, etc

class option {
- id
- description
- hypervisorFlag
}

...

In my instance I have a complicated mainframe I'm trying to replicate in object form so I have a pretty complicated tree like mainframe.configurationID.hardware.ioAddress.attachedDevice.deviceFileAsset.deviceFileAssetOptions to describe a read/write flag (example) on a disk attached to an address attached to my instance of a fake mainframe. This whole post was made because I had the thought "yeah that makes sense in the real world because that's the chain, but is it being overly complex?" like.. I could easily get away with mainframe.configurationID.hardware.device and have that device object specify its assigned address, its assets on disk, and the flags associated with that asset.

After reading above I think I need to understand what I want to do.

If I want to be able to attach and detach different devices from specific addresses, or to have addresses with no devices attached, then it makes sense to make those separate. If I want to attach that device to a different mainframe instance then it makes sense to not attach it to a specific address which may or may not conflict with another device somewhere.

I can describe this mythical mainframe down to the atomic level but does it make sense? And I'm now understanding that (common sense aside) absent a good reason to, no.. it doesn't.

I think you're on a path that will work for you. I don't really understand what your mainframe.configurationID.hardware.ioAddress.attachedDevice.deviceFileAsset.deviceFileAssetOptions is supposed to represent. Do all of those dots represent the same type of relationship?

If I understand correctly, then each element of computer.hardware represents one possible hardware-configuration for that computer. Does it really make sense for the "options" to be separate from that? Or is a set of options, in practice, associated with a particular hardware-configuration? Maybe the answer is "yes, they're totally independent". I don't know enough about the problem-domain to say.

Are there constraints on what a sensible instance of class "hardware" looks like? For example, should there always be at least one CPU? If so, maybe that's part of the responsibilities of the hardware class.

some kinda jackal
Feb 25, 2003

 
 

Hammerite posted:

I think you're on a path that will work for you. I don't really understand what your mainframe.configurationID.hardware.ioAddress.attachedDevice.deviceFileAsset.deviceFileAssetOptions is supposed to represent. Do all of those dots represent the same type of relationship?

If I understand correctly, then each element of computer.hardware represents one possible hardware-configuration for that computer. Does it really make sense for the "options" to be separate from that? Or is a set of options, in practice, associated with a particular hardware-configuration? Maybe the answer is "yes, they're totally independent". I don't know enough about the problem-domain to say.

Are there constraints on what a sensible instance of class "hardware" looks like? For example, should there always be at least one CPU? If so, maybe that's part of the responsibilities of the hardware class.


You got it. Essentially in that dot dot example mainframe base class contains a configuration which contains a hardware container which contains multipe io addresses which contains ... etc. The specific dot dot dot example I gave is how complicated I've made it right now, and the classes I pasted are my newer line of thinking. I actually forget exactly what was in my head when I was typing out that last example and I've probably had five or six changes of heart about how it's formatted so I'm going to say that the feedback I have on my thought process is moving in the right direction and I probably have enough information to actually come up with something that works now that I know I'm not out on mars. I'm definitely working to simplify and start condensing where it makes sense, and still extract where it makes sense to have items that need more flexibility than being baked in.

Agrikk
Oct 17, 2003

Take care with that! We have not fully ascertained its function, and the ticking is accelerating.
How do I use the "netplan set" command on a Ubuntu server box to add nameservers and a search domain to the netplan yaml file?

I have a netplan yaml file like this:
code:
network:
    ethernets:
        ens5:
            dhcp4: true
            dhcp6: false
            match:
                macaddress: 02:24:e6:01:19:d3
            set-name: ens5
    version: 2
and I would like to add three name servers and a search domain so that the file looks like this:

code:
network:
    ethernets:
        ens5:
            dhcp4: true
            dhcp6: false
            nameservers:
                addresses [10.0.0.1, 10.0.0.2, 10.0.0.3]
                search: [int.mydomain.com]
            match:
                macaddress: 02:24:e6:01:19:d3
            set-name: ens5
    version: 2
I'm trying to do stuff like:

netplan set ethernets.ens5={nameservers.addresses: [10.0.0.1, 10.0.0.2, 10.0.0.3]}

but I keep getting this error: "Error parsing YAML: did not find expected node content"

The internet is full of people showing how to use nano to edit the YAML file over SSH but I need to do it programmatically.

12 rats tied together
Sep 7, 2006

you're probably missing a : for addresses. a good idea would be to paste your yaml into a web editor of some sort or parse it in a programming language to make sure that it looks like what you expect

what you have is technically valid yaml but the value of it is probably not what netplan wants. the error message could be better

Agrikk
Oct 17, 2003

Take care with that! We have not fully ascertained its function, and the ticking is accelerating.
I ended up breaking it into individual commands:

sudo netplan set ethernets.ens5.nameservers.search=[int.mydomain.com]
sudo netplan set ethernets.ens5.nameservers.addresses=[10.0.0.1]
sudo netplan set ethernets.ens5.nameservers.addresses=[10.0.0.2]
sudo netplan set ethernets.ens5.nameservers.addresses=[10.0.0.3]
sudo netplan apply

which produced

code:
network:
  version: 2
  ethernets:
    ens5:
      match:
        macaddress: "xx:xx:xx:xx:xx:xx"
      nameservers:
        addresses:
        - 10.0.0.1
        - 10.0.0.2
        - 10.0.0.3
        search:
        - int.mydomain.com
      dhcp4: true
      dhcp6: false
      set-name: "ens5"

12 rats tied together
Sep 7, 2006

fwiw i did some brief research into netplan to see if they had a yaml schema that would explain the problem and i instead found that they hand wrote and bundled a 3000+ line yaml parser in C instead of using a library and closed the window in disgust

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

12 rats tied together posted:

fwiw i did some brief research into netplan to see if they had a yaml schema that would explain the problem and i instead found that they hand wrote and bundled a 3000+ line yaml parser in C instead of using a library and closed the window in disgust

their yaml parser does use libyaml (it uses yaml.h), they didn't entirely roll their own.

that said, i'm not sure why they wrote libnetplan in C - that project seems like it would have been much easier to implement in a language that has reflection.

MrMoo
Sep 14, 2000

I’m sure someone will want to rewrite it in Rust soon enough. It’s not exactly a library you want a tonne of heavy dependencies on.

Adbot
ADBOT LOVES YOU

boofhead
Feb 18, 2021

Huh.. is there a way to see what organizations github thinks you're a member of, aside from profile > your organizations (also accessible via settings > access > organizations)?

I was removed from my old job's organization a few weeks ago, and github correctly shows that I'm not a member of any organization, but I just had to re-auth github in vs code on my laptop and when it popped up the oauth site to give authorization, it says under "my organizations" that my old company already approved this app. I assume there's some legacy scope record there somewhere..? I already revoked all my app/oath permissions in github once I first saw it, and reinstalled vscode and so forth, and even temporarily created and deleted a new organization, but it's still showing up

Any idea what's going on? I think it just means "your (old) organization already approved access to this app so you don't need to request it again" but since I'm not a member of that organization and thus can't leave it.. I'm wondering what's going on and if/how I can cut ties completely. This is my personal account but when I was working for them they invited me to their organization

Incidentally, what's the standard approach -- do people create new github profiles for each new job (using the company email) or do you have each new job invite you to the org?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply