ristos
You can't do length + 1 as a single expression in any language though, in python for example len(ls) + 1 is two expressions, but I get what you mean, it is a little less terse. But those commas in prolog are super useful. And you can make your example bidirectional too:
``` :- use_module(library(clpfd)).
foo(List, X) :- length(List, Out), X #= Out + 1. ```
``` ?- foo(X, 3). X = [_, _] .
?- foo(X, 3), X = [a,b]. X = [a, b] . ```
-----
No standarized collection types:
I think that's a feature rather than a bug. The ability to just use infix and compound terms in a generic way without it being tied to a clause or operator is a huge feature. For example:
``` pets(dog-rex-poodle, dog-fluffy-bishon). ```
That defines the collection in a semantic or mathematical sort of way, then you can pass that into whatever data structure you want, whether it's a ordered map or a hashmap or whatever.
----
No boolean values:
That's also a feature in prolog, same as having no null. It's the same sort of motivation for Haskell's Maybe and Either types or Rust's Option and Result types, same kind of usefulness.
----
Cuts are confusing:
Conditionals don't need to use cuts, the modern prolog way of doing it is using the reif library:
``` :- use_module(library(reif)).
if_(Cond, Then, Else) ```
For example:
``` :- use_module(library(clpfd)). :- use_module(library(reif)).
sign(X, S) :- if_(X #> 0, S = pos, S = nonpos). ```
---
Non-cuts are confusing:
This isn't doing what you're thinking:
``` \+ (A = B) ```
It's unifying A with B, `A = B` unifies. You want `dif(A, B)`.
----
Straying outside of default queries is confusing:
It might be tempting to use bagof, but it's not monotonic. You should try to write as much prolog code as you can in a pure, monotonic way, so your code can take advantage of prolog's unique advantages, like monotonic debugging.
Check out the SWI prolog discourse group, there's a lot of good stuff in there and everyone's very supportive to newbies. And Markus Triska's website and youtube channel Power of Prolog is a super useful resource for all this stuff, a good place to go if you want to know what sets prolog apart. A lot of prolog's usefulness doesn't show up as an embedded language either, like minikanren isn't "pretty much prolog", it lacks a lot of stuff that prolog offers. Multiparadigm programming languages are also really popular now, but a lot of what sets programming languages apart isn't what they can do, it's what they can't do, what sort of paradigm or way of thinking they force you to adopt.
I'm pretty bummed out to hear about all of this. I thought the suckless style software was pretty cool, easy to grok, minimalist. I use a lot of it. Granted, I've been planning on translating all of the minimalist C software I've been using to R7RS scheme, so maybe this just gives me more motivation to get started on porting my stack to that.
Someone in an earlier thread said something like, if he avoided software written by assholes then he would have a hard time using his computer, something like that. I think that's kind of true, but there are definitely red lines when it comes to violent ideologies.
I found out I'm a slow metabolizer of caffeine, a cup of coffee will last like 16-18 hours for me, even though the half-life is supposed to be 3-7 hours for adults, which makes coffee a more dangerous drug for me because it'll disrupt my sleep more than the average joe. I still like coffee but I have to be more careful than most people.
That being said I'm pretty sure cannabis is horrible for the developing brain. I remember one anecdote of someone who told me they would get really high when they were 14, and would get so high that they would literally black out, like have no idea what just happened. Probably best to be avoided until adulthood. Nobody when I was in college or older ever experienced anything like that, just the usual report of infrequent users forgetting what they were just talking about or what someone else just said to them, the whole short term memory loss thing.
That's probably the difference between how cool people use technology vs nerds or dorks. When I use my phone, it's either to look something up, study or learn something new, read the news, or talk to my mom. I would imagine I probably don't look cool using my phone.
Joking aside, I think one thing that I really don't like about smartphones and texting entering our lives is that when you're out for food or coffee people get on their phones, and it feels antisocial, like you can't talk to them now without interrupting them from whatever they're doing on their phone. In the past, I remember, if people had nothing to say, they would just sit there and look off into the distance. It all felt way more social back then, when people got together they interacted more.
The issue isn't around tasks that are predictable in nature and therefore easy to estimate with a small margin of error, it's around complexity in software, unforeseen things, bugs, etc, which can compound for larger long term projects.
If engineers give estimates close to what it would be if everything goes right, then they risk overpromising and underdelivering if something goes wrong (hofstader's law). They might've just wanted to do the right thing by saving the company money and time, but in the end they footgunned themselves.
Or engineers intentionally over-estimate in order to manage the complexity, but then you end up with a lot of padding and parkinson's law. Because as soon as the engineer starts underpromising and overdelivering consistently, management will pressure them to lower their estimates because they have a track record of doing that, so instead they're incentivized to pad and then fill up the entire time they estimated even if it took less time.
Sprints were probably invented in order to deal with some of these issues, so that people just work with a bunch of smaller tickets that are much easier to estimate, with the more complex long term estimates going to management, which are incentivized to get it right because they're shareholders. That often leads to micromanagement and burnout, and it doesn't fix the padding/overestimation issue either, it might even amplify it in a lot of cases.
People here mention giving ranges or probability distributions, and have also equally mentioned that they don't work because management wants a single number, or management just assumes the best-case or middle-case of the range as the actual estimate, and then they still get in trouble for giving ranges and it didn't solve anything. It also doesn't solve the problem of unanticipated setbacks, the whole you don't know what you don't know thing, which can only really be solved culturally in some way.
While there are certainly bad managers that want to squeeze their workers, a lot of the time management is probably also pressured to give estimates and that's why they want and need that accuracy, because they're pressured by investors and clients that want to know how much time and money something will cost.
Overall the entire problem is a system cultural issue around managing complexity.
If I were interviewer, I'd probably do a coding assignment that builds on an already existing mini codebase, quarter or half day assignment (2-4 hours), with a strict time limit to turn it in by the end of the allotted time. It would test for someone's ability to read unfamiliar codebases, with the codebase being reasonably small for the little time that's available, and the ability to build features on top of it, to write clean readable code compatible with the codebase, etc. And an a 30-60 minute more open-ended informal knowledge interview to gauge how knowledgeable the candidate is and where their strengths and weaknesses are. And previous work would probably be a good indicator, like open source work and/or previous job experience.
I'd like to eventually write something like this, there are a lot of things it would enable that you just can't do with emacs. It would be an emacs killer if implemented well, but it would also open up a whole new set of possibilities.
I don't know how far I'd get just working on it on my free time, which is competing for time with other projects. I'm looking for funding for these sorts of projects. If anyone reading this is an investor and interested in funding this sort of thing, please reach out.
I wouldn't say that the logic or relational way of describing effects is a bad thing either. By design it allows for multiple return values (foo/1, foo/2, ...) you can build higher level predicates that return multiple resources, which is pretty common for many programs. It makes concatenative (compositional) style programming really straightforward, especially for more complex interweaving, which also ends up being quite common. Many prolog implementations also support shift/reset, so that you can easily build things like conditions and restarts, algebraic effects, and/or debugging facilities on top. Prolog is also homoiconic in a unique way compared to lisp, and it's quite nice because the pattern matching is so powerful. Prolog really is one of the best languages I ever learned, I wish it was more popular. I think prolog implementations need a better C FFI interop and a nicer library ecosystem. Trealla has a good C FFI.
I think logic programming is the future, and a lot of these problems with prolog are fixable. If it's not going to be prolog, it'll probably be something like kanren and datalog within a lisp like scheme or clojure(script).
This is a great resource for getting a good feel of prolog: https://www.youtube.com/@ThePowerOfProlog/videos
I'm looking to use prescheme for low level code that needs more precise control where chibi scheme or guile isn't a good fit. I'm also hoping at some point that I have the time to write a borrow checker on top of prescheme, which might be a subset of prescheme but would still fit many use cases, so that it can be used instead of Rust. And also to create a transpilation step that creates optimizations, that would then ideally be proved. And then that way the borrow checked code with optimizations can be all transpiled by prescheme into C, and then tiny c compiler can compile it unoptimized into straightforward assembly. The goal would be to have verified code that can also be easily hand audited as well. LLMs just make all this stuff so much more possible because of how much faster we can iterate. Fun times for coding!
What ended up happening in practice is just that time was saved on grunt work, and really engineers just ended up working on composing and debugging higher level components, optimization, or low level work.
Can you elaborate on your example here? Couldn't they just do something like replace oldGalleryAppOpensOrWritesToAnyPlace() to askPermissionsForWhichFilesAndFoldersCanBeWrittenTo() or ifSensitiveDirIsNoLongerAccessibleAskPermissionOnWhichFolderToUseForCompat() ?
> The problem is that you invariably have to think about types. If you mistakenly pass a string to a function expecting an integer, you better hope that that is properly handled, otherwise you risk having type errors at runtime, or worse—no errors, and silent data corruption.
The silent data corruption is really only a problem with weak dynamic typing, that usually automatically coerces types. A lot of dynamically typed languages still have strong typing and will immediately error out. And usually in practice you end up testing all of the code you're writing anyone, so this almost never happens in practice except when someone is refactoring without thoroughly testing what they did, which should be done anyway whether there are types or not.
> Primarily that if specifying types is optional, most programmers will not bother, or will just default to `any` to silence the type checker.
They probably do bother when it's an important module, or a an edge boundary that needs to be documented with a contract, or during times of significant refactoring. And these days LLMs can generate the specs, optional types, and tests pretty easily for any sort of self-contained, modular, reasonably well written code.
So now with LLMs I think there's even better reasons to use dynamic typing. And type completion in an IDE still exists for a bunch of dynamically typed languages anyway, like javascript.
Wouldn't it depend on the type? Something like:
int p; p = &x;
MOV @R1, R2 ; R1 contains the address of x, move it to pointer p in R2
int p; int value = *p;
MOV @R2, R0 ; Dereference pointer p (in R2), load the value into R0 (int value)
int x = 5;
MOV #5, -(SP) ; Push the value 5 onto the stack (stack-allocated int)
int x = 10; int y = x + 5;
MOV #10, R0 ; Load the immediate value 10 into register R0 (for x)
ADD #5, R0 ; Add 5 to the value in R0 (x + 5), store result in R0
or
MOV #10, -(SP) ; Push 10 onto the stack for x
MOV (SP), R0 ; Load x from stack into R0
ADD #5, R0 ; Add 5 to x
Whether a variable gets stack-allocated or register-allocated, it's still a pretty close mapping afaict. From my understanding the original C mapped closely to PDP-7 and then PDP-11 assembly. The original implementation and how it maps to PDP-11 could be used as a reference implementation.
I don't think this is the case with your comments and a lot of other comments, I think there's genuine confusion and misunderstanding, but I definitely also feel like at this point a decent number of people are intentionally trolling and acting as thought they don't understand what's being said when people make the case for lots of small composable programs, and why that might be provide additional benefits over just having a bunch of small composable pure functions.
I've heard it being called a high level assembly that maps closely to assembly many times at this point, it makes sense to me why people would say that.
> If C maps 1:1 to assembly in your view, then (unsafe) Rust does; if Rust doesn't map 1:1 to assembly, nor does C. It's as simple as that.
I thought the mapping issue was unrelated to the borrow checker, and that it's possible to write a borrow checker for a restricted subset of C. I thought the thing that was making it not map 1:1 was actually all of the extra features in Rust, like the ADTs and async and all of that. Is that not actually the case?
1. It doesn't map almost 1:1 to assembly the way C does, so it's not inherently clear if the code will necessarily do what it says it does. That seems questionable for something as important as a kernel and driver.
2. Only one real Rust compiler, that's a recursive compiler, which reminds me of the Trusting Trust problem: https://dl.acm.org/doi/abs/10.1145/358198.358210
- Too basic: At least half of the software I use just uses plain makefiles and maybe a configure script. No autotools. I optionally run ./configure, and then make and make install, and it just works. I definitely wouldn't consider my setup to be a toy by any stretch of the imagination. It's built out of smaller programs that do one thing and one thing well.
- Too complicated: I don't know, I think make and how it works is really easy to understand to me at least. I guess everyone's had different experiences. Not necessarily your case, but I think usually it's because they had bad experiences that they probably blamed make for, when they were trying to build some complex project that either had a bad build setup itself (not make's fault), or without the requisite knowledge.
- No standard library: It's supposed to be tooling agnostic, which is what makes it universally applicable for a very wide range of tools, languages, and use cases. It's viewed as a feature, not a bug.
- Too constricting: I'm not sure what you mean here, it's designed to do one thing and one thing well. The simple layer is the dependency tracking.
- Too much magic: Cryptic or inconsistent syntax: See 'Too complicated'