1. 0

    This has nothing to do with Linux, it’s a bash feature.

    1. 3

      The suggestion link is just below the title.

    1. 12

      Another article about macOS package managers which fails to mention Joyent’s binary sets for pkgsrc on macOS… I’m beginning to think I’m the only one using them.

      1. 8

        I used to maintain a bootstrapper script for getting people going with Joyent’s pkgsrc builds on macOS a few years back: https://github.com/cmacrae/savemacos

        I have since moved to Nix, as I use it for more than just package management. But, I can assure you; you won’t be the only one using those packages!

        1. 3

          I used to use savemacos back then, thank you a lot for making something useful and sharing it.

          1. 2

            Ah well that’s nice to hear! You’re very welcome

        2. 3

          I’m not using /u/jperkin’s binary packages but I’ve been using pkgsrc on macOS since ~2007 (I’m a former heavy NetBSD user, so rather like pkgsrc). I’m pretty happy with it although I’ve never explored any of the other options (MacPorts, Homebrew, etc).

          1. 3

            You are not

          1. 11

            Well…it’s not comprehension syntax.

            1. 1

              What should it be called, in your opinion?

              1. 2

                I propose to call them “binding operators”: like “infix operators”, but instead of infix symbols they are binders.

                1. 1

                  do’ish notation / idiom brackets

                  1. 1

                    “Applicative let syntax” or “monadic let syntax”. IMO, OCamlers are good at not inventing terms for the sake of it.

                    1. 1

                      F# has a similar syntax and calls calls it “computation expression”, ppx_let calls it “applicative/monadic let binding”. The first thing I think of when I see “comprehension” is list comprehension, which doesn’t have much to do with this (and which I’m glad OCaml doesn’t have special syntax for).

                    1. 4

                      Agreed. I’m very up on the idea of getting more languages to run on the BEAM. I miss static types and, frankly, I wish that Rust could compile down to run on the BEAM!

                      1. 5

                        Yeah, I love Elixir to death, but sometimes I find myself wishing for a real type system. Some folks swear by Dialyzer, but it feels a bit like a cludgy piece of typing duct tape.

                        1. 12

                          The dynamically-typed nature of Erlang and Elixir and BEAM comes from a design requirement: that the systems built in Erlang can be upgraded at runtime. Strong typing gets quite a bit more complicated when you need to be able to have multiple versions of a type coexist at runtime.

                          Side note, this took me a while to absorb when beginning to write Elixir. My instinct was to use structs instead of generic maps for GenServer state, since better-defined types are better, right? But that imposes hard requirements on hot upgrades that wouldn’t have been there if I’d used untyped maps from the start; removing fields from a struct breaks upgrades. This knowledge was somewhere between “esoteric” and “esoteric to Ruby assholes who just showed up, well-known to wonks”. The Erlang Way is a lot more than “let it crash”. :)

                          1. 3

                            The dynamically-typed nature of Erlang and Elixir and BEAM comes from a design requirement: that the systems built in Erlang can be upgraded at runtime. Strong typing gets quite a bit more complicated when you need to be able to have multiple versions of a type coexist at runtime

                            Yeah, I really wish there was more type system research going into figuring out how to use them effectively in upgradable, always-on systems, where you might have heterogeneous versions across a cluster. I actually think static types could be super helpful here, but as far as I’m aware there doesn’t seem to be much work put into it.

                            1. 4

                              It’s very difficult. It’s not like nobody tried — https://homepages.inf.ed.ac.uk/wadler/papers/erlang/erlang.pdf

                              And when people talk about “I wish there was a type system” they probably don’t realise that Erlang is very different animal (that can do things other animals have no concepts for). Just bolting on types is not an option (if you want to know what happens if you do so, look at CloudHaskell — you have to have a exact binary for every node in the entire cluster, or else).

                              1. 1

                                Just bolting on types is not an option (if you want to know what happens if you do so, look at CloudHaskell — you have to have a exact binary for every node in the entire cluster, or else).

                                That’s what I mean. I see Cloud Haskell as interesting, but really not the distributed type system I want. It would be super cool to see more new ideas here (or rediscovery of old ones, if they’re around). Eg. you may need some kind of runtime verification step to ensure that a deployment is valid based on the current state of the world. Perhaps some stuff from databases and consensus would help here. Doing that efficiently could be… interesting. But that’s why research is important!

                              2. 3

                                I think protocol buffers (and similar systems like Thrift / Avro) are pretty close to the state of the art (in terms of many large and widely deployed systems using them). When you write distributed systems using those technologies, you’re really using the protobuf type system and not the C++ / Java / Python type system. [1] It works well but it’s not perfect of course.

                                I also would make a big distinction between distributed systems where you own both sides of the wire (e.g. Google’s), and distributed systems that have competing parties involved (e.g. HTTP, e-mail, IRC, DNS, etc.). The latter case is all untyped because there is a “meta problem” of agreeing on which type system to use, let alone the types :) This problem is REALLY hard, and I think it’s more of a social/technological issue than one that can be addressed by research.

                                [1] This is a tangent, but I think it’s also useful to think of many programs as using the SQL type system. ORMs are a kludge to bridge SQL’s type system with that of many other languages. When the two type systems conflict, the SQL one is right, because it controls “reality” – what’s stored on disk.

                                1. 2

                                  I think protocol buffers ⟨…⟩ are pretty close to the state of the art

                                  Seriously? PB, where you can’t even distinguish between (int)-1 and (uint)2 is state of the art?

                                2. 2

                                  Alice ML is a typed programming language designed to enable open extensions of systems. Objects can be serialized/deserialized and retain their types and it’s possible to dynamically load new code.

                                3. 2

                                  The Erlang Way is a lot more than “let it crash”. :)

                                  I am so with you on this one, and I’ve got so much to learn!

                                  1. 6

                                    You might find ferd’s intro helpful. For historical perspective with some depth, you might like Armstrong’s thesis from 2003 that describes everything in deep detail.

                                  2. 2

                                    Yup, this is related to the point I was making about protobufs and static “maybe” vs. dynamic maps here. In non-trivial distributed systems, the presence of fields in message has to be be checked at RUNTIME, not compile-time (if there’s a type system at all).

                                    https://lobste.rs/s/zdvg9y/maybe_not_rich_hickey#c_povjwe

                                    I think of protobufs/thrift as trying to “extend your type system over the network”. It works pretty well, but it’s also significantly different from a type system you would design when you “own the world”. Type systems inherently want a global view of your program and that conflicts with the nature of distributed systems.

                                    edit: this followup comment was more precise: https://lobste.rs/s/zdvg9y/maybe_not_rich_hickey#c_jc0hxo

                                  3. 2

                                    So this is really interesting. I read the paper on success typing and it seems pretty cool. It still, however, doesn’t guarantee soundness. Then on the other hand, neither does TypeScript, so it’s hard for me to make up my mind about what I want.

                                  4. 4

                                    That would be cool. There’s at least Rustler.

                                    1. 4

                                      Static types per se — that’s easy. But think about the distributed system with different versions of VMs. Think about live upgrades.

                                  1. 18

                                    In short, if you’re building a web application and you’re accepting input from users, you should always normalize it to a canonical form in Unicode.

                                    Strong disagree, you should use string comparisons that understand Unicode. You can’t achieve “intuitive matching” via normalization without discarding information from the user’s input.

                                    For example, the FFI ligature :

                                    > x = 'ffi'
                                    > x === 'ffi'
                                    false
                                    > ['NFC', 'NFD', 'NFKC', 'NFKD'].map(nf => x.normalize(nf) === 'ffi')
                                    [false, false, true, true]
                                    

                                    Only the K normalization forms will make this comparison equal. However:

                                    > ['NFC', 'NFD', 'NFKC', 'NFKD'].map(nf => x.normalize(nf))
                                    ["ffi", "ffi", "ffi", "ffi"]
                                    

                                    … you have lost information from the user’s input. has been decomposed to ffi. If you store the user’s data normalized then you’ve thrown things away.

                                    Instead, use localeCompare (or Intl.Collator) to properly do language-sensitive string comparisons:

                                    > 'ffi'.localeCompare('ffi', 'en', { sensitivity: 'case' })
                                    0
                                    

                                    As always, remember that equivalence is language-sensitive:

                                    > 'i'.localeCompare('I', 'en', { sensitivity: 'base' })
                                    0
                                    > 'i'.localeCompare('I', 'tr', { sensitivity: 'base' })
                                    1
                                    

                                    There are no simple one-size-fits-all solutions when dealing with text. You must consider your scenario and what you are trying to achieve. A rule like “always normalize everything” can cause trouble.

                                    1. 6

                                      One way to interpret the fine article is to normalize not when storing the data, but rather to normalize when performing string comparisons. I personally always store data as the user entered it, SQL injection attempts, XSRF attempts, and all, exactly as they entered it. I then normalize the data when in use, i.e. when comparing passwords, or outputting HTML. The only place where I would suggest normalizing text is in a separate full-text search storage engine, such as ElasticSearch.

                                      1. 1

                                        (Yes, that’s better, although I’m also fairly sure that NKDC/NKFC fail for comparisons in other ways, but I didn’t have time to come up with any.)

                                      2. 1

                                        When creating a file on macOS with HFS+, the filesystem driver always normalizes it and stores its filename in NFD form. So, it decomposes the characters if the user wants to create a filename passing a composed form. Then, user may access the file by passing both composed filename and decomposed filename.

                                        (Haven’t checked if that’s the case with APFS)

                                        1. 2

                                          APFS: store as is, compare under normalization

                                      1. 5

                                        You can also use Nerves on a Raspberry Pi: https://nerves-project.org/

                                        1. 3

                                          Yes.

                                          But GRiSP is real bare-metal, no OS involved.

                                          1. 2

                                            I onky skimmed it but it said RTEMS repeatedly. It’s a minimalist RTOS.

                                        1. 4

                                          I didn’t know about SO_REUSEPORT, which is pretty cool. I found this article on lwn about it: https://lwn.net/Articles/542629/

                                          It’s a shame that it’s apparently not a good tool to allow new servers to spin up and take over incoming connections without losing any. What is the right way to do such a thing? Do the BSDs (where the idea originated) have a better way of doing this?

                                          1. 3

                                            See SO_REUSEPORT_LB for FreeBSD

                                            1. 2

                                              You can pass the existing listening fd over a Unix socket to the new server, or just preserve the fd when you fork and exec by telling the newly exec’d process what the existing listening fd number is.

                                              1. 2

                                                I’m only familiar with the second approach from back when I dabbled with MUD servers (the server would fork(), then the parent would exec() the new version, and read its state back from the child through a pipe). Are you saying that you can pass file descriptors between arbitrary processes? I thought fds only made sense to a process (including through an exec()) and its children.

                                                1. 4
                                            1. 2

                                              Today in “All of the Operating Systems are broken”: We can’t be sure if what we tell the OS to write to disk is actually written to disk for realsies.

                                              1. 1

                                                FreeBSD (and possibly Illumos) are fine.

                                                1. 3

                                                  The wiki uses the word “presumably” for FreeBSD. That doesn’t really inspire any confidence.

                                                  1. 3

                                                    “Not confirmed broken” at least beats “confirmed broken” ;)

                                                    1. 1

                                                      Technically correct is the best kind of correct :)

                                              1. 1

                                                Very weird and unnecessary complicated way. What about

                                                long x; int y; x = y; y = x; //assume their sizes are different
                                                

                                                Not to mention values can be in registers (and they may not be of the same size).

                                                1. 1

                                                  I think that’s covered. I usually try to get people to diagram memory in boxes, so hopefully they’ll see the boxes are different sizes and comprehend the dangers of truncation.

                                                  I wouldn’t worry about registers. They’re not relevant to understanding how a C program operates. The abstract machine in the C standard is a little vague, so modeling your program as a concrete machine with a stack and a heap is a useful clarification, but I would always study a program’s behavior in the context of a pure memory architecture.