335 karma · joined July 3, 2010
Interestingly, for me, I only see some of the issues the GP mention when using xorg instead of wayland.
Yeah, the initial launch was pretty bad. However, this has improved quite a bit from the initial launch if you haven't tried it in a while.
- Scala is much closer to Java making it an easier sell to other Java programmers.
- I think static typing makes a lot more sense for larger projects
- I found a rich static type system easier to use and more generally applicable than macros.EDIT It looks like it did make it in: https://github.com/scala/scala/commit/d5ae93e1b35fac4002cfbf...
Failure { error: Error }
Success<T> { value: T }
I assume you want value or error not value and error.> JVM is such a failure
I'm genuinely confused why you would consider that to be the case. Especially since your preferred alternative (WebASM) is just about as bad or worse by your stated measures (start up time, GC).
EDIT - You have a tree of dependencies. This is something you care about. In order to create that tree in code, you have to convert it to a linear series of statements. This conversion is something you don't really care about as long as you end up with the tree you want. Dependency injection frameworks should make it so you can declaratively specify that tree without having to worry about how it is actually constructed.
I can understand some hesitation about using a dependency injection framework. Some of them can be very hard to reason about (especially the run time frameworks). However, they don't have to be that hard to reason about. My personal favorite is a compile time dependency injection framework for scala called Macwire[1]. In scala, you might do manual dependency injection like this:
val instantiated = new Class(dependency1, dependency2)
With Macwire you could write the same thing as: val instantiated = wire[Class]
The wire macro above, automatically looks up dependencies by type and creates an instance of Class with those dependencies as arguments. If you combine that macro with Scala's built in support for laziness then you don't have to worry about initialization order either. This gives you something that is fairly easy to reason about (it is basically what your were doing by hand before) and will sort our necessary dependencies for you (and error out of they are missing).Wouldn't it be in the same boat as scala-js? There are quite a few libraries that support scala-jvm and scala-js.
Yes, I'm aware of how journaling works. The problem with your description is that it requires both data and metadata journaling. This is rarely the case because data journaling requires that you write all of your data to the disk twice (which also requires additional seeks as well). So, nearly all in-place filesystems default to only metadata journaling for performance reasons (this is the case for ext4). This means that, in practice, if you get a system crash mid file write, you will end up with a partially updated file. On a COW filesystem you are always guaranteed the old file or the new file.
> Maybe you don't know as much about filesystems as you want to appear to?
That is possible, although I'm not sure how much I want to appear to know :P. More seriously, this is extremely condescending. Why is this necessary? If you think I'm wrong, just tell me I'm wrong. There is a pretty decent chance I'm wrong about most things. So, condescension shouldn't be required.
> The risk of bitrot becomes interesting when you talk about long timeframes like a decade.
I think you are making a lot of assumptions here about frequency of bitrot, especially in the face of failing hardware. Also, consumer desktops are not the only use. There are bunch usages where the risk of bitrot is much more concerning. Or were you intending your original post to be only about consumer usage?
>Considering the capacity and price of a 2TB disk, compression isn't the immediate need of the consumer either, unless they have a NAS, where Syno and QNAP already provide integrated solutions with btrfs and ext4 both.
This kind of feels like you are saying that it isn't needed because it is already being used when it is needed. Am I misunderstanding? Consumers may want compression for smaller hard drives they already own in order to get more out of them. Also, compression can speed up disk IO in some workloads.
> The modern ext4 journaling is also very battle tested, it's a very simple and resilient physical journal, data loss in ext4 happens largely when the disk is already failing or has been cheap enough to lie about the state of data being written or not.
The issue with ext4 resilience isn't about quality of the implementation. It is a fundamental limitation of no COW filesystems. Since things are overwritten, if the system crashes mid write you can end up with partial data. COW filesystems always give you the old data or the new data. This can be worked around but that requires that all of your applications do this workaround.
> For the average consumer that sums up to "it doesn't matter enough". Something like bcachefs could change the game by being btrfs+ZFS but without the dataloss risk.
What makes you think bcachefs is going to be any better? A lot of functionality still has to be implemented. What makes you think that the implementation of this functionality will be any less error prone?
You also haven't addressed the large pile of new features provided by BTRFS/ZFS. Snapshotting alone can make it worth the price of admission for consumers. With snapshotting, you can set up a system where rollbacks after any upgrade can occur seamlessly.
As you mentioned, the bitrot. Also, my understanding is that a COW filesystem can be more reliable than a traditional journaling filesystem in a system crash since nothing is overwritten. Plus there is compression and all of the features provided by a COW filesystem.
Is the underlying code terrible? I haven't heard any particular concerns about code quality (aside from the original implementation of RAID 5/6). Most of the issues I've heard about were due to people using features that weren't ready yet. I've heard a lot fewer of these issue since BTRFS started providing clear statuses for individual features[1]. Do you have a different impression?
Thanks for the link. However, I feel like that still doesn't give a lot of detail. Can you have multiple receivers? Can receivers be generic? Can receiver resolution be controlled by other arguments?
> It's just a closure with a compiler-added paremeter. It can be nullable if you want and exceptions work the same as any other closure. If you want async/await you'd use coroutines. I think coroutines and receivers mix just fine, you'd just make the type a suspending lambda with receiver, but I've never tried
Sorry I didn't mean to imply that Kotlin was incapable expressing all of these things together. I meant it would be desirable to express all of these things with the same tools. It seems like the you would have to express these with several different paradigms and be aware of how they interact. That also means you would need more boilerplate because you have to abstract each of these things separately.
I think this issue with this is what happens when you combine DB transactions with async queries with errors and null handling? It seems like you have to combine 4 different mechanisms in order to do this (receivers, async/await, try/catch, if(null)). Is there some way to do this all with receivers? It seems like they don't really compose.
The 'command' version in Scala could be written roughly the same way for any combination of the 4 features (the types would just change). For example you could write a Scala version of your original example as:
def test(): Unit = {
// update() compiles but doesn't do anything to the DB
val helloDBOperation = for {
_ <- query()
_ <- update()
_ <- query2()
} yield "Hello"
val hello = db.run(helloDBOperation)
println("$hello") // prints "Hello"
}
This would look almost exactly the same no matter which combination of the 4 features you use.EDIT - it looks like I somehow also accidentally replied to you with an earlier version of this comment. Unfortunately , I didn't notice until it was too old to delete or edit. Please ignore the other comment that is very similar to this one. Sorry about the confusion.
I think this issue with this is what happens when you combine DB transactions with async queries with errors and null handling? It seems like you have to combine 4 different mechanisms in order to do this (receivers, async/await, try/catch, if(null)). Is there some way to do this all with receivers? It seems like they don't really compose.
The 'command' version in Scala could be written roughly the same way for any combination of the 4 features (the types would just change). For example you could write your original example as:
def test() {
// update() compiles but doesn't do anything to the DB
val helloDBOperation = for {
result1 <- query()
_ <- update()
result2 <- query2()
} yield "Hello"
println("$hello") // prints "Hello"
} type MustBeInTransaction = implicit MyTransaction => Unit
def makeObject(i: Int): MustBeInTransaction = {
return { /* do something with i or whatever in a transaction */ }
}
def test(): Unit = {
var genericArray = Array(1, 2, 3).map(makeObject)
//genericArray.forEach { _() } // fails to compile
transaction {
genericArray.forEach { _() }
}
}
EDIT - I just noticed that this is almost exactly the same as the example used in the function types link below[1] https://stackoverflow.com/questions/45875491/what-is-a-recei...
[2] https://kotlinlang.org/docs/reference/extensions.html
[3] https://www.scala-lang.org/blog/2016/12/07/implicit-function...
EDIT just realized you are m50d on reddit and probably saw the exact same comment I got that from.
I'm not sure I totally understand this. What do you mean by "macros going wrong"? It doesn't seem like there are that many implicits used. The mostly to add methods to strings to make expressing things more concise. Do you think implicits shouldn't be used at all?
> The thing that makes sbt difficult in practice for me is twofold. I don't want to deliver hackedy shit to a client that I then have to defend if their quality control comes back complaining about it. Being able to use regular scala in sbt definitions is convenient but at the cost of making things expressable that should wisely be done another way.
> Which brings me to the second point which is getting a sbt definition from a client and then they don't want to change it, or they don't have quality control so it's just awful, or its so bad that someone has to rewrite it for the solution. The latter being the easiest to deal with, but that also costs time.
This seems contradictory. It seems like in the first paragraph is at odds with the second. In the first paragraph, it sounds like you are saying that you want to be able to write messy builds and not have anyone complain. In the second paragraph, it sounds like you are saying you don't want deal with other people's messy builds. Am I understanding correctly? If so, do you want messy builds or not? How is this related to the build tool? What build tool prevents people from writing messy builds? Are you thinking of something like maven that is very rigid?
> For those looking, what things do you feel have most improved the sbt experience?
Some things that may (since I don't know when you last looked) have improved:
* SBT 0.13 greatly improved the .sbt build file syntax (no more line breaks between settings, multi project in one file)
* SBT 0.13 also cleaned up the symbol soup. Now to define tasks/settings you only need to know 3 symbols that are fairly self explanatory ':=', '+=', and '++='. If you want to depend on another task/setting output you just have do `taskName.value` in your task/setting definition.
* A new artifact resolver has been written[1] that fixes all of the issues with ivy. It can be used today and is currently in the process of being integrated in the default SBT install.
* SBT 1 introduced a build server concept with language server protocol support that is starting to improve IDE/editor integration
* This isn't strictly SBT but IntelliJ now natively understands .sbt files so you can autocompletion and code navigation.
This seems a bit hyperbolic and a little vague. So, I can't really address it directly. Interestingly, I find sbt much easier to extend than most build tools because sbt tasks generally don't rely on side effects to communicate. Obviously YMMV.
> it has multiple configuration files
Doesn't every build tool have this? Or do you mean multiple config file formats? If that is the case, newer version of sbt have moved a single file format.
> the ridiculous Ivy global lock and inability to fetch Ivy artefacts in parallel etc
I can understand this concern. Ivy is kind of annoying. Fortunately, a new artifact resolver called Coursier[1] has been written and it solves this problem. You can use it now and it is currently in the process of being integrated as part of the default SBT install[2].
[1] https://github.com/coursier/coursier [2] https://github.com/sbt/sbt/issues/2997