Trivia related to computer graphics + early Linux: Bruce Perens was the leader of Debian for awhile, and also worked at Pixar for 12 years.
I assume that Pixar was an early adopter of Linux, because otherwise they would have to pay commercial OS licensing fees for the hundreds / thousands of machines they used to render movies.
Although I read Ed Catmull’s recent book and I don’t think he mentioned Linux? That book did mention the NYIT graphics lab.
https://en.wikipedia.org/wiki/Bruce_Perens
https://en.wikipedia.org/wiki/New_York_Institute_of_Technology_Computer_Graphics_Lab
I vaguely recall some news around 2003 (I think it was) about Pixar switching from Sun to Intel hardware, and porting renderman.
found this: https://www.cnet.com/news/pixar-switches-from-sun-to-intel/
May have been what I was recalling.
Maybe they used SGI before that?
Cool! I didn’t know that.
You are probably right - buying SGI in the 2000s isn’t likely a smart move ;)
This story said they used SGI for desktops and Suns for rendering.
Also for @trousers.
They used Suns for trousers? Sparc64 pants? A novel usecase for sure. ;)
I kid, I kid. Thanks for the link. :)
They were rendering them in the movie. Had to get accurate lighting, ruffling, and so on. Geek producing it spent so much on the hardware they couldnt afford all the actors. Show got cancelled.
Many investors now suspect the Trouser Tour documentary was a ruse devised so the producer could play with a bunch of SGI and Sun boxes. Stay tuned for updates.
Hm, I used to convert web logs to JSON records – one per line – and then use grep to do a pre-filter! It can filter out 90% or 99% of the lines that need to be filtered, and then you parse JSON to get the exact filter.
grep is amazingly fast! This seems like the same idea taken a little further. I’ll have to look at how they do it in more detail.
Section 7.2 of the paper actually uses grep/ripgrep as a basis of comparison. It seems the two have the same or better performance than Sparser, which still wins out by a small margin for the most selective queries.
Yes. Always use grep first, even if one awk would do. This special one purpose only tool really cut the time down. Especially when you want to work on less than 100 million lines out of a billion lines.
I scanned over this, and OSH [1] should support basically everything here. If it doesn’t, feel free to file a bug!
https://github.com/oilshell/oil/issues
Hm now that I look more at the repo, it looks like this would be a good test: https://github.com/dylanaraps/pure-bash-bible/blob/master/test.sh
About analytics: You can do them on the server side by parsing your web logs! That used to be how everyone did it! Google Analytics popularized client side analytics using JavaScript around 2006 or so.
Unfortunately I feel like a lot of the open source web analytics packages have atrophied from disuse. But I wrote some Python and R scripts to parse access.log and it works pretty well for my purposes.
http://www.oilshell.org/ is basically what this article recommends, although I’m using both client-side and server-side analytics. I can probably get rid of the client-side stuff.
related: http://bettermotherfuckingwebsite.com/ (I am a fan of narrow columns for readability)
I agree, I used to use JAWStats a PHP web app that parsed and displayed the AWStats generated data files to provide visually appealing statistics a lot like Google analytics but entirely server side with data originating from apache/nginx log files.
It’s a shame that it was last worked on in 2009. There was a fork called MAWStats but that hasn’t been updated in four years either :(
For a while I self hosted my feed reader and web analytics via paid for apps, Mint and Fever by Shaun Inman but those where abandoned in 2006. It seems like all good software ends up dead sooner or later.
Maybe the GDPR will give these project a new breath.
They are much better for privacy aware people.
It’s been on my list of projects to attempt for a while, but my static site generator Tapestry takes up most of my spare time.
I currently use GoAccess myself, the only thing that would make the HTML reports better is seeing a calendar with visit counters against days.
I saw SAT solvers as academically interesting but didn’t think that they have many practical uses outside of other academic applications. … I have to say that modern SAT solvers are fast, neat and criminally underused by the industry.
Echoing a good comment on reddit: The author didn’t list any practical applications!!! How can you then say they are criminally underused?
The only one I know of is writing versioned dependency solver for a package manager (in the style of Debian’s apt). However, very few people need to write such code.
What are some other practical applications? I think they are all quite specialized and don’t come up in day-to-day programming. Happy to be proven wrong.
EDIT: I googled and I found some interesting use cases, but they’re indeed specialized. Not something I’ve done or seen my coworkers do:
https://www.quora.com/What-are-the-industrial-applications-of-all-SAT-solvers
http://www.carstensinz.de/talks/RISC-2005.pdf
I can think of a certain scheduling algorithm that might have used a SAT solver, but without details I can’t be sure.
They see some use in formal methods for model checking. The Alloy Analyzer converts Alloy specs to SAT problems, which is one of the reasons its such a fast solver.
There’s also this talk on analyzing floor plans.
A previous submission on SAT/SMT might help you answer that.
I’ve used Z3 to verify that a certain optimized bitvector operation is equivalent to the obvious implementation of the intended calculation.
Just typed up the two variants as functions in the SMT language with the bitvector primitives and asked Z3 for the satisfiability of f(x) != g(x) and rejoiced when it said “unsatisfiable.”
I’ll just post it here. :)
(define-sort Word () (_ BitVec 64))
(define-fun zero () Word (_ bv0 64))
;; Signed addition can wrap if the signs of x and y are the same.
;; If both are positive and x + y < x, then overflow happened.
;; If both are negative and x + y > x, then underflow happened.
(define-fun
add-overflow-basic
((x Word) (y Word)) Bool
(or (and (bvslt x zero)
(bvslt y zero)
(bvsgt (bvadd x y) x))
(and (bvsgt x zero)
(bvsgt y zero)
(bvslt (bvadd x y) x))))
;; Here is a clever way to calculate the same truth value,
;; from _Hacker's Delight_, section 2.13.
(define-fun
add-overflow-clever
((x Word) (y Word)) Bool
(bvslt (bvand (bvxor (bvadd x y) x)
(bvxor (bvadd x y) y))
zero))
(set-option :pp.bv-literals false)
(declare-const x Word)
(declare-const y Word)
(assert (not (= (add-overflow-basic x y)
(add-overflow-clever x y))))
(check-sat)
Here’s you an example of SMT solvers used for stuff like that. I added some more stuff in comments. You might also like some examples of Why3 code which is translated for use with multiple solvers. Why3 is main way people in verification community use solvers that I’m aware. WhyML is a nice, intermediate language.
I found this library (and its docs) interesting. They go over some practical examples.
This looks like it’s about linear programming, not SAT solving. They’re related for sure but one reason it would be nice to see some specific examples is to understand where each one is applicable!
I have personally used them when writing code to plan purchases from suppliers for industrial processes and to deal with shipping finished products out using the “best” carrier.
Neat stuff!
I wonder what @andyc has to say about Oh vs. Oil :)
Thanks for the shout out :) I listened to the end of the talk (thanks for the pointer by @msingle), and basically everything he says is accurate. I found his 2010 master’s thesis several years ago, and it is a great read. Section 3 on Related Work is very good.
https://scholar.google.com/scholar?cluster=9993440116444147516&hl=en&as_sdt=0,33
Oh and Oil have very similar motivations – treating shell as a real programming language while preserving the interactive shell. Oh seems to be more Lisp-like while Oil simply retains the compositionality of the shell; it doesn’t add the compositionality of Lisp. (In other words, functions and cons cells compose, but processes and files also compose).
I mention NGS here, which probably has even more similar motivations:
http://www.oilshell.org/blog/2018/01/28.html
The main difference between Oh and Oil is the same as the difference between NGS/Elvish/etc. and Oil: Oil is designed to be automatically converted from sh/bash.
My thesis is that if this conversion works well enough, Oil could replace bash. If I just designed a language from scratch, I don’t think anyone would use it. Many people seem to agree with this. After all, fish has existed for 15 years, and is a nicer language than bash for sure, but I’ve seen it used zero times for scripts (and I’ve looked at dozens if not a hundred open source projects with shell scripts.)
However as he correctly notes (and I point out in the FAQ), Oil doesn’t exist yet! Only OSH exists.
The experience of implementing OSH and prototyping the OSH-to-Oil gave me a lot of confidence that this scheme can work. However it’s taking a long time, longer than expected, like pretty much every software project ever.
I’m not really sure how to accelerate it. Maybe it will get there and maybe it won’t :-/ No promises!
I “front-loaded” the project so that if I only manage to create a high-quality implementation of OSH (a bash replacement), then I won’t feel bad. I’m pretty sure people will use that if it exists.
I maintain a “help wanted” tag for anybody interested in contributing:
https://github.com/oilshell/oil/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22
This is mainly helping with OSH, as Oil is a concentrated design/implementation effort that is hard to parallelize. Feel free to join https://oilshell.zulipchat.com too!
Michael MacInnis mentions talking to @andyc ~44:25 in the talk, but I’d also like to hear his opinion on Michael’s work.
Oh! (no pun intended :p) I’m still at the ~33:00 mark into the video and hadn’t gotten to that part. But yeah, I’d like to hear that.
Wow great to see this! I remarked after one of the chapters in craftinginterpreters that I didn’t find a lot of literature on bytecode VMs.
There are a lot of books on compilers, but not as much for interpreters (i.e. the bytecode compiler + dynamic runtime combo that many languages use).
There are the Lua papers, a few blog posts about Python, some papers about the predecessor to OCaml, and craftinginterpreters, but not much else I could find. I found this book recently, but it’s not specifically about the compiler / VM:
http://patshaughnessy.net/ruby-under-a-microscope
Anyway I am glad to see another addition to this small space! :)
I’m hoping to find some time to really push into my compiler / VM project this winter: http://www.oilshell.org/blog/2018/03/04.html .
Basically the idea is that Python has a “dumb” compiler and a very rich runtime. (This fact has been impressed upon me by hacking on its source code!). I want to make it have more of a smart compiler and small/dumb runtime.
There’s also Nils M Holm’s books: http://t3x.org/
Which parts discuss bytecode interpreters? I see a lot of different things there, including native code compilers, but no bytecode interpreters.
One of the features of the T3X compiler is a portable bytecode interpreter. Here’s the source, I think you’ll like it: https://www.t3x.org/t3x/t.t.html
There are a lot of books on compilers, but not as much for interpreters (i.e. the bytecode compiler + dynamic runtime combo that many languages use).
Not a book, but you still might find this paper interesting: Engineering Definitional Interpreters:
Abstract: A definitional interpreter should be clear and easy to write, but it may run 4–10 times slower than a well-crafted bytecode interpreter. In a case study focused on implementation choices, we explore ways of making definitional interpreters faster without expending much programming effort. We implement, in OCaml, interpreters based on three semantics for a simple subset of Lua. We compile the OCaml to x86 native code, and we systematically investigate hundreds of combinations of algorithms and data structures. In this experimental context, our fastest interpreters are based on natural semantics; good algorithms and data structures make them 2–3 times faster than interpreters. Our best interpreter, created using only modest effort, runs only times slower than a mature bytecode interpreter implemented in C.
Wow thanks! This is exactly the kind of thing I’m looking for.
For example, even the first sentence is somewhat new to me. Has anyone else made a claim about how much slower a tree interpreter (I assume that’s what they mean by definitional) is than a bytecode interpreter? I’ve never seen that.
I know that both Ruby and R switched from tree interpreters to bytecode VMs in the last 5-8 years or so, but I don’t recall what kind of speedup they got. That’s something to research (and would make a good blog post).
Anyway I will be reading this and following citations :-) I did find a nice paper on the design of bytecode VM instructions. Right now choosing instructions seems to be in the category of “folklore”. For example, there are plenty of explanations of Python’s bytecode, but no explanations of WHY they are as such. I think it was basically ad hoc evolution.
I did find a nice paper on the design of bytecode VM instructions.
Would mind sharing what that paper is? I’d love to read more about how to do this properly.
I’m pretty sure this is the one I was thinking of:
ABSTRACT MACHINES FOR PROGRAMMING LANGUAGE IMPLEMENTATION
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.68.234
We present an extensive, annotated bibliography of the abstract machines designed for each of the main programming paradigms (imperative, object oriented, functional, logic and concurrent). We conclude that whilst a large number of efficient abstract machines have been designed for particular language implementations, relatively little work has been done to design abstract machines in a systematic fashion.
A good term to search for is “abstract machine” (rather than bytecode interpreter). The OCaml paper is called the “ZINC Abstract Machine” and it’s quite good.
There is a bunch of literature on stuff like the SECD machine, which is an abstract machine for Lisp, and which you can find real implementations for.
There seems to be less literature on stack / register bytecode interpreters. The Lua papers seem to be the best read in that area.
This guy is asking a similar question: https://stackoverflow.com/questions/1142848/bytecode-design
I think practically all “Why You Should…” articles would be improved if they became “When You Should…” articles with corresponding change of perspective.
An even better formulation would be “Here is the source code for an app where I didn’t use a framework. It has users, and here are my observations on building and deploying it”.
In other words, “skin in the game” (see Taleb). I basically ignore everyone’s “advice” and instead look at what they do, not what they say. I didn’t see this author relate his or her own experience.
The problem with “when you should” is that the author is not in the same situation as his audience. There are so many different programming situations you can be in, with different constraints, and path dependence. Just tell people what you did and they can decide whether it applies to them. I think I basically follow that with http://www.oilshell.org/ – I am telling people what I did and not attempting to give advice.
(BTW I am sympathetic to no framework – I use my own little XHR wrapper and raw JS, and my own minimal wrapper over WSGI and Python. But yes it takes forever to get things done!)
His earlier books are also good. It is a lot of explaining the same ideas in many different ways, but I find that the ideas need awhile to sink in, so that’s useful.
He talks about people thinking/saying one thing, but then acting like they believe its opposite. I find that to be painfully true, and it also applies to his books. You could agree with him in theory, but unless you change your behavior then you might not have gotten the point :-)
Less abstractly, the worst manager I ever had violated the “skin in the game” rule. He tried to dictate the technology used in a small project I was doing, based on conversations with his peers. That technology was unstable and inappropriate for the task.
He didn’t have to write the code, so he didn’t care. I was the one who had to write the code, so I’m the one with skin in the game, so I should make the technology choices. I did what he asked and left the team, but what he asked is not what the person taking over wanted I’m sure.
In software, I think you can explain a lot of things by “who has to maintain the code” (who has skin in the game). I think it explains why the best companies maintain long term software engineering staff, instead of farming it out. If you try to contract out your work, those people may do a shitty job because they might only be there for a short period. (Maybe think of the healthcare.gov debacle – none of the engineers really had skin in the game.)
It also explains why open source code can often be higher quality, and why it lasts 30+ years in many cases. If the original designer plans on maintaining his or her code for many years, then that code will probably be maintainable by others too.
It also explains why “software architect” is a bad idea and never worked. (That is, a person who designs software but doesn’t implement it.)
I’m sure these principles existed under different names before, and are somewhat common sense. But they do seem to be violated over and over, so I like to have a phrase to call people on their BS. :-)
Yeah, the phrase works as a good lens and reminder. Interestingly, as most parents will attest to - the “do as I say not as I do” is generally unsuccessful with kids. They are more likely to emulate than listen.
I definitely agree with this change. It’d get more people thinking architecturally, something that’s sorely needed.
I agree that make is too freaking hard. it’s a terrible tool and you don’t have to use it. It took me years to realize this. I deleted the makefiles from my projects. I no longer use makefiles.
Yup. I should also write a blog post on “invoking the compiler via a shell script”.
The main thing to know is that the .c source files and -l flags are order dependent. With a makefile, most people use separate processes to compile and link so I think it doesn’t come up as much.
I don’t use Make as a build tool, but I find it quite handy to collect small scripts and snippets with PHONY targets that don’t attempt any dependency tracking. Make is almost universally available, the simple constructs I use are portable between gmake and BSD make, and almost every higher-level tool out there understands Makefile — so coworkers using various IDEs and the command lin can all discover and run the “build”, “this test”, “download dependencies”, “run import process”, “lint”, etc tasks. If I need a task that’s more than two lines, I put it in a shell script.
Although some languages now come with tooling that understands scripts, such as Cargo or NPM, I still find a Makefile useful for polyglot projects or when it’s necessary to modify the environment before calling down to that language specific tooling.
Yes, I want to write about this too! You are using Make like a shell script :)
I use this pattern in shell:
# run.sh
build() {
...
}
test() {
...
}
"$@"
Then I invoke with
$ run.sh build
...
$ run.sh test
I admit that Make has a benefit in that the targets are auto-completed on most distros. But I wrote my own little auto-complete that does this. I like the simplicity of shell vs. make, and the syntax highlighting in the editor.
When I need dependency tracking, I simply invoke make from the shell script! Processes compose.
You’ll see this in thousands of lines of shell scripts (that I wrote) in the repo:
About two years ago I finally sat down and read the GNUMake manual. It’s very readable, and it’s more capable than just about any other make out there. For one project, the core of the Makefile is:
%.a :
$(AR) $(ARFLAGS) $@ $?
libIMG/libIMG.a : $(patsubst %.c,%.o,$(wildcard libIMG/*.c))
libXPA/src/libxpa.a : $(patsubst %.c,%.o,$(wildcard libXPA/src/*.c))
libStyle/libStyle.a : $(patsubst %.c,%.o,$(wildcard libStyle/*.c))
libWWW/libWWW.a : $(patsubst %.c,%.o,$(wildcard libWWW/*.c))
viola/viola : $(patsubst %.c,%.o,$(wildcard viola/*.c)) \
libIMG/libIMG.a \
libXPA/src/libxpa.a \
libStyle/libStyle.a \
libWWW/libWWW.a
The rest of it is defining the compiler and linker flags (CC, CFLAGS, LDFLAGS, LDLIBS) and some other targets (clean, depend (one command line to generate the dependencies), install, etc). And this builds a program that is 150,000 lines of code. I can even do a make -j to do a parallel build. I’m not entirely sure where all this make hate comes from.
I’ve read the GNU make manual (some parts multiple times) and written 3 significant makefiles from scratch. One of them is here:
https://github.com/oilshell/oil/blob/master/Makefile (note that it includes .mk fragments)
It basically works but I’m sure that there are some bugs in the incremental and parallel builds. I have to make clean sometimes and I’m not brave enough do parallel builds. How would I track these bugs down? I have no idea. I tried but I kept breaking other things, and I got no feedback about this.
In other words, it’s extraordinarily difficult to know whether your incremental build is correct, and whether your parallel build is correct. Make offers you no help there essentially.
There are a lot of other criticisms out there, but if you scroll down here you’ll see mine:
http://www.oilshell.org/blog/2017/05/31.html
(correctness, gcc -M, wrong defaults for .SECONDARY, etc.)
There is also a debugging incantation I use that I had to figure out with some hard experience. Basically I disable the builtin rules database and enable verbose mode.
Another criticism is that the builtin rules database can make builds significantly slower.
I’m not using Make for a simple problem, but most build problems are not simple! It is rare that you just want to build a few C files in a portable fashion. For that, it’s fine. But most systems these days are much more complex than that. Multiple languages and multiple OSes lead to an explosion and complexity, but the build system is the right place to handle those problems.
I somehow seem to miss these “complex builds that break Make.” I have a project that uses C, C++ and Lua in a single executable and make handled it fine (and that includes compiling the Lua code into Lua bytecode, then transforming that into a C file which is then compiled into an object file for final inclusion in the executable).
I don’t know. For as bad as make is made out to be, I’ve found the other supposed solutions to be worse.
Over on the orange site, commenters are saying that NeXT failed because they didn’t get many customers, then got bought by Apple who didn’t immediately see a turnaround. From a startup perspective though, NeXT managed to live off the funding they could get until they made it to “exit” and they proved to be of immense strategic value to the acquiring company.
But the lesson I take from NeXT is that they were great - better than many would associate with Steve Jobs - at the compromise. They looked at the Mac (quite closely of course, with so many ex-Apple staff on board, leading to the lawsuit that stopped them competing in the PC market), they looked at the Alto and Smalltalk, and they built the bits of the Alto that the Mac missed with existing technology. The GUI came from Adobe, OOP from Stepstone, the image model was simulated first with removable media then with NFS, with ethernet networking coming from 4.2BSD and Mach, which also made the whole lot accessible to people with existing software. They could have gone down the Be route and started from scratch, but they licensed and Free Softwared their way to having a product.
Yeah I agree that you often have to cobble something together from existing parts to achieve something big. That’s one of the lessons I learned from reading Stallman’s biography. As far as I remember, GCC, Emacs, and many other GNU projects all started from borrowed code.
They didn’t just start typing in a blank text file. That would have pushed it from a “huge undertaking” to “unfinishable”.
The key though is you have to deeply understand all those parts and not just blindly copy them… otherwise you’ll get an incoherent system. But if you choose carefully, this can really accelerate the project and make it possible to ship.
Thanks! It’s going well, but more slowly than I would like, which is pretty much every software project ever :)
I wish I had time to work on the Oil language, but I’m sort of putting that off until OSH is in better shape and has users, which I think requires OPy and at least one other thing.
I haven’t had as much time to participate here and on /r/programminglanguages, but I noticed this post [1] and the one about flags vs. args. It sounds like we’re thinking about things very much along the same lines.
Just like there are memory-safe languages, there should be “string-safe” languages. I’m not sure if Oil will get there, but it definitely needs to go on that direction. This related to my comment about security here [2]
So yeah hopefully I can get to Oil sooner rather than later, and I would love to have your input. Or maybe we can think of some way to graft safety onto OSH. The idea of shell doing all the command line parsing and then serializing to
cmd "${flags[@]}" -- "${args[@]}"
I think is somewhat useful / promising. Basically, the shell is completely ignorant of which things are flags and which things are args right now. But it doesn’t have to be. It might not even require Oil.
[1] https://rain-1.github.io/escaping.html
[2] https://lobste.rs/s/jn62zk/persuasive_language_for_language#c_avqn7r
Not surprised. Oracle’s Java copyright lawsuit going the way it is, Google is going to be license price extorted if they stick to Java so they pretty much need to kill Android if the lawsuit succeeds. Working their own version also gives them the copyrights so the ruling will be helpful in maintaining a tight grip on the new platform.
As I understand it (and as noted by alva above), Fuschia competes with Linux, not Java. It’s a microkernel, not a language VM or a language. The article was technically confused – the Oracle line was just a throwaway and not terribly accurate.
Java is going to be in Android forever, simply because there are hundreds of thousands of apps written in Java. Kotlin was a logical move because it’s very compatible with Java.
I assume the thing everyone is kind of getting at is Flutter, which is the preferred IDE for Fuchsia and it’s not Java-encumbered.
Working on a compiled implementation of Javascript - https://github.com/timruffles/js-to-c. Started as an answer to SICP exercise 5.52 (https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-35.html#%_thm_5.52)
This is cool! I’ve been thinking of doing something like this for my Python compiler:
http://www.oilshell.org/blog/2018/03/04.html
That is, instead of compiling to bytecode, compile to C.
Have you tackled garbage collection yet? I suppose it is no harder to do in C code than interpreted bytecode, but I haven’t really gotten into the details.
Thanks! Will have to take a look at your Python work.
Re GC I’ve sketched out a plan to do something similar to the SICP stop and copy GC:
Actually I have a more immediate question besides GC: Are you handling JavaScript exceptions, and if so, how?
I was thinking of compiling down a control flow graph and then the generated C code would just have “goto” / jumps instead of while/for/if/etc.
As an alternative, maybe there is some way to hack exceptions with setlongjmp() ?
About GC, I was researching the stop-and-copy strategy, as well as mark-and-sweep. It seems like almost every language does mark-and-sweep, but the “compaction” property of stop-and-copy appealed to me. I think I saw that the es shell used stop-and-copy (although the project iself is basically defunct).
One thing I wasn’t sure about was moving pointers. If you want to have a C API, then C code might hold pointers to JS/Python objects, and then you don’t want to move those objects. I guess this can be solved by another layer of indirection (e.g. immovable stubs).
But I haven’t gotten very far with this – just brainstorming!
Spookily, yes, it’s the other big topic on my mind! I’ve already written up my thoughts on setjmp or the more explicit goto strategy you identified. I’m going for setjmp because it felt like everything you can do in JS can throw, so the explicit route would be lots of code.
Re the C API, yes that would certainly make stop’n’copy awkward!
Isn’t yours a scripting language for quick-and-dirty commands like BASH? If so, can’t you just do reference counting or something? I don’t see them running long enough for things like compaction to matter. I mean, maybe you’ll do it for fun or to feed into a future project. I just think simpler stuff might be a better default if it’s short-running scripts.
Actually shell does not require any GC or ref counting whatsoever, because strings are just copied everywhere! Same with awk. I didn’t realize this before implementing a shell!
But this is for OPy – the subset of Python that Oil is implemented in. The whole parser is written in Python, and I want to compile to either C or my own bytecode, and in either case you might reasonably want GC.
One reason is that parsers creates lots of tiny objects. Although, it might be reasonable to just hack some special case “arena” support into the VM, and forget about GC, which is a big implementation effort.
I think that the “ephemeral process” model of shell makes GC and memory management somewhat unnecessary. You can just allocate and throw the whole process away. But for the shell itself, there are two places where memory management matters:
If those 2 use cases can be handled with manually allocated arenas and not GC, maybe I don’t need GC. But the problem with arenas is that I want to detect invalid references if you make a “free” error. I’m not sure how to do that, at least without slowing down common cases.
“Actually shell does not require any GC or ref counting whatsoever, because strings are just copied everywhere! “
Seems like that offers opportunity for efficiency improvement in a system where they’d pass pointers to strings instead of copy them. Probably what the capability-based systems did or could’ve done.
“ Although, it might be reasonable to just hack some special case “arena” support into the VM”
That’s what I was thinking when you said “ephemeral.” In networking and games, I’ve seen people use memory pools that let you fast allocate the first N values, let you get more in chunks if you need, and release it all at once when done. Really easy to implement. For determining size, you might write a little profile that runs a lot of bash scripts through it measuring typical and worst-case uses. I know you already have a bunch for compatibility testing. Then, just pick starting and allocation size that fits most of that well.
What you think?
Yes that’s definitely in line with how I think about things. If you have very good test coverage it opens up a lot of possibilities for feedback-based / data-driven optimization.
I need to get the compiler/VM working first, which will inevitably be without memory reclamation, since that is mostly a separate concern. And then I will see how I can fit in arenas or GC.
It’s a little complicated because I’m not sure if the language will have the concept of stack and heap. Python really doesn’t have that – logically everything is on the heap and garbage collected.
So it’s not clear you want to allocate stack variables on the arena meant for the syntax tree.
This sort of relates to the “generational hypothesis” as far as I understand. Most loca” variables are expected to be in the young generations, but this is not a hard-and-fast guarantee. It’s just a heuristic.
But if OVM doesn’t have GC then there might be “garbage” from local vars?. I think that OPy will be more value-based than Python, but it is constrained by the fact that it is valid Python!
In other words, the first cut might be really rough and waste a lot of memory :-/
Maybe our idea of the “web” is what’s too small! Web Assembly is more than appropriately named in my opinion for how well it works for the transfer and immediate evaluation of procedures over networks, but the imagination of many for the idea of the web is lost beyond the horizon of their web browser. There’s a lot of our thinking that could use a bit of Imagination Fuel
I’m very much on that team. I’m giving a full conference talk about it in Barcelona next week. Different framing work well for different people, so I picked this one for this post, as I wanted it to be short, and this framing is shorter.
Didn’t mean it to be nitpicking, it’s too easy to accidentally write posts in that style lol. Very good article and I’m excited for any future ones about this topic!
Friday: https://jscamp.tech/schedule/
It says TBD but it’s a wasm talk.
Yeah I think we browsers have become too big, monolithic, and homogeneous. I would like to see more diversity in web clients. Those clients could use WebAssembly outside the context of the browser.
The browser has a very specific and brittle set of security policies, and WebAssembly doesn’t change that. It will inherit the same problems that JavaScript has.
Sort of! You know, at parse time, every function a wasm program could call. This is extremely useful in a security context.
Imagination Fuel
Love It. The captures a lot of meetings I have been trying to have with folks at work. They are thinking low level performance fixes for things, while necessary, they are having a huge problem jumping up a couple abstraction levels and thinking transformatively.
Many of the author’s experiences speaking with senior government match my own.
However, there’s one element that I think is very easily lost in this conversation, and which I want to highlight: there is no group I spend more time trying to convince of the importance of security than other software engineers.
Software engineers are the only group of people I’ve ever had push back when I say we desperately need to move to memory safe programming languages. All manner of non-engineers, when I’ve explained the damages wrought by C/C++, and how nearly every mass-vulnerability they know about has a shared root cause, generally understand why this is an important problem, and want to discuss ideas about how do we resolve this.
Engineers complain to me that rewriting things is hard, and besides if you’re disciplined in writing C and use sanitizers and fuzzers you’ll be ok. Rust isn’t ergonomic enough, and we’ve got a really good hiring pipeline for C++ engineers.
If we want to build software safety into everything we do, we need to get engineers on board, because they’re the obstacle.
People don’t even use sanitizers and fuzzers, so I’m not sure why you would expect them to rewrite in Rust. It’s literally 1000x less effort.
As far as I can tell, CloudFlare’s CloudBleed bug would have been found if they compiled with ASAN and fed about 100 HTML pages into it. You don’t even have to install anything; it’s built right into your compiler! (both gcc and Clang)
I also don’t agree that “nearly every mass vulnerability has a shared root cause”. For example, you could have written ShellShock in Rust, Python, or any other language. It’s basically a “self shell-code injection” and has very little to do with memory safety (despite a number of people being confused by this.)
The core problem is the sheer complexity and number of lines of unaudited code, and the fact that core software like bash has exactly one maintainer. There are actually too many people trying to learn Rust and too few people maintaining software that everybody actually uses.
In some sense, Rust can make things worse, because it leads to more source code. We already have memory-safe languages: Python, Ruby, JavaScript, Java, C#, Erlang, Clojure, OCaml, etc.
Software engineers should definitely spend more time on security, and need to be educated more. But the jump to Rust is a non-sequitur. Rust is great for kernels where the above languages don’t work, and where C and C++ are too unsafe. But kernels are only a part of the software landscape, and they don’t contain the majority of security bugs.
I would guess that most data breaches these days have nothing to do with memory safety, and have more to do with bugs similar to the ones in the OWASP top 10 (e.g. XSS, etc.)
https://www.owasp.org/images/7/72/OWASP_Top_10-2017_%28en%29.pdf.pdf
Edit: as another example, Mirai has nothing to do with memory safety:
https://en.wikipedia.org/wiki/Mirai_(malware)
All it does it try default passwords, which gives you some idea of where the “bar” is. Rewriting software in Rust has nothing to do with that, and will actually hurt because it takes effort and mindshare away from solutions with a better cost/benefit ratio. And don’t get me wrong, I think Rust has its uses. I just see people overstating them quite frequently, with the “why don’t more people get Rust?” type of attitude.
There were languages like Opa that tried to address what happened on web app side. They got ignored just like people ignore safety in C. Apathy is the greatest enemy of security. It’s another reason we’re pushing the memory-safe, higher-level languages, though, with libraries for stuff likely to be security-critical. The apathetic programmers do less damage on average that way. Things that were code injections become denial of service. That’s an improvement.
not only software engineers, almost the entire IT industry has buried it’s head in the sand and is trying desperately hard to hide from the problem, because “security is too hard”. We are pulling teeth to get people to even do the minimal upgrades to things. I recently had a software vendor refusing to support anything other than TLS 1.0. After many exchanges back and forth, including an article from Microsoft(and basically every other sane person) saying they were dropping all support of older TLS protocols because of their insecurity, they finally said, OK we will look into it. I’m sure we all have stories like this.
If you can’t even bother to take the minimum of steps to upgrade your security stacks after more than a decade,(TLS1.0 released in 1999 and TLS 1.2 is almost exactly a decade old now) because it’s “too hard”, trying to get people to move off of memory unsafe languages like C/C++ is a non-starter.
But I agree with you, and the author.
I would like to use TLS 1.3 for an existing product. It’s in C and Lua. The current system is network driven using select() (or poll() or epoll() depending upon the platform). The trouble I’m having is finding a library that is easy, or even a bit complicated but sane to use. The evented nature means I an notified when data comes in, and I want to feed this to the TLS library instead of having the TLS library manage the sockets for me. But the documentation is dense, the tutorials only cover blocking calls, and that’s when they’re readable! Couple this with the whole “don’t you even #$@#$# think of implementing crypto” that is screamed from the roof tops and no wonder software engineers steer away from this crap.
I want a crypto library that just handles the crypto stuff. Don’t do the network, I already have a framework for that. I just need a way to feed data into it, and get data out of it, and tell me if the certificate is good or not. That’s all I’m looking for.
OpenBSD’s libtls.
TLS 1.3 is not quite ready for production use, unless you are an early adopter like Cloudfare. Easy to use API’s that are well-reviewed are not there yet.
Crypto Libraries: OpenBSD’s libtls like @kristapsdz mentioned, or libsodium/nacl or OpenSSL. If it’s just for your internal connections and don’t actually need TLS, just talking to libsodium or NaCL for an encrypted stream of bytes is probably your best bet, using XSalsa20+Poly1305. See: https://latacora.singles/2018/04/03/cryptographic-right-answers.html
TLS is a complicated protocol(TLS1.3 reduces a LOT of complexity, it’s still very complicated).
If you are deploying to Apple, Microsoft or OpenBSD platforms, you should just tie to the OS provided services, that provide TLS. Let them handle all of that for you(including the socket). Apple and MS platforms have high-level API’s that will do all the security crap for you. OpenBSD has libtls.
On other platforms(Linux, etc), you should probably just use OpenSSL. Yes it’s a fairly gross API, but it’s pretty well-maintained nowadays(5 years ago, it would not qualify as well maintained.). The other option is libsodium/NaCL.
Okay, fine. Are there any crypto libraries that are easy to use for whatever is current today? My problem is: a company that is providing us information today via DNS has been invaded by a bunch of hipster developers [1] who drunk the REST Kool-Aid™ so I need a way to make an HTTPS call in an event driven architecture and not blow our Super Scary SLAs with the Monopolistic Phone Company (which would case the all-important money to flow the other way), so your advice to let OS provided TLS services control the socket is a non-starter.
And for the record, the stuff I write is deployed to Solaris. For reasons that exceed my pay grade.
So I read the Cryptographic Right Answers you linked to and … okay. That didn’t help me in the slightest.
The program I’m working on is in C, and not written by me (so it’s in “maintenance mode”). It works, and rewriting it from scratch is probably also a non-starter.
Are you getting a sense of the uphill battle this is?
[1] Forgive my snarky demeanor. I am not happy about this.
Edit: further clarification on what I have to work with.
I get it, it sucks sometimes. I’m guessing you are not currently doing any TLS at all? So you can’t just upgrade the libraries you are currently using for TLS, whatever they are.
In my vendor example, the vendor already implemented TLS (1.0) and then promptly stopped. They have never bothered to upgrade to newer versions of TLS. I don’t know the details of their implementation, obviously, since it’s closed-source; but unless they went crazy and wrote their own crypto code, upgrading their crypto libraries is probably all that’s required. I’m not saying it’s necessarily easy to do that, but this is something everyone should do at least once every decade, just to keep the code from rotting a terrible death anyways. TLS 1.2 becomes a decade old standard next month.
I don’t work on Solaris platforms (and haven’t in at least a decade, so you are probably better off checking with other Solaris people). Oracle might have a TLS library these days, I have no clue. I tend to avoid Oracle land whenever possible. I’m sorry you have to play in their sandbox.
I agree the Crypto right-answers page isn’t useful for you, since you just want TLS, It’s target is for developers who need more than TLS. I used it here mostly as proof of why I recommended XSalsa20+Poly1305 for symmetric encryption. Again, you know you need TLS, so it’s a non-useful document for you at this point.
Event driven IO is possible with OpenSSL, but it’s not super easy see: https://www.openssl.org/docs/faq.html#PROG11. Then again, nothing around event driven IO is super easy. Haproxy and Nginx both manage to do it, and are both open-source implementations of TLS, so you have working code you can go examine. Plus it might give you access to developers who have done event driven IO with TLS. I haven’t ever written that implementation, so I can’t help with those specifics.
OpenSSL is working on making their API’s easier to use, but it’s a long, slow haul, but it’s definitely a known problem, and they are working on it.
As for letting the OS do the work for you, you are correct there are definitely use-cases where it won’t work, and it seems you fit the bill. For most applications, letting the OS do it for you is generally the best answer, especially around Crypto which can be hard to get right, and of course only applies to the platforms that offer such things(Apple, MS, etc). Which is why I started there ;)
Anyways, good luck! Sorry I can’t just point to a nice easy example, for you. Maybe someone else around here can.
I’m not even using TCP! This is all driven with UDP. TCP complicates things but is manageable. Adding a crap API between TCP and my application? Yeah, I can see why no one is lining up to secure their code.
I think there is a communication issue here.
The vendor you are connecting with over HTTPS supports UDP packets on a REST API interface? really? Crazier things have happened I guess.
I think what you are saying is you are doing DNS over UDP for now, but are being forced into HTTPS over TCP?
DNS over UDP is very far away from a HTTPS rest API.
Anyways, for being an HTTPS client, against a HTTPS REST API over TCP, you have 2 decent options:
Event driven/async: use libevent, example code: https://github.com/libevent/libevent/blob/master/sample/https-client.c
But most people will be boring, and use something like libcurl (https://curl.haxx.se/docs/features.html) and do blocking I/O. If they have enough network load, they will setup a pool of workers.
Right now, we’re looking up NAPTR records over DNS (RFC-3401 to RFC-3404). The summary is that one can query name information for a given phone number (so 561-555-5678 is ACME Corp.). The vendor wants to switch to a REST API and return JSON. Normally I would roll my eyes at this but the context I’m working in is more realtime—as in Alice is calling Bob and we need to look up the information as the call is being placed! WE have a hard deadline with the Monopolistic Phone Company to provide this information [1].
We don’t use libevent but I’ll look at the code anyway and try to make heads and tails.
[1] Why are we querying a vendor this for? Well, it used to be in house, but now “we lease this back from the company we sold it to - that way it comes under the monthly current budget and not the capital account.” (at least, that’s my rational for it).
Tell me how it goes. Fwiw, you might want to take a quick look at mbed TLS. Sure it wants to wrap a socket fd in its own context and use read/write on it, but you can still poll that fd and then just call the relevant mbedtls function when you have data coming in. It does also support non-blocking operation.
https://tls.mbed.org/api/net__sockets_8h.html#a2ee4acdc24ef78c9acf5068a423b8c30 https://tls.mbed.org/api/net__sockets_8h.html#a03af351ec420bbeb5e91357abcfb3663
https://tls.mbed.org/api/structmbedtls__net__context.html
https://tls.mbed.org/kb/how-to/mbedtls-tutorial (non-blocking io not covered in the tutorial but it doesn’t change things much)
I’ve no experience with UDP (yet – soon I should), but if you’re doing that, well, mbedtls should handle DTLS too: https://tls.mbed.org/kb/how-to/dtls-tutorial (There’s even a note relevant to event based i/o)
We use mbedtls at work in a heavily event based system with libev. Sorry, no war stories yet, I only got the job a few weeks ago.
Oddly - this sounds like the author has just discovered dependency injection? I would have thought that concept would translate pretty well to Go. I’ve written a lot of Go, but I cut my teeth largely on C, C++, and C# so dependency injection has always been on my radar. When I wrote Go, I learned it and largely applied my own lessons from C, C++, and C#.
Due to compiler constraints and the language’s ethos, global state and func init feel weird in Rust (my current language). You can’t, without jumping through hoops, create objects that are partially constructed (e.g. using Option for uninitialized types). That said, even if you’ve got struct members that are of type Option, you are actually initializing it to something - sometimes it’s just None.
I don’t have enough context in Go land to know why this author’s argument might be a novel conclusion. Does anyone have some context? I’d love to learn more.
Many Go programmers seem to feel very comfortable with global state. When I join new organizations or projects, I often find myself needing to educate and socialize the problems that come from that. This post is just a formalization of the things I’ve been saying informally for a long time.
I wish I knew why this was so relatively prevalent in the Go ecosystem. If I had to guess, I’d speculate that it’s because a lot of the example code in e.g. books and tutorials doesn’t really shy away from global state.
It’s also related to the standard library itself having lots of globals. Which itself leads to bad things, like the cryptographic randomness source being trivially modifiable: https://github.com/golang/go/issues/24160
The Go community has a strong culture of writing application-specific code that is “good enough”, and tends to err strongly on the side of avoiding premature abstraction. For a significant number of use cases, globals (combined with a reasonable amount of documentation, testing, and good discipline) tend to be the “good enough” solution.
The thesis of Go’s culture is that premature abstraction often costs more than rewriting code. The argument is that you often know your domain better after writing a “good enough” first version, and that premature abstractions lock you in to specific designs more tightly that may be harder to change down the line.
It’s definitely not a conventional position, but it’s not indefensible – it’s hard to argue that Go developers are not pragmatic (or not productive).
Yup, this was my comment when this appeared a year ago on HN:
In other words, use a pure dependency-injection style, and ZERO globals.
The issue with double dash (--) is one “string safety” problem I’ve meant to address on my blog, but haven’t yet.
I’m not sure I’m following the argument in this post though.
-- convention does.NUL convention of find -print0 and xargs -0 is (surprisingly) sufficient? I didn’t quite see that until writing the two Git Log in HTML posts.I think there does need to be some kind of lint check or function wrapper for --, like flagging the former but not the latter (somehow):
grep foo $file # oops, $file could be a flag and GNU and OS X grep accepts it
grep -- foo $file # this is more correct
mygrep() { # mygrep only accepts args
grep -E --color -- "$@"
}
mygrep2() { However sometimes you do want the user to be able to append flags, so this function is useful too
grep -E --color "$@"
}
mygrep2 -v pattern *.c # invert match
The rule I have for Oil is to avoid “boiling the ocean” – i.e. there can’t be some new protocol that every single command has to obey, because that will never happen. Heterogeneity is a feature.
However there should be some recommended “modern” style, and I haven’t quite figured it out for the flag/arg issue.
I think one place Oil will help is that you can actually write a decent flags parser in it. So maybe the shell can allow for wrappers for grep/ls/dd etc. that actually parse flags and then reserialize them to “canonical form” like:
command @flags -- @args # in Oil syntax
command "${flags[@]}" -- "${args[@]}" # in bash syntax
I addressed quoting and array types in various places, including this post:
Thirteen Incorrect Ways and Two Awkward Ways to Use Arrays
There are also related string safety problems here – is -a an argument or an operator to test ?
I don’t see the need to distinguish between paths and strings.
They’re different types: a path is a string which is or could be the pathname of a file or directory on disk. Since they’re different types, treating them differently yields all the standard benefits of strong typing.
This is very cool, but note that it is a microbenchmark comparing the overhead of calling plus(int, int). This is a very specific case of FFI that is easy and simple.
For Oil, I care more about moving strings back and forth across the VM boundary (not to mention bigger objects like records and arrays of records). There are many more design choices in that case, and I suspect the results will also look different.
This was my thought as well, anything more complex than basic types or char*, you’re essentially serializing/deserializing with all the performance problems that entails.
Hm, I don’t quite see what you’re getting at, because
You don’t have to serialize complex data types to move them across the VM boundary. For example, you can move a C struct to a Python dict or a Lua table with a series of API calls, without serializing (i.e. integers and floats more or less retain their native representation; they’re just wrapped). It generally involves copying strings, but both of those languages provide various complex ways to avoid copying (buffer protocol, light user data).
I don’t think of serialization as slow. In fact sometimes I have serialized data to protobufs and moved them across the VM boundary as a single string, rather than constructing a series of complex function calls, and doing incref/decref in Python, etc.
I think tracking ownership can be more inefficient than serializing and copying, since it’s a global algorithm (i.e. causes contention among threads), and people invent all sorts of hacks to try to make it easier (layers like SWIG). More layers is more inefficient.
Anyway I don’t have numbers to back that up, which is what I would have liked to see here. But it’s pretty difficult to measure that, since these issues are situated in complex programs, with big library bindings and complex call patterns.
You don’t have to serialize complex data types to move them across the VM boundary. For example, you can move a C struct to a Python dict or a Lua table with a series of API calls, without serializing
My contention is that this is effectively serialization, only without the intermediate string (or whatever) representation; each element of the data is transformed from host to c and c to host. Similar problem arises in micro kernel architectures and IPC, not being able to push/pop data direcly onto a process stack for example.
I think I’ve read this paper a half dozen times now after seeing someone or other wax lyrical about it online. And I just don’t get why people like it so much. Which means either I’m too much of an insider and this is old news, or I don’t appreciate what I don’t know.
Part of the problem is that it seems to overstate its contributions. It isn’t really “identifying” causes of complexity. Fred Brooks pointed out essential vs accidental complexity back in 1975, and there’s been a steady stream of articulation ever since. To any Haskeller the point that complexity comes from state is old news. Relational algebra is ancient. At best OP is laying things out in a new way.
This time I figured I’d speed up my rereading time by instead chasing down past threads. And I discovered a decent summary. This was helpful, because it helped me to focus on the ‘causes of complexity’ portion of OP.
Causes of complexity according to OP:
Compare the list I made a couple of years ago:
Comparing these two lists, it looks like there’s a tension between the top-down and bottom-up views of software management. In the bottom-up view people seem to think about software like physics, trying to gain insight about a system by studying the atoms and forces between atoms. You tend to divide complexity into state and order, essential and accidental. Reductionism is the occupational hazard.
In my top-down view I tend to focus on the life cycle of software. The fact that software gets more complex over time, in a very tangible way. If we could avoid monotonically adding complexity over time, life would be much better. Regardless of how many zeroes the state count has. In this worldview, I tend to focus on the stream of changes flowing into a codebase over time, alongside the stream of changes happening to its environment. This view naturally leads me to categorize complexity based on its source. Is it coming from new feature requirements, or changes to the operating environment? How can I keep my raft slender as I skim the phase transition boundary between streams?
The blind spot of the bottom-up view is that it tends to end up at unrealistic idealizations (spherical cows as @minimax put it in this thread). The blind spot of the top-down view is that there’s a tendency to under-estimate the complexity of even small systems. Blub. The meme of the dog saying “this is fine” while surrounded by flames.
It seems worth keeping both sides in mind. In my experience the top-down perspective doesn’t get articulated as often, and remains under-appreciated.
Here’s my take on it: https://news.ycombinator.com/item?id=15776629
I also don’t think it’s a great paper. It’s long on ideas but short on evidence, experience, and examples. I don’t think you’re missing anything.
I have a similar take to you. I think it’s one of those papers that is easy to get excited about and everyone can agree that complexity is bad and all that. But I have not seen any successful application of the ideas in there. The author’s haven’t even successfully implemented the ideas beyond a little prototype, so we don’t have any idea if what they say actually pans out.
And to toss my unscientific hat into the ring: IME the biggest source of complexity is not programming model but people just not being disciplined about how they implement things. For example, I’m currently working in a code base where the same thing is implemented 3 times, each differently, for no obvious reason. On top of that, the same thing is some times and id, sometimes the id is a string and sometimes an int, and sometimes the string is a URL, and it’s never clear when or why. This paper is not going to help with that.
If what you say is true, then the success of LAMP stacks with associated ecosystems for new people and veterans alike might make it high on “evidence, experience, and examples.” That architecture worked for all kinds of situations even with time and money limitations. Except that the specific implementation introduces lots of the complexities they’d want people to avoid. So, maybe instead the Haskell answer to a LAMP-style stack or something like that fitting their complexity-reduction ideas.
Although the others shot it down as unrealistic, your characterization seems to open doors for ways to prove or refute their ideas with mainstream stuff done in a new way. Maybe what they themselves should’ve have done or do they/others do later.
Yes, so depending on the scope of their claims, it’s either trivial and doesn’t acknowledge the state of the art, or it’s making claims without evidence.
Appreciating LAMP is perhaps nontrivial. Google services traditionally used “NoSQL” for reasons of scaling, but the relatively recent development of Spanner makes your architecture look more like LAMP.
But either way I don’t think that LAMP can be “proven” or “refuted” using their methodology. It’s too far removed from practice.
In my top-down view I tend to focus on the life cycle of software. The fact that software gets more complex over time, in a very tangible way. If we could avoid monotonically adding complexity over time, life would be much better.
Thanks for the interesting commentary. Some parts definitely resonated, particularly about the half-features and difficulty of knowing how and where to make the right change.
This is only the germ of an idea, but it is perhaps novel and perhaps there is an approach by analogy with forest management. Periodic and sufficiently frequent fires keep the brush under control but don’t get so big that they threaten the major trees or cause other problems.
Could there be a way of developing software where we don’t look at what is there and try to simplify/remove/refactor, but instead periodically open up an empty new repo and move into it the stuff we want from our working system in order to build a replacement? The big ‘trees’ of well understood core functionality are most easily moved and survive the burn, but the old crufty coupling doesn’t make the cut.
Some gating on what would be moved would be needed. The general idea though is that only sufficiently-well-understood code would make it across to the new repo. And perhaps sufficiently reliable/congealed black boxes. It would interplay a lot with the particularly language’s module/package and testing systems.
The cost would be periodic re-development of some features (with associated time costs and instability). The benefit would be the loss of those code areas which accrete complexity.
Yes, definitely worth trying. The caveat is that it may be easy to fall into the trap of a full rewrite. There’s a lot of wisdom encoded in the dictum to avoid rewrites. So the question becomes: how would you make sure you don’t leave behind the most time consuming bugfixes you had to make in production on the old repo? Those one-liners that took a week to write?
This paper was written in 2006, two years before Applicatives were introduced. The Haskell community’s understanding of how to best structure programs has been refined a lot in that time and I think you underestimate the insights of this paper even if it is only a refinement of Brooke’s ideas from 40 years ago.
I got the impression that akkartik was saying that OOTP hadn’t added much new. My claim is that the fact that Applicatives were only introduced 10 years ago shows that the bar for novelty is actually quite low.
“This was helpful, because it helped me to focus on the ‘causes of complexity’ portion of OP.”
That’s the part I read in detail. I skimmed the second half saving it for later since it was big. The first half I liked because it presented all those concepts you listed in one location in an approachable way. It seems like I learned that stuff in pieces from different sub-fields, paradigms, levels of formality, etc. Seeing it all in one paper published ahead of some of these things going into mainstream languages was impressive. Might have utility to introduce new programmers to these fundamental concepts if nothing else I thought. Understanding it doesn’t require a functional programming or math back ground.
Ok, so that’s their requirements. Minimax mentions things like business factors. You mention changes with their motivations. I’ll add social factors which includes things that are arbitrary and random. I don’t think these necessarily refute the idea of where the technical complexity comes from. It might refute their solutions for use in the real world such as business. However, each piece of the first half is already getting adoption in better forms in the business world on a small scale. There’s also always people doing their own thing in greenfield projects trying unconventional methods. So, there’s at least two ways their thinking might be useful in some way. From there, I’d have to look at the specifics which I haven’t gotten to.
I do thank you for the Reddit search given those are about the only discussions I’m seeing on this outside here. dylund’s comments on Forth were more interesting than this work. Seemed like they were overselling the positives while downplaying negatives, too, though.
As someone who is a total stranger to Elm, its dev and its community, but was interested for a long time in learning this language, I wonder if this opinion reflects the feeling of the “great number” or not.
I have to say that I personally can very much see where he’s coming from. GitHub contributions are dealt with in a very frustrating way (IMO they’d do better not allowing issues and PRs at all). There’s a bit of a religious vibe to the community; the inner circle knows what’s good for you.
That said, they may very well be successful with their approach by a number of metrics. Does it hurt to loose a few technically minded independent thinkers if the language becomes more accessible to beginners?
Where I see the largest dissonance is in how Elm is marketed: If the language is sold as competitive to established frameworks, you’re asking people to invest in this technology. Then turning around and saying your native modules are gone and you shouldn’t complain because no one said the language was ready feels a bit wrong.
Yeah when I look at the home page, it does seem like it is over-marketed: http://elm-lang.org/
At the very least, the FAQ should probably contain a disclaimer about breaking changes: http://faq.elm-community.org/
Ctrl-F “compatibility” doesn’t find anything.
It’s perhaps true that pre-1.0 software is free to break, but it seems like there is a huge misunderstanding in the community about compatibility. The version number doesn’t really mean much in my book – it’s more a matter of how many people actually rely on the software for production use, and how difficult their upgrade path is. (Python 3 flauted this, but it got by.)
I think a lot of the conflict could be solved by making fewer promises and providing some straightforward, factual documentation with disclaimers.
I watched the “What is Success?” talk a couple nights ago and it seemed like there is a lot of unnecessary conflict and pain in this project. It sounds like there is a lot to learn from Elm though – I have done some stuff with MUV and I like it a lot. (Although, while the types and purity probably help, but you can do this in any language.)
I watched the talk also, after another… Lobster(?)… Posted it in another thread. My biggest takeaway was that Evan really doesn’t want to deal with an online community. People at IRL meetups, yes. Students in college, yes. People/companies online trying to use the language? No. His leading example of online criticism he doesn’t want to deal with was literally “Elm is wrong” (he quoted without any context, which isn’t that helpful. But maybe that was all of it.)
That’s fine. He’s the inventor of the language, and the lead engineer. He probably does have better things to do. But as an outsider it seems to me that someone has to engage more productively with the wider community. Our, just come out and say you don’t care what they think, you’ll get what you’re given, and you can use it if you choose. But either way communicate more clearly what’s going on, and what to expect.
I’ve shipped multiple production applications in Elm and attempted to engage with the community and I can say that their characterization perfectly matches mine.
Native modules being removed in particular has caused me to no longer use Elm in the future. I was always ok with dealing with any breakage a native module might cause every release, and I’m even ok with not allowing them to be published for external consumption, but to disallow them completely is unreasonable. I’m sure a number of people feel the same way as I do, but it feels impossible to provide meaningful feedback.
I work for a company that began using Elm for all new projects about a year and a half ago. That stopped recently. There are several reasons that people stopped using Elm. Some simply don’t like the language. And others, like the author of this post, want to like the language but are put off by the culture. That includes me. This article closely resembles several conversations I’ve had at work in the past year.