Comment on
Popular Programming Book "Clean Code" is being rewritten
But is it rewritten in Rust?
Comment on
Popular Programming Book "Clean Code" is being rewritten
But is it rewritten in Rust?
Comment on
Googling
“I’ve got 10 years of googling experience”.
“Sorry, we only accept candidates with 12 years of googling experience”.
Comment on
We'll refactor this next year anyways
public class AbstractBeanVisitorStrategyFactoryBuilderIteratorAdapterProviderObserverGeneratorDecorator {
// boilerplate goes here
}
Comment on
A real chicken-and-egg situation
C++’s mascot is an obese sick rat with a missing foot*, because it has 1000+ line compiler errors (the stress makes you overeat and damages your immune system) and footguns.
EDIT: Source (I didn't make up the C++ part)
Comment on
And I will have my vengeance, in this file host or the next.
Hello Gladiator (2000) REMASTERED EXTENDED 1080p BluRay 10bit HEVC 6CH 4.3GB - MkvCage.
Comment on
*Permanently Deleted*
It's funny because, I'm probably the minority, but I strongly prefer JetBrains IDEs.
Which ironically are much more "walled gardens": closed-source and subscription-based, with only a limited subset of parts and plugins open-source. But JetBrains has a good track record of not enshittifying and, because you actually pay for their product, they can make a profitable business off not doing so.
Comment on
It's time to mentally prepare yourselves for this
Reply in thread
Comment on
Java vs Kotlin
C++
Comment on
Kotlin is Tetris
The Tetris design system:
Write code, delete most of it, write more code, delete more of it, repeat until you have a towering abomination, ship to client.
Comment on
19 April 2024
Java the language, in human form.
Comment on
I'm betting on Call-by-Push-Value
Reply in thread
I believe the answer is yes, except that we’re talking about languages with currying, and those can’t represent a zero argument function without the “computation” kind (remember: all functions are Arg -> Ret, and a multi-argument function is just Arg1 -> (Arg2 -> Ret)). In the linked article, all functions are in fact “computations” (the two variants of CompType are Thunk ValType and Fun ValType CompType). The author also describes computations as “a way to add side-effects to values”, and the equivalent in an imperative language to “a value which produces side-effects when read” is either a zero-argument function (getXYZ()), or a “getter” which is just syntax sugar for a zero-argument function.
The other reason may be that it’s easier in an IR to represent computations as intrinsic types vs. zero-argument closures. Except if all functions are computations, then your “computation” type is already your closure type. So the difference is again only if you’re writing an IR for a language with currying: without CBPV you could just represent closures as things that take one argument, but CBPV permits zero-argument closures.
Comment on
Guess the language: #4
Haskell
Comment on
Methods Should Be Object Safe
Reply in thread
I could understand method = associated function whose first parameter is named self, so it can be called like self.foo(…). This would mean functions like Vec::new aren’t methods. But the author’s requirement also excludes functions that take generic arguments like Extend::extend.
However, even the above definition gives old terminology new meaning. In traditionally OOP languages, all functions in a class are considered methods, those only callable from an instance are “instance methods”, while the others are “static methods”. So translating OOP terminology into Rust, all associated functions are still considered methods, and those with/without method call syntax are instance/static methods.
Unfortunately I think that some people misuse “method” to only refer to “instance method”, even in the OOP languages, so to be 100% unambiguous the terms have to be:
impl block.self (even if it takes Self under a different name, like Box::leak).self, so it can be called like self.foo(…).Comment on
Wait... it's all backdoors ? Always has been bro...
Reply in thread
Comment on
Using Go as a compiler backend?
Go as a backend language isn’t super unusual, there’s at least one other project (https://borgo-lang.github.io) which chosen it. And there are many languages which compile to JavaScript or C, but Go strikes a balance between being faster than JavaScript but having memory management vs. C.
I don’t think panics revealing the Go backend are much of an issue, because true “panics” that aren’t handled by the language itself are always bad. If you compile to LLVM, you must implement your own debug symbols to get nice-looking stack traces and line-by-line debugging like C and Rust, otherwise debugging is impossible and crashes show you raw assembly. Even in Java or JavaScript, core dumps are hard to debug, ugly, and leak internal details; the reason these languages have nice exceptions, is because they implement exceptions and detect errors on their own before they become “panics”, so that when a program crashes in java (like tries to dereference null) it doesn’t crash the JVM. Golang’s backtrace will probably be much nicer than the default of C or LLVM, and you may be able to implement a system like Java which catches most errors and gives your own stacktrace beforehand.
Elm’s kernel controversy is also something completely different. The problem with Elm is that the language maintainers explicitly prevented people from writing FFI to/from JavaScript except in the maintainers’ own packages, after allowing this feature for a while, so many old packages broke and were unfixable. And there were more issues: the language itself was very limited (meaning JS FFI was essential) and the maintainers’ responses were concerning (see “Why I’m leaving Elm”). Even Rust has features that are only accessible to the standard library and compiler (“nightly”), but they have a mechanism to let you use them if you really want, and none of them are essential like Elm-to-JS FFI, so most people don’t care. Basically, as long as you don’t become very popular and make a massively inconvenient, backwards-incompatible change for purely design reasons, you won’t have this issue: it’s not even “you have to implement Go FFI”, not even “if you do implement Go FFI, don’t restrict it to your own code”, it’s “don’t implement Go FFI and allow it everywhere, become very popular, then suddenly restrict it to your own code with no decent alternatives”.
Comment on
What's your favorite weird corner of the internet?
Not weird and not super obscure, but if you haven't already seen this blog: https://ciechanow.ski/. Each post explains some random concept, but with interactive animations which make it intuitive and interesting.
Comment on
Argument datatype dependant of previous arguments?
Multiple ways you can do this. Most of these should also extend to multiple arguments, and although the constant is promoted to type level, you can pass it around nested functions as a type parameter.
In Java (personally I think this approach is best way to implement your specific example; also Kotlin, C#, and some others are similar):
interface HashAlgo<Options> {
String hash(String password, Options options);
}
class Bcrypt implements HashAlgo<BcryptOptions> { ... }
class Argon2 implements HashAlgo<Argon2Options> { ... }
record BcryptOptions { ... }
record Argon2Options { ... }
In Haskell without GADTs (also Rust is similar):
class HashAlgo opts where
hash :: String -> opts -> String
data BcryptOptions = BcryptOptions { ... }
data Argon2Options = Argon2Options { ... }
instance HashAlgo BcryptOptions where
hash password BcryptOptions { .. } = ...
instance HashAlgo Argon2Options where
hash password Argon2Options { .. } = ...
In C (with _Generic):
typedef struct { ... } bcrypt_options;
typedef struct { ... } argon2_options;
char* hash_bcrypt(const char* password, bcrypt_options options) { ... }
char* hash_argon2(const char* password, argon2_options options) { ... }
#define hash(password, options) _Generic((options), bcrypt_options: hash_bcrypt, argon2_options: hash_argon2)(password, options)
In TypeScript, inverting which type is parameterized (see this StackOverflow question for another TypeScript approach):
type HashAlgo = 'bcrypt' | 'argon2'
type HashOptions<H> = H extends 'bcrypt' ? BcryptOptions : H extends 'argon2' ? ArgonOptions : never
function hash<H>(password: string, algorithm: H, options: HashOptions<H>): string { ... }
This way is a bit more straightforward but also way more complicated for the compiler, and most languages don't have these features or they're very experimental. Dependent types are useful when your constant is non-trivial to compute and you can't even compute it fully, like vectors with their length as a type parameter and append guarantees the return vector's length is the sum. In that case generics aren't enough. Constant generics aren't full dependent types but let you do things like the vector-sum example.
In Haskell with GADTs AKA Generic Algebraic Data types (also works in Idris, Agda, and other Haskell-likes; you can simulate in Rust using GATs AKA Generic Associated Types, but it's much uglier):
data BcryptOptions = BcryptOptions { ... }
data Argon2Options = Argon2Options { ... }
data Algorithm opts where
Bcrypt :: Algorithm BcryptOptions
Argon2 :: Algorithm Argon2Options
hash :: String -> Algorithm opts -> opts -> String
hash password algo opts =
case algo of
| Bcrypt -> ... -- opts is inferred to be BcryptOptions here
| Argon2 -> ... -- opts is inferred to be Argon2Options here
In Coq (also flipping the parameterized types again):
Inductive algorithm : Set := bcrypt | argon2.
Inductive algorithm_options (A: algorithm) : Set := bcrypt_options : ... -> algorithm_options bcrypt | argon2_options : ... -> algorithm_options argon2.
Fixpoint hash (password : string) (algo : algorithm) (opts : algorithm_options also) : string := ... .
Comment on
Guess the language: #4
Reply in thread
Actually it could be PureScript, Curry, Clean, or Miranda. Even more swap the double/single colon. It’s a common syntax :)
EDIT: Or Plutus (blockchain smart contract dialect), or Frege (Haskell dialect for the JVM), or Eta (also JVM), or Discus (formerly Disciple, strict Haskell dialect), or this interpreted Haskell dialect which I forget the name of.
Comment on
My biggest difficulty is choosing a language/framework/engine
Choose Rust. Problem solved.
/s
Comment on
PC graphics cards to get more expensive again "thanks" to AI boom
But aren’t the GPUs used by AI different than the GPUs used by gamers? 8GB of RAM isn’t enough to run even the smaller LLMs, you need specialized GPUs with 80+GB like A100s and H100s.
The top-tier consumer models like the 3090 and 4090 have 32GB, with them you can train and run smaller LLMs locally. But there still isn’t much demand to do that because you can rent GPUs on the cloud for cheap; enough that the point where renting exceeds the cost of buying is very far off. For consumers it’s still too expensive to fine-tune your own model, and startups and small businesses have enough money to rent the more expensive, specialized GPUs.
Right now GPU prices aren’t extremely low, but you can actually but them from retailers at market price. That wasn’t the case when crypto-mining was popular