I'm a nushell user but like... job control in nushell is pretty miserable still unfortunately.
Nushell is definitely my fav of the set (xonsh is a neat experiment but ultimately is missing pipeline programming that nushell gives....), and I write personal shell scripts for myself mostly in nu.
Aside: for shell scripts, my preference is something like nu, then python + stdlib, giving me argparser etc, then just zsh/bash/whatever. Seriously annoying how POSIX shells do not give good argument parsing, tho I get it's a hard problem
Is there any good faith read of this that people can lend credence to? The one I could maybe come up with (with their mention of stability) is "we want OSes derived from AOSP to be stable, instead of following main too closely". They mention third party devs working off of stable too... so maybe they're like "instead of dealing with outside contributors messing around with our 'wip' stuff, we'll sign up for integration work".
Almost all device run on the initial android release (QPR0), and never shipped any of quarterly updates. Even less so using _main_ as a baseline so that point is moot.
With android 16 introducing "mid releases" (QPR2), they expect OEMs to start shipping those as well, QCOM already has a QPR2 BSP release, and Samsung is expected to release QPR2 based builds soon.
As far as contributions go, google usually wanted patches to apply to main, I don't think that ever changed.
And even there now that AOSP development is fully closed, it's even easier as partners will likely just upload patches against internal main instead. Less integration work there as well.
There really isn't a good explanation as to why they want to do move code drop cadence, other than they can and want to avoid wasting time releasing QPR1/3 that no OEM ever shipped (expect Pixels that is)
So the source code will be released in a kind of FreeBSD releases? These pieces work together, base things off them, don't mess with (or even see) any WIP stuff.
In other words, the result is still open, but the development process is not.
I don't work on Android, but I suspect it's a whole lot less work for both confidentiality and maintenance to not have to worry about daily/weekly OSS releases. That's probably worth more to the decisionmakers than the value of random contributions from people who aren't already inside the partner tent.
[edit] based on the other comments, I surmise that public pushes were already infrequent.
Android's foundation has been mostly stable for years now, with fairly minor changes between releases. So I guess they just don't want to deal with too many versions to document and support, given that device vendors are generally awful.
Also for a long time they were doing yearly (or longer) release, afaiu it's only the past two years that they switched to quarterly (with the QPR release).
At least with DMCA you so get a notice and you can somewhat challenge it. With Italy's Piracy Shield you have no notice and there's no public record of which IPs/websited have been blocked, so it's hard to even challenge it in court.
I mean markdown existed, but before that there's like... whatever format phpbb and friends let you use for forum posts right? There was stuff that was text-y.
But perhaps it was the first big format that was followed a Unix-y "here's a CLI tool to go to HTML from this" thing, instead of some php script
I feel like even outside of AI translation, formalization not capturing the spirit of what the informal description was provided is always a risk.
This is also a big risk when trying to prove code correctness: "prove this algo works" means you gotta define "works" along certain axes, and if you're very unlucky you might have a proof that exploits the uncertainty around a certain axis.
We have also worked via Renovate recently and are enjoying it. The dashboard is particularly nice for onboarding repos with lots of old deps (checkmark -> make a PR is a nice flow that semi-automates things).
Dependabot integrates decently well with Github of course but so far renovate has worked well for us.
A thing I've experienced is people buying a bigger fridge, and then just leaving it when they move out because they're moving to a place with a fridge that is fine (for example, moving in with someone who has a nice fridge). Everyone walks away from that basically fine.
One thing I think worth considering for systems languages on this point: if you don't want to solve every expressiveness issue downstream of Result/Option/etc from the outset, look at Swift, which has nullable types.
MyObject can't be null. MyObject? can be null. Handling nullability as a special thing might help with the billion-dollar mistake without generating pressure to have a fully fleshed out ADT solution and everything downstream of that.
To people who would dismiss ADTs as a hard problem in terms of ergonomics: Rust makes it less miserable thanks to things like the question-mark shorthand and a bazillion trait methods. Languages like Haskell solve it with a monads + do syntax + operating overload galore. Languages like Scala _don't_ solve it for Result/Option in any fun way and thus are miserable on this point IMHO
I like to think about how many problems a feature solves to judge whether it's "worth it". I believe that the Sum types solve enough different problems that they're worth it, whereas nullability solves only one problem (the C-style or Java-style null object) the Sum types can solve that with Option<T> and also provide error handling with Result<T, Err> and control flow with ControlFlow<Continue, Break> among others so that's already a better deal.
Nullability is a good retro-fit, like Java's type erased generics, or the DSL technology to cram a reasonable short-distance network protocol onto the existing copper lines for telephones. But in the same way that you probably wouldn't start with type erased generics, or build a new city with copper telephone cables, nullability isn't worth it for a new language IMO.
- `?T` and `T!E` as type declaration syntax that desugars to them;
- and `.?` and `.!` operators so chains like `foo()?.bar()!.baz()` can be written and all the relevant possible return branches are inserted without a fuss.
Having `Option` and `Result` be simply normal types (and not special-casing "nullable") has benefits that are... obvious, I'd say. They're just _normal_. Not being special cases is great. Then, having syntactic sugars to make the very, _very_ common cases be easy to describe is just a huge win that makes correct typing more accessible to many more people by simply minimizing keystrokes.
The type declaration sugar is perhaps merely nice to have, but I think it really does change the way the average programmer is willing to write. The chaining operators, though... I would say I borderline can't live without those, anymore.
Chaining operators can change the SLOC count of some functions by as much as... say, 75%, if we consider a language like Go with it's infamous "if err not nil" clause that is mandated to spread across three lines.
I mean, yeah, type erasure does give parametricity, but, you can instead design your language so that you monomorphize but insist on parametricity anyway. If you write stable Rust your implementations get monomorphized but you aren't allowed to specialize them - the stable language doesn't provide a way to write two distinct versions of the polymorphic function.
And if you only regard parametricity as valuable rather than essential then you can choose to relax that and say OK, you're allowed to specialize but if you do then you're no longer parametric and the resulting lovely consequences go away, leaving it to the programmers to decide whether parametricity is worth it here.
I don't understand your first paragraph. Monomorphization and parametricity are not in conflict; the compiler has access to information that the language may hide from the programmer. As an existance proof, MLTon monomorphizes arrays while Standard ML is very definitely parametric: http://www.mlton.org/Features
I agree that maintaining parametricity or not is a design decision. However, recent languages that break it (e.g. Zig) don't seem to understand what they're doing in this regard. At least I've never seen a design justification for this, but I have seen criticism of their approach. Given that type classes and their ilk (implicit parameters; modular implicits) give the benefits of ad-hoc polymorphism while mantaining parametricity, and are well established enough to the point that Java is considering adding them (https://www.youtube.com/watch?v=Gz7Or9C0TpM), I don't see any compelling reason to drop parametricity.
My point was that you don't need to erase types to get parametricity. It may be that my terminology is faulty, and that in fact what Rust is doing here does constitute "erasing" the types, in that case what describes the distinction between say a Rust function which is polymorphic over a function to be invoked, and a Rust function which merely takes a function pointer as a parameter and then invokes it ? I would say the latter is type erased.
The Scala solution is the same as Haskell. for comprehensions are the same thing as do notation. The future is probably effect systems, so writing direct style code instead of using monads.
It's interesting that effect system-ish ideas are in Zig and Odin as well. Odin has "context". There was a blog post saying it's basically for passing around a memory allocator (IIRC), which I think is a failure of imagination. Zig's new IO model is essentially pass around the IO implementation. Both capture some of the core ideas of effect systems, without the type system work that make effect systems extensible and more pleasant to use.
I personally don't enjoy the MyObject? typing, because it leads to edge cases where you'd like to have MyObject??, but it's indistinguishable from MyObject?.
E.g. if you have a list finding function that returns X?, then if you give it a list of MyObject?, you don't know if you found a null element or if you found nothing.
It's still obviously way better than having all object types include the null value.
When you want to distinguish `MyObj??` then you'll have to distinguish the optionality of one piece of code (wherever your `MyObj?` in the list came from) with some other (list find) before "mixing" them. E.g. by first mapping `MyObj?` to `MyObj | NotFoundInMyMap` (or similar polymorphic variant/anonymous sum types) and then putting it in a list. This could be easily optimized away or be a safe no-op cast.
Common sum types allow you to get around this, because they always do this "mapping" intrinsically by their structure/constructors when you use `Either/Maybe/Option` instead of `|`. However, it still doesn't always allow you to distinguish after "mixing" various optionalities - if find for Maps, Lists, etc all return `Option<MyObj>` and you have a bunch of them, you also don't know which of those it came from. This is often what one wants, but if you don't, you will still have to map to another sum type like above.
In addition, when you don't care about null/not found, you'll have the dual problem and you will need to flatten nested sum types as the List find would return `Option<Option<MyObj>>` - `flatten`/`flat_map`/similar need to be used regularly and aren't necessary with anonymous sum types that do this implicitly.
Both communicate similar but slightly different intent in the types of an API. Anonymous sum types are great for errors for example to avoid global definitions of all error cases, precisely specify which can happen for a function and accumulate multiple cases without wrapping/mapping/reordering.
Sadly, most programming languages do not support both.
> E.g. if you have a list finding function that returns X?, then if you give it a list of MyObject?, you don't know if you found a null element or if you found nothing.
This is a problem with the signature of the function in the first place. If it's:
where the _result_ is responsible for the return value wrapping. Making this not copy is a more advanced exercise that is bordering on impossible (safely) in C++, but Rust and newer languages have no excuse for it
inline fun <T> Sequence<T>.last(predicate: (T) -> Boolean): T {
var last: T? = null
var found = false
for (element in this) {
if (predicate(element)) {
last = element
found = true
}
}
if (!found) throw NoSuchElementException("Sequence contains no element matching the predicate.")
@Suppress("UNCHECKED_CAST")
return last as T
}
A proper option type like Swift's or Rust's cleans up this function nicely.
Your example produces very distinguishable results. e.g. if Array.first finds a nil value it returns Optional<Type?>.some(.none), and if it doesn't find any value it returns Optional<Type?>.none
The two are not equal, and only the second one evaluates to true when compared to a naked nil.
This is Swift, where Type? is syntax sugar for Optional<Type>. Swift's Optional is a standard sum type, with a lot of syntax sugar and compiler niceties to make common cases easier and nicer to work with.
Well, in a language with nullable reference types, you could use something like
fn find<T>(self: List<T>) -> (T, bool)
to express what you want.
But exactly like Go's error handling via (fake) unnamed tuple, it's very much error-prone (and return value might contain absurd values like `(someInstanceOfT, false)`). So yeah, I also prefer language w/ ADT which solves it via sum-type rather than being stuck with product-type forever.
I like go’s approach on having default value, which for struct is nil. I don’t think I’ve ever cared between null result and no result, as they’re semantically the same thing (what I’m looking for doesn’t exist)
You don't even need to end the file in `.go` or the like when using shebangs, and any self-respecting editor will be good at parsing out shebangs to identify file types (... well, Emacs seems to do it well enough for me)
no need to name your program foo.go when you could just name it foo
Nushell is definitely my fav of the set (xonsh is a neat experiment but ultimately is missing pipeline programming that nushell gives....), and I write personal shell scripts for myself mostly in nu.
Aside: for shell scripts, my preference is something like nu, then python + stdlib, giving me argparser etc, then just zsh/bash/whatever. Seriously annoying how POSIX shells do not give good argument parsing, tho I get it's a hard problem
reply