2,777 karma · joined March 29, 2011
Website: https://yorickpeterse.com
In terms of installation size it's not a problem either, as one can verify using `flatpak list --columns=size,name`:
1.3 MB Flatseal
2.9 MB Extension Manager
47.7 MB Celluloid
604.6 MB Freedesktop Platform
680.0 MB Freedesktop Platform
533.8 MB Mesa
533.8 MB Mesa (Extra)
469.8 MB Mesa
469.8 MB Mesa (Extra)
46.9 MB Intel VAAPI driver
50.9 MB Intel VAAPI driver
20.3 MB FFmpeg extension with extra codecs
790.0 kB openh264
763.9 kB openh264
243.7 MB GNU Image Manipulation Program
7.7 MB HEIC
17.6 MB Characters
14.2 MB Connections
25.4 MB Image Viewer
946.7 kB HEIC
25.5 MB Sushi
39.8 MB Papers
941.3 MB GNOME Application Platform version 46
1.0 GB GNOME Application Platform version 47
794.1 kB Fonts
137.7 MB gThumb Image Viewer
1.1 MB adw-gtk3 Gtk Theme
269.6 MB Firefox
482.3 MB Signal Desktop
The duplicate entries is because certain Flatpaks may require different versions
of e.g. the Freedesktop platform (that being possible is one of its big selling
points).In short, storage isn't a problem at all for any computer produced in the last 20 years.
In contrast, a migration written in an actual programming language can handle all such cases. Depending on the amount of abstractions applied, it can also look close enough to a declarative language (in other words, it doesn't have to be verbose).
So yes, the limitation I'm implying very much is a limitation of SQL (or really any declarative query language for that matter). It has nothing to do with the data model as it applies equally well to using e.g. MongoDB, Redis, or really anything else that stores a pile of data you may want to transform in complex ways.
This works until you need something beyond the trivial "UPDATE table SET column1 = column2", such as "Update the values in column X with the contents of file Y that exists in Amazon S3", or really anything else you can't express in SQL.
> Another bonus (and I’m sure I’ll be told I’m wrong) is that you don’t need to write tests for the migration, because it’s declarative.
This indeed is wrong, and essentially comes down to "It looks simple so it's correct". A SQL query "DELETE FROM users" might be correct, but if you meant for it to be "DELETE FROM users WHERE id IN (...)" it's going to cause problems.
In other words, at least for data migrations you absolutely have to write tests or you will run into problems.
> I didn't get along well with this manager, The resulting conflict lead to a "performance enablement plan", a procedure meant to get things back on track before the need for a "performance improvement plan" (PIP). A PIP is meant to be used as a last attempt at improving the relationship between an employee, their work, and their employer.
Not getting along with someone doesn't imply or mean that a person is difficult to manage. Instead, it means there's simply friction between two people.
In this specific case, the main source of friction was that my messed up working hours resulted in me performing tasks later than expected (though still within any deadlines), though I recall those time frames not being well specified to begin with (i.e. it was more of an implicit assumption that X would be done by hour Y).
I believe I was also a little late for a meeting because I'd overslept. That's not good, but it certainly isn't a case of "Wow this person is so difficult", instead more of a case of "This person needs to get his schedule back together".
Either way, you seem to be interpreting the story in a way different from what's written down. I doubt I can change that, so I'm going to leave it at this comment.
Because colleagues have told me so, and the annual employee reviews were positive as well. In fact, outside the PEP the only actionable/to-improve feedback I got was essentially "Sometimes you can be a little harsh/blunt", which is vastly different from "this person is difficult to work with".
Project.limit(10).each do |project|
project.members.count
end
Here `project.members` returns some sort of query object that produces a query along the lines of this: [...] FROM members WHERE members.project_id = X [...]
In other words, you can just query associated data on a per-project basis, without needing to pass any additional arguments.The problem this results in is that in the above code you'd run a COUNT query for _each_ project. Instead what you'd want is a single COUNT that groups data per project, such that you can then pass that data along with the `each` call. This setup would only require 2 queries, instead of 20.
To eager load data in Rails you have to explicitly opt-in, resulting in something like this:
Project.includes(:members).limit(10).each do |project|
project.members.count
end
The problem here is that an opt-in mechanism is too easy to forget (as is evident by how common these N+1 query problems are), and even if you include it there are certain cases where you still end up running extra queries for each row.The solution here is to separate querying from the row instance types, e.g. a "Project" type can't query data itself and instead requires it to be passed in. This makes it much more difficult to create N+1 query problems.
To put it differently, try to think of an application that does all the things you _don't_ want to be doing if you're going to (or want to) shard. Then imagine that that's basically GitLab, coupled with the load patterns simply not justifying it.
I can also guarantee you that the work I did very much did good work instead of "cause problems". Feel free to ask anybody that worked at GitLab during the same time (or is still there) and see for yourself :)
Small nitpick/pedantics: the person in question wasn't a database administrator, or a junior developer as some sites reported at the time. They were a senior developer that happened to be part of a (too) small database focused team.
Another motivator for that is that years ago I worked on Rubinius for a while (an implementation of Ruby), and helped out with a parser for Ruby (https://github.com/whitequark/parser). The Ruby developers really liked changing their already impossible syntax in even more impossible ways on a regular basis, making it a real challenge to provide syntax related tools that support multiple Ruby versions. I wanted to avoid making the same mistake with Inko, hence I'm actively trying to keep the syntax as simple as is reasonable.
As for the specific examples:
`fn async` means your parser only needs to look for `A | B | fn` in a certain scope, instead of `A | B | fn | async fn`. This cuts down the amount of repetition in the parser. An example is found at https://github.com/inko-lang/inko/blob/8f5ad1e56756fe00325a3..., which parses the body of a class definition.
Skipping parentheses is directly lifted from Ruby, because I really like it. Older versions took this further by also letting you write `function arg1 arg2`, but I got rid of that to make parsing easier. It's especially nice so you can do things like `if foo.bar.baz? { ... }` instead of `if foo().bar().baz?()`, though I suspect opinions will differ on this :)
Until recently we did in fact use `::` as a namespace separator, but I changed that to `.` to keep things consistent with the call syntax, and because it removes the need for remembering "Oh for namespaces I need to use ::, but for calls .".
`[T]` for generics is because most editors automatically insert a closing `]` if you type `[`, but not when you type `<`. If they do, then trying to write `10<20` is annoying because you'd end up with `10<>20`. I also just like the way it looks more. The usual ambiguity issues surrounding `<>` (e.g. what leads to `foo::<T>()` in Rust) doesn't apply to Inko, because we don't allow generics in expressions (i.e. `Array[Int].with_capacity(42)` isn't valid syntax) in the first place.
header
size
capacity
data ptr
Where `data ptr` is a raw pointer to data that is realloc'd as the array grows. ptr to array --> array
header
size
capacity
[
val1 ptr --> val1
val2 ptr --> val2
]
At some point I'd like to support stack allocated values as an optimization,
including the ability to allocate values directly into arrays, but that's not a
priority at this point.In addition, for objects other than String, Channel and processes, the count is just a regular increment (i.e. `count += 1`), not an atomic increment.
It's true that this can have an impact on caches, as the count increment is a write to the object, and so over time I hope to add optimizations to remove redundant increments whenever possible.
As for the borrow checker: Inko doesn't have one, at least not like Rust (i.e it's perfectly fine to both mutably and immutably borrow a value at the same time), so the cost of that isn't present.
"unwrap_or" could perhaps be called "unwrap_or_use_default", but that's a tad too much to type. What it basically does is this:
match the_value {
case Some(val) -> val
case _ -> 0
}
i.e if you have a Some, you "unwrap" its value and return it, otherwise you return a default value.