Tiny Core: The Little Distro That Could - <AUTHOR> http://www.linux-mag.com/id/7457 <comment><USER2> Until a few months ago, I used Damn Small Linux daily as a secondary work system, on an old 128M P2 laptop. It made a great ssh (and 3270) terminal. If I watched the memory usage, I could even browse the web, play MP3s, etc. if my main laptop was rebooting or being worked on by IT. Not bad for system that ran from a CD and a 32M USB drive. I also used the CD to do rescue work on my primary laptop and other people's systems. I still keep a DSL CD in my laptop bag.

I didn't realize Robert is a 60 year old disabled retiree. <http://distrowatch.com/weekly.php?issue=20090323#feature>

</comment><comment><USER3> The ISO download was down off the main page but I found a mirror at:

[http://linux.softpedia.com/progDownload/Tiny-Core-Linux- Down...](http://linux.softpedia.com/progDownload/Tiny-Core-Linux- Download-43833.html)

</comment><comment><USER3> I'm impressed. I installed it to disk on a VM and with Firefox 3.5.2 installed it was only using 75MB.</comment>


My startup launch was a failure or was it? - <AUTHOR> https://phalox.be/wp/startup/my-startup-launch-was-a-failure-or-was-it/ <comment><USER2> Yes and no.

It doesn't appear to have failed because, according to the post, you didn't define what 'success' looks like. If you don't have a goal to measure your success against then you can't really fail. That's not very helpful though.

If the point of the launch was to go from nice-project-that-might-get- customers-one-day to actual-real-product-that-has-customers then yes, it was a failure. That's OK though, because a failure is only an event that didn't meet the criteria of success. You can try again, hopefully with a more successful pitch or a different audience and maybe meet your success criteria. At the very least you now know of one thing that _doesn 't_ work.

However...

You're on HN again with a blog post that barely mentions the product. There's no link to it. There's no opening line that says "I launched Validat.r and this is what happened" or a closing paragraph all about the product for people who see _this_ post but didn't see the original one. Honestly, if you're going to leave that sort of _really obvious_ stuff out of your marketing posts, why bother?

<comment><AUTHOR> Dear Chris,

Thank you very much for taking the time to provide me with honest feedback! You're indeed right and highlight some interesting shortcomings.

Allow me to reply to your remarks.

In the post, I indeed didn't define what success looked like and I'll be amending this. My initial goal was to get a first feel of the market and not necessarily customer conversions (that might not have come through very well). But I did want to strike a chord with the many entrepreneurs who have built a product, do the magic launch and then nothing happens.

I think that potential readers would most definitely be interested in this extra information, so thanks for the idea!

For your final remark. You're very right that I'm underusing the marketing potential of these blog posts, and I'm still learning my way through content marketing. I'm also still struggling a bit with getting Wordpress to obey and make jump out call to actions. But maybe most importantly: I don't want to build an empty blog for pure marketing reasons and I'm trying to find the balance between these conversions and valuable content.

Thanks again for your input, I'll make work of it!

<comment><AUTHOR> For completeness sake, here's the updated post adjusted to your feedback: [https://phalox.be/wp/startup/my-startup-launch-was-a- failure...](https://phalox.be/wp/startup/my-startup-launch-was-a-failure-or- was-it/)

Once again, thanks for your input!</comment></comment></comment>


Where the Printf Rubber Meets the Road (2010) - <AUTHOR> http://blog.hostilefork.com/where-printf-rubber-meets-road/ <comment><USER4> So for the record, I wrote this four years ago. No idea why this is being linked to from here now.

Let the record show, also, that I in no way stand by this as being a sane approach. I just walked through it, trying to answer the question. I wanted to show the path to syscall and it was way wackier than I thought it would be. That's why it ended up a blog entry.

(Though when my blog went down one day, someone copied the content into the answer:

[http://stackoverflow.com/revisions/2444508/3](http://stackoverflow.com/revisions/2444508/3)

...and they did so ignoring the license clause difference that my blog is CC- BY-NC-SA instead of CC-BY-SA. There's been a bit of a tussle over that distinction lately, with people selling StackOverflow books:

[http://meta.stackoverflow.com/questions/272768/is-this- site-...](http://meta.stackoverflow.com/questions/272768/is-this-site- violating-the-tos-of-stack-overflow-and-or-amazon-and-how-to-report)

...and I'll leave it to those with more interest in the issue to decide if that is worth worrying about, because I don't actually care.)

Anyway, I'm sure the people involved in writing this had their reasons for doing it this way, and it had to do with code legacy and evolution. I don't want anyone to mistake my trace through it as endorsement. It's just what it is.

<comment><USER5> You did miss one fun rabbit hole with printf in glibc:

Search for register_printf_function, and realize that printf is now a function that can have whatever side-effects you like (which really sucks for optimization around logging code)

<comment><USER6> Glibc may have register_printf_function, but GCC does not have register_printf_attribute, so this is not as practical as it could be. That is:

        __attribute__ ((format (printf, 1, 2)))

attaches to a function declaration to indicate that the function works like printf, so GCC can warn if a (compile time) format string does not match the variadic arguments. But there seems to be no way to extend this attribute, nor to make entirely new attributes in the same spirit.

So you can register new format specifiers for printf, but it seems you'll then have to disable warnings about bad format specifiers during compilation. Those warnings have very few false positives and catch real bugs in real code.

<comment><USER5> "But there seems to be no way to extend this attribute, nor to make entirely new attributes in the same spirit."

Yes, GCC knows, and we're good with that :)

The glibc extension here serves no earthly purpose (and on the gcc side, we've considered pretending it doesn't exist before so we can actually optimize better).

It sounds like it may be a good idea, except 1\. It modifies functions that have a standard purpose, standard set of format specifiers, and are, as you point out, often error checked to make sure they match those :)

2\. Unless you see the printf handler registration, which may be in a library, linked to your program, or whatever, you'll have no idea that the code is wrong.

</comment></comment></comment></comment><comment><USER7> So what is the reason for all the rest of the indirection and macros? Why __printf and then aliased to printf? Why is outchar a macro instead of being factored into a function? If you asked me to implement printf I wouldn't immediately do all of this - so what would my implementation be missing that makes these necessary?

<comment><USER6> I can speculate: __printf(), having two leading underscores, is a named reserved for "the implementation," meaning the compiler and standard library. This enables at least one useful thing, which is that the standard library (or a third-party library I guess) can use this function when it needs to print things, and not worry that printf() has been redefined by the application.

You might think, but who in their right mind would redefine printf()? But C has an infinite number of people using it all the time, so every possible weird thing has been done a few times by now.

<comment><USER8> > You might think, but who in their right mind would redefine printf()?

One cannot discount those in their wrong mind either.

I had a header dedicated to #undef ing things ruby.h redirected so I could include it in C++ contexts without breaking the SC++L. While I don't see printf on the list, read, write, close, fclose, sleep, and many more are.

</comment></comment></comment><comment><USER9> This is interesting and timely since just recently I attempted to make the same journey in the name of creating a tiny obfuscated C program[0]. However, right at the part where the author says _" you might start thinking that you no longer care how printf works"_ is exactly where I stopped caring.

In the end, I did find out _a_ way to write to stdout without calling putchar or including stdio directly, but there's still some mystery in the call to _write_ :

        // Printing to stdout without stdio or putchar
        // Originally adapted from here:
        // http://stackoverflow.com/a/14296280
        void print_char(char item, int len)
        {
            for (;len; --len)
            {
                write(1, &item, 1);
            }
        }

[0] - [https://github.com/lelandbatey/tiny_tree_printer](https://github.com/lelandbatey/tiny_tree_printer)

<comment><USER10> How is that function mysterious? It just writes the same character to stdout len times. The only mystery is how anyone thought that would be efficient.

<comment><USER9> What I mean by mysterious is that the syscall is hidden in the write statement, vs hidden in the printf statement. Also, I wrote the above function (actually a much smaller version) as part of a program that would print a binary tree of any given height[0]. It wasn't meant to be efficient, it was meant to be small, since the total size of the program was 777 bytes (later reduced to 505 bytes).

[0] - [http://lelandbatey.com/posts/2014/09/binary-tree- printer/](http://lelandbatey.com/posts/2014/09/binary-tree-printer/)

</comment></comment></comment><comment><USER11> I had a similar question to this once, and what I mainly found out was that FreeBSD's (and thus OSX's) libc is much, much, more readable than glibc.

<comment><USER12> Any other C library is more readable than glibc. Even on Linux, the Musl C library is far far far more readable.

The OpenBSD C library is the one I usually look at when I want to understand how a specific function works. It doesn't have insane optimizations or bloat like glibc, but it's clean and portable.

<comment><USER11> The only thing you can begin to understand by reading glibc is the terrible genius of Ulrich Drepper.

</comment></comment></comment><comment><USER13> The original Stack Overflow question seems to have some fundamentally broken assumptions about implementation in assembly. The design decisions of one random compiler don't determine how all implementations work. And even for that compiler, a lack of inline assembly doesn't mean the standard library can't use assembly; it just has to use separate assembly files. Beyond that, the compiler itself generates machine code, and it can (and does) do so as part of the implementation of varargs.

Regarding the site hosting the article: ugh, it's bad enough to capture the left arrow key and have it go to a previous article, but pay attention to the modifiers to avoid breaking alt-left as a keyboard shortcut for "back". (Browsers shouldn't even allow unprivileged pages to override shortcuts like alt-left.)

<comment><USER4> But I'll patch the alt-left thing if it makes you happy. :-/ Still, if you want to give feedback, write an email and make it friendly. It doesn't make people feel good with "ugh". There's so much "ugh"-worthy stuff out there and I don't think I deserve that.

<comment><USER13> I appreciate you taking the time to fix the alt-left problem. I've run into too many sites doing similar things lately, including Google Blogger-based sites; this was just the most recent one I've run into. I was attempting to express a very mild annoyance; sorry if it came across as excessively snarky.

<comment><USER4> It's all right, I just try kind of hard to be the least ad-having, most license friendly, site I can host... so it gets my goat a bit given what I think are better "ugh" targets by far.

I'm traveling and it's not convenient to fix it tonight, but I will do it as soon as I can.

</comment></comment></comment></comment><comment><USER14> In my more nieve days I used to genuinely believe that macros were a cool language feature. Now after spending some time in C/C++ I think macros are the enemy of code readability, and I think this article summarises WHY nicely.

I'm sure there is some political reason why ldbl_strong_alias was used to rename printf to __printf, but frankly I don't really care, there are macros like that all over the C/C++ libraries that make finding the source for anything pure hell.

The only acceptable macro to me now is a block which only runs in the DEBUG build and is skipped over by the compiler in RELEASE.

PS - Yes this is why I don't code in C/C++ very often. I'm currently playing with Go which has no concept of macros.

<comment><USER15> the very few times you want the source to something to debug it etc. you'll have a hard time. the 99% of the time when you want not the implementation details but some semantically meaningful mapping of one token to one concept, macros are wonderful.

another huge benefit is that they take care of fiddly bookkeeping for you - <USER2> you have to remember some incantation that involves doing several steps in sequence and making sure that if you change place A you need to change B and C in certain ways, just write a macro and be happy.

</comment></comment><comment><USER16> I messed around with an MSP430 micro controller for a while. There, I didn't want to use a "standard" (Third-party but popular) library. I ended up grabbing and modifying a much more direct version of printf, and I got to the syscall part much quicker. When you only have 256 bytes of RAM and 64 KB ROM, you don't have such luxury. You also dont need to support C++.

</comment><comment><USER17> I think printf() is one of the more interesting functions in the standard C library to examine, since it is both so often-used and has the very visible effect of producing output. It also shows some of the ways in which data can be interpreted. Variants or limited-functionality versions of it can make good assignments for programming courses.

Printing floating-point numbers is also another topic that is subtly more difficult than it looks at first glance... here's one of the better articles about it: [http://www.ampl.com/REFS/rounding.pdf](http://www.ampl.com/REFS/rounding.pdf)

</comment><comment><USER18> When I was using actively Turbo/Borland Pascal I always thought that the "Write"/"Writeln" (forgive me, if mispelled) were magic functions :) - <USER3> they were taking random amount of arguments, but there was no pre-processor, or "..." as in "C"...

</comment><comment><USER19> There is a very quick overview of all this here: [http://en.wikipedia.org/wiki/Write_(system_call)#Higher_leve...](http://en.wikipedia.org/wiki/Write_\(system_call\)#Higher_level_I.2FO_functions_calling_write)

</comment><comment><USER20> I think the question comes from a lack of understanding of varargs, not a desire to see a bunch of crappy glue code inside glibc.

And for that type of C newbie who hasn't seen varargs... I'd say the answer is pretty intuitive if you know how to look. First of all the answer is staring at you in the manpage:

       int printf(const char *s, ...);
       int vprintf(const char *s, va_list ap);

OK, so there is the set of ordinary C functions that do it. Look up this va_list thing and an implementation becomes obvious... printf sets up a va_list and calls vprintf... vprintf scans the string for '%' and uses va_arg...

But the important thing is none of this is magic and you can totally guess it just by looking at documentation. I don't think it ever confused me when I learned C. I don't really get why this is so shocking to people who read the manpages.

<comment><USER15> what? the question had nothing to do with varargs; the poster quite rightly noted that at some point in the implementation of printf, when you needed to send an actual character to an actual output device, you would need something conceptually lower-level than C. they were just confused about the specifics of interfacing between C and the machine; i think one of the comment-level answers put its finger on the nub:

> Visual Studio x64 doesn't support inline assembler. That doesn't mean you > can't have assembler code. You can still have assembler, just not inline.

and in the answer section

> The system calls are (on platforms that I know of) internally implemented by > a call to a function with inline asm that puts a system call number and > parameters in CPU registers and triggers an interrupt that the kernel then > processes.

<comment><USER20> I have a hard time seeing this as a magical part even in the eyes of a newbie. How common is it to hear about stdio but not also know about write()? I am pretty sure one man page refers to another. My point, just as it is with varargs, is you should be curious enough to find these answers yourself, run some experiments, make some good guesses.

Since Visual Studio is mentioned, I'll say that despite some extra layers (CRT, kernel32, ntdll) the concepts are identical and when I was a kid learning C I was able to dig through header files and documentation to conceptually figure it out.

<comment><USER15> it's not a question of anything being magical; the op just got confused by the difference between writing inline assembly in a c program and calling from c code into compiled and linked in assembly code.

<comment><USER20> I'll say, somebody is a little confused.

I still say, they don't understand varargs, don't understand the difference between stdio and write(), and most importantly, they are not curious enough to read documentation and make smart guesses. And I am saying, the bullshit obfuscation involved in this article, sharing so many meaningless implementation details about a very specific libc, will not help answer this question or gain insight more than RTFMing and being curious about the world would.

So... If you are even asking questions like... "Does printf use assembly?" I'd argue you are not thinking it through. You need to get yourself to a place where you can answer that yourself. It will be a very obvious answer when you do. Writing frivolous blog posts is not the way to get there.

<comment><USER4> Just noting that the article is not the _source_ of the bullshit obfuscation, nor does it condone it. I'm part of the Red project.

[http://www.red-lang.org/p/contributions.html](http://www.red- lang.org/p/contributions.html)

We're trying to undo this stuff. I was just answering a question.

<comment><USER15> red looks awesome, and has come a lot further than I expected! (i peeked at it a few years ago and there didn't seem to be much). I love that small executable sizes are an explicit design goal.

<comment><USER4> The next release will be able to directly build and sign .APK files, with no JDK or JarSigner required to be installed. Still under 1MB for that same compiler executable on all platforms...that can also build PE (Windows), Mach-O (OS/X), and ELF (Linux etc.)

It's moving slower than we'd like, but definitely moving.</comment></comment></comment></comment></comment></comment></comment></comment>


British and Canadian governments exposed passwords and security plans on Trello - <AUTHOR> https://theintercept.com/2018/08/16/trello-board-uk-canada/ <comment><USER2> What an absolute farce. It is almost unbelievable how technically incompetent government employees can be.

Included in the publicly available information:

login credentials to a U.K. government account on a domain registrar

login information for CPanel

FTP credentials

<comment><USER3> It's not like there isn't plenty of awareness and mandatory training either! I suspect some of these are gross misconduct.</comment></comment>


Computation and State Machines (2008) [pdf] - <AUTHOR> http://research.microsoft.com/en-us/um/people/lamport/pubs/state-machine.pdf <comment><USER2> I would say that the premise of this article is a bit wrong. Naturally the examples given about the language usage in physics do look odd because these are very simple examples based on the form of the writing that has been agreed on long time ago.

Now when we evaluate something more elaborate then we will suddenly discover that there are multiple ways to write down some very fundamental concepts. For example Maxwell equations can be written in many different forms [http://www.maxwells-equations.com/forms.php](http://www.maxwells- equations.com/forms.php)

So in fact the language is a very important tool even in physics.

Even the author continues and defines a new language to express the relations between and in the state machines.

Nonetheless this is an interesting piece of the writing.

<comment><USER3> It's a bit of a subtle point, but it is hard precisely because physics doesn't quite have anything that is analogous to programming languages, and Lamport does draw a distinction between a universal formalism -- "ordinary" math -- and a programming language, which is something that is far more complex. The different forms of Maxwell's equations you present are all very much written in the same language of multivariate calculus (but in different words).

I would present Lamport's thesis like this: engineering disciplines usually use a different formalism to _reason_ about system from the one they use to construct it. Consider an electrical engineer designing an analog circuit, and a mechanical engineer designing a contraption made of gears, pulleys and springs. When reasoning about their designs, they can either use the "standard" mathematical approach, plugging in the equations describing what the components do, or invent an algebra of electronic components and an algebra of mechanical components and reason directly in a "language" of capcitors and resistors (one capacitor plus one resistor etc.) or a language of gears and springs. The engineers obviously use a domain-specific tool to design the low-level details of the system, but they use standard math to reason about it (to guide their high-level design and to make sure it achieves what they want). Lamport suggests that software engineers do the same.

In practice, most software engineers reason about designs only informally, and even then mostly in their heads. This is fine for simple things, but very hard to do when more complex software systems are concerned. The software verification (academic community) indeed uses an approach similar to Lamport (which uses more-or-less simple math to reason about programs), but they hide the techniques from the user, exposing them merely to the results in a sort of a CAD tool. The programming language theory community indeed tries to make mathematical reasoning explicit, but they're trying to make reasoning possible _within_ the construction language (like the algebra of gears). There are a few advantages to that approach, but one major disadvantage: because programming languages, as Lamport notes, are so complex, the mathematics required to reason about them syntactically is very arcane and _extremely_ complex; it is very much a completely new math. And because it is so new and so complex, PLT researchers have not yet been able to accomplish the task. Reasoning in that new math is _currently_ much harder and much more limited that in Lamport's "simple math" approach[1]. Of course, it's possible that PLT will come up with good solutions in the future.

But in addition to the big pragmatic advantage of Lamport's approach (which may be temporary), he claims that using the universal language of "ordinary" math helps find the great similarities between different programs that can be written in many different ways, but are essentially the same. This helps to better understand what computation _is_ , and also helps focus on the core issue -- how the program's environment behaves, how the program interacts with it, and why the program's behavior achieves its goals -- rather than being distracted right from the beginning by details such as the choice of abstractions (which are important only after the core issues are understood).

[1]: I sometimes see academic papers about how to reason about pretty basic program properties in Coq that would never be written about Lamport's TLA+ for the simple reason that an ordinary engineer would find them almost trivial in TLA+ after only a month or so since first encountering it.

<comment><USER4> > The programming language theory community indeed tries to make mathematical > reasoning explicit, but they're trying to make reasoning possible within the > construction language (like the algebra of gears).

We discussed this point before, and this comment finally makes it clear to me why we never saw eye to eye.

Equational reasoning is _a_ tool for reasoning about programs, but not the only tool. Lamport uses it too, in the form of refinement (this is exactly what is used in practice, outside of "educational" papers about functional programming). More importantly though, nobody insists that all reasoning has to be equational. The Hoare logic and its Owicki-Gries variant for concurrency are the grandfathers of modern program logics, which give you a very precise language (ordinary math + X, same as what Lamport wants) for reasoning about program correctness.

The only difference in this paper is that Lamport insists that programs should be syntactically represented as state machines, while most PLT people would insist that this is too much of a restriction. Typed programs enable more abstract programs and specifications, because the abstraction mechanism offered by types is in some sense dual to refinement. Types allow you to restrict client code in a lightweight way. Combined with higher-order functions (i.e., first class functional abstraction), this allows you to achieve more abstract specifications than what can be done with state machines.

If I have a program in TLA+, I represent it as a state machine and any specification I write down is essentially just a refinement of this state machine. You cannot really hide internal states, unless you can explicitly skip them via simulation (or at least that is my impression of TLA+, you are far more experienced here and very welcome to contradict me).

In a typed programming language, quantification gives you the dual abstraction of restricting the users of your program. Existential types allow you to pick an invariant relation between two implementations. Crucially this invariant does not have to be a (bi)simulation. If you look at this from a state machine perspective, then these can be two completely different state machines externally, but they are different in a way that clients can't exploit.

For a concrete example, in [https://people.mpi- sws.org/~dreyer/papers/caresl/paper.pdf](https://people.mpi- sws.org/~dreyer/papers/caresl/paper.pdf) , the authors show refinement between flat combining and locking, as well as between a lockfree and a lock-based stack with iterators. In this generality, this is only sound for typesafe clients.

\----

If this sounds overly negative and critical of Lamport's work, then I apologize. The main point I want to make is that we are using substantially the same tools. There is nobody in program verification who seriously insists that equational reasoning is the only tool the world needs. The point of equational reasoning was always that it is a lightweight method which is easily taught to ordinary programmers. That's what makes it valuable.

If you encounter a paper (typically by Richard Bird :) ), verifying complicated programs using only equational reasoning, then take this with a grain of salt. The interesting thing about such papers is that it's possible at all ("see how far you can push this idea!"), but I've yet to meet anyone who seriously insists that this is always the easiest proof you can come up with...

<comment><USER3> > The only difference in this paper is that Lamport insists that programs > should be syntactically represented as state machines

Just to make sure: He does not intend for compilable programs to be represented as TLA formulas, just as electrical circuits are not laid out with ODEs. He says that _reasoning_ about the properties of programs should be done in a system like TLA, just as electrical circuits can be modeled as ODEs. TLA+ itself comes with a tool that lets you write specification in a syntax called PlusCal, which is similar to familiar imperative languages, and Lamport himself says that he made that language because that syntax may be more convenient for some tasks.

> while most PLT people would insist that this is too much of a restriction.

Reasoning syntactically about a program in its ready-for-construction source has several advantages, but I doubt anyone would view TLA as any sort of a restriction (quite the opposite: it allows to specify discrete dynamical systems that are not even computable). But the main difference for _practitioners_ is this: TLA works _today_ for a wide range of programs. The ideal of reasoning about programs in their source language with similar cost is far from realized. TLA succeeds in practice simply because it is far less ambitious.

> Typed programs enable more abstract programs and specifications, because the > abstraction mechanism offered by types is in some sense dual to refinement.

I am not sure what you mean.

> Combined with higher-order functions (i.e., first class functional > abstraction), this allows you to achieve more abstract specifications than > what can be done with state machines.

I am not sure you quite realize how state machines are used in TLA. You can use higher-order functions to your heart's content, as well as higher-order state machines. The machine's are abstract, and what constitutes a state is completely up to you. TLA+ is rich enough that you can write an entire program completely using pure functions, that executes as a single TLA step.

But reasoning with functions is a specific level of abstraction. TLA allows you to go arbitrarily higher or lower. Functions are too high an abstraction if you're interested to reason about, say, complexity, and they're too low an abstraction if you want to reason about interaction (where they can only constitute single monadic steps). They may be either too high or too low for concurrency. Of course, you can embed TLA in FP and vice versa, but TLA is "naturally" broader in scope.

> You cannot really hide internal states

Why not? That's what existential temporal quantification is for. `∃∃ x : F`, means that F performs its observable operation using some internal hidden state, x (∃∃ is usually written as bold ∃)

> In a typed programming language, quantification gives you the dual > abstraction of restricting the users of your program.

I'm not sure exactly what you mean. You can achieve the same thing you can with types in an untyped TLA-based language like TLA+. For example, the typing judgment `f:A->B` is simply a conjunction with the proposition `f ∈ [A → B]` in TLA+. Remember that the environment is also part of the process (or machine).

> Crucially this invariant does not have to be a (bi)simulation

The concept of bisimulation is a rather complex one designed to explain the difference between trace inclusion and stronger forms of equivalence in algebraic models that do not model state explicitly (like CSP/CCS). In TLA, bisimulation and trace equivalence are the same, and simulation is just trace- inclusion. The famous result "the existence of refinement mapping" by Abadi and Lamport shows that this is complete, namely that any implementation relation "A implements B" can be modeled as "A refines B", which in TLA translates to the familiar implication operator `A ⇒ B`. So there is no such thing as two machines, one implementing the other, that cannot be expressed in this way.

> In this generality, this is only sound for typesafe clients

Again, in TLA, a "typesafe client" is no more than an assumption about the properties of the client "process". In TLA, a specification often takes the form `◻User ∧ ◻Program` or the equivalent `◻(User ∨ Program)`

> That's what makes it valuable.

I would say that in practice the opposite holds. TLA+ is used by "ordinary" engineers to reason about systems that are far larger and trickier (concurrent, distributed) than anything that is currently done by engineers using equational reasoning in typed PFP. This advantage may (or may not) be short-lived, but I think the combination of simplicity, ease of learning, and scalability both in size and in scope is precisely TLA's _pragmatic_ edge.

<comment><USER5> > But reasoning with functions is a specific level of abstraction.

Excellent point. The level of abstraction is a fundamental concern, not just in modeling but also in reasoning about systems. And state machines (as used in TLA or otherwise) add temporal abstractions to the mix of abstraction mechanism. To add to its power, it does not lead to any loss of generality; since any program in a language with well-defined operational semantics can be modeled using state machine (transition systems with infinite state space to be little more precise).

> In TLA, bisimulation and trace equivalence are the same, and simulation is > just trace-inclusion.

There is a fundamental difference between (bi)simulation and trace inclusion(equivalence); the former is a branching time notion and the later is a linear time notion. In fact if one it is easy to show if A is a simulation of B then A is also a "trace inclusion" of B. And this difference has some practical ramifications as well.

The branching time notions of refinement also has (not so famous) the completeness result.

[http://www.ccs.neu.edu/home/pete/pub/charme-refinement- branc...](http://www.ccs.neu.edu/home/pete/pub/charme-refinement-branching- time.pdf)

[https://arxiv.org/pdf/1502.02942v1.pdf](https://arxiv.org/pdf/1502.02942v1.pdf)

This has some practical ramification as well: the refinement map in Abadi/Lamport is a projection function, while in branching time it can be arbitrary function. The former is intuitively clear but often difficult in practice. The flexibility has been exploited to efficiently analyze correctness of several non-trivial systems like pipelined processors.

> So there is no such thing as two machines, one implementing the other, that > cannot be expressed in this way.

To drive home the point what the completeness result means is that one can always reason at the level of abstraction that was use to specify the system: state and their successors. This is what we are after in type-based reasoning: reasoning at the level of functional abstraction.

<comment><USER3> > the former is a branching time notion and the later is a linear time notion

I think it is more precise to say that you cannot formally encode the concept of (bi)sumlation in LTL while you can in CTL. The notion itself, however, is a general _structural_ relation between state machines that is independent of the temporal logic you use to formalize properties of their behavior, if any. I.e. (bi)simulation would exist even if temporal logic didn't.

> the refinement map in Abadi/Lamport is a projection function

One of the few things I _don 't_ like in TLA+ is that you can create a "refinement" mapping which isn't sound (and therefore isn't a refinement). In particular, you can add auxiliary variables that interfere with the specification. I know Lamport is working on a paper where he gives _syntactic_ rules that ensure that the mapping is sound.

> This is what we are after in type-based reasoning: reasoning at the level of > functional abstraction.

The challenge is making that reasoning rich enough (to reason about a wide class of programs), scalable enough, and at the same time affordable and simple for engineers. This goal is far from realized (and currently falls short, at least in the last two requirements).

P.S

I also think that TLA allows reasoning about structural properties of the state machine without any use of temporal logic at all. If A and B are _action_ formulas and S is a state-predicate, then `S ∧ A` denotes the set of transitions _from_ the set of states denoted by S (conversely `S' ∧ A` is the set of transitions _into_ S). And so `S ∧ A ⇒ S ∧ B` means "for every transition A leaving S, there is a transition B leaving S". I think that you can therefore show (bi)simulation directly, structurally, rather than behaviorally; there is no use of behaviors (traces) or computation trees here at all (you may want to show that all the states S are reachable from the initial states).

[1]: Unless behaviors are sequences of states, in which case the relationship coincides with the trace-equivalence relationship. There _may_ be subtle technical differences (that I don't quite understand) even then, but they don't matter.

<comment><USER5> > create a "refinement" mapping which isn't sound (and therefore isn't a > refinement).

Could you please illustrate what do you mean by unsound refinement ? Refinement mapping along with the notion of correctness forms the part of "trust computing base". More precisely, the statement "A implements B under a refinement map r" assumes two things (1) what it means to implements; is it trace inclusion or simulation or stuttering simulation .... (2) what is the refinement map r. What am i missing here ?.

Also can you please illustrate (with an example possibly) on the syntactic rules that Lamport is working on ?

Addition of auxiliary variables to the specification introduces an extra proof obligation, namely, that it does not change its observable behavior. Often this is not very difficult as one can syntactically differentiate between auxiliary variables and the state variables.

>I also think that TLA allows reasoning about structural properties of the state machine without any use of temporal logic at all.

Definitely. Like you mention the refinement based on (bi)simulation or trace inclusion is different from the use of temporal logic to formalize properties of systems. Though two are related in the sense of adequacy of temporal logic: simulation preserves ACTL* properties while trace inclusion preserves LTL properties.

> I think that you can therefore show (bi)simulation directly, structurally, > rather than behaviorally; there is no use of behaviors (traces) or > computation trees here at all.

I do not quite understand what you mean here. Behaviors are succinctly represented using state machines -- set of states and there successors-- and then we look at a state machine as a generator of (possibly infinite) behaviors -- either as traces or computation trees.

The scenarios where trace inclusion requires addition of prophecy and history variables to recover local reasoning (in other words structural reasoning using state and its successors) is more related to the completeness argument you mentioned earlier.

<comment><USER3> > (1) what it means to implements; is it trace inclusion or simulation or > stuttering simulation .... (2) what is the refinement map

Well, in TLA there's only one kind of implementation relation, as the three relations you mention coincide since a trace is a sequence of states, under a stuttering equivalence. All other implementation relations (like event-trace- inclusion) are the same relation on a reduced (abstracted) machine, with some of the state erased, or a refined machine with an added time variable. So the map (which we take to include auxiliary variables) fully describes the "kind" of implementation.

> Could you please illustrate what do you mean by unsound refinement?

The TLA+ features are, of course, completely sound, but you may not be mapping what you intended to map. The trickiest problem has to do with prophecy variables. You need to ensure that they don't restrict the behavior of the spec, and it's not always obvious.

> Also can you please illustrate (with an example possibly) on the syntactic > rules that Lamport is working on ?

The simplest rule is that if the spec is

        Init ∧ ◻[Next]_x

and, under an invariant:

        Inv ∧ Inv' ⇒ (Next ≣ ∃i∈Π : N(i))

Then the spec remains equivalent when introducing the prophecy variable p like so:

        ∃∃p : (Init ∧ p ∈ Π) ∧ ◻[N(p) ∧ p' ∈ Π]_<x, p>

This basically means that the prophecy must cover the entire parameter space of N, and that once used, a prophecy must be "forgotten". The problem is parameterizing N. So the rules treat various forms of Next (disjunction, quantification etc.).

But if Next is parameterized well so that its behavior isn't restricted by p, then obtaining a useful value from p for the mapping can be tricky: so you know what value will be picked by an existential quantifier, and you know what disjunct will be chosen etc., but you still need an interpreter for those choices to obtain the result of the machine's action. You could write the spec itself in a clever parameterized way so that it would be its own prophecy interpreter, but then you'd make it completely unreadable.

In short, the problem is that you need to make sure that your prophecy variables are sound, but you still want to keep the spec at that refinement level readable and not sacrifice it for the sake of refinement to some other level, and the two requirements pull in opposite directions.

What you could do (and that's what I did in a complex spec), is to use a prophecy variable that interacts well with the spec (i.e., doesn't make it unreadable) but doesn't conform with Lamport and Merz's rules, and _then_ prove that it is sound. Unfortunately, that property alone can be 95% of what you intend the refinement to show in the first place, and here the model checker can't help you at all.

We really need a model checker that can handle temporal quantification.

> I do not quite understand what you mean here.

Oh, it was just to emphasize the point that simulation is a structural property, unrelated to temporal logic or any notion of time or behavior.

<comment><USER5> Thanks for the explanation especially with the example.

I now understand what you meant by "unsound refinement" and the desire to keep the spec readable.

> and that's what I did in a complex spec

Do you believe that your hands were tied because the mapping can only be a projection of state?

Is this spec and the corresponding implementation that you are working on in public domain ?

> We really need a model checker that can handle temporal quantification.

Do you intend to restrict the quantification only over time variable only ? Why do I ask this? This is because even in presence of finite stuttering, one only needs to analyze about state and the successors ?

If you intend to quantify over state variables (including the prophecy/history variable, for instance in the example you mentioned) -- that is a more general problem of dealing with quantifiers and there is still long way to automate that aspect.

<comment><USER3> > Is this spec and the corresponding implementation that you are working on in > public domain ?

Not currently. Maybe in the future.

> Do you believe that your hands were tied because the mapping can only be a > projection of state?

Rather than? I'm not sure I understand the question, so maybe. My problem is that some reductions require moving certain transitions backwards or forwards in time. In theory, the idea is described well in section 3 of this paper [https://research.microsoft.com/en- us/um/people/lamport/pubs/...](https://research.microsoft.com/en- us/um/people/lamport/pubs/cohen-tlareduction.pdf) (this is pretty much exactly my problem: I wanted to see if a distributed transaction is a refinement of an atomic transaction). In practice, using prophecy variables isn't easy.

> Do you intend to restrict the quantification only over time variable only?

Oh, no, in TLA "temporal quantification" means hidden state, or quantifiers over state variables. Having the model checker handle that would make auxiliary variables unnecessary in cases where the more abstract specification has hidden internal state (unless you want a deductive proof). It will save us the hardest work, and ensure that the implementation relation is sound (no auxiliary variables necessary).

It would have solved my problem, too. Even though auxiliary variables would still be necessary, the model-checker could check that they're sound by checking:

        S ⇒ (∃∃ aux : S_with_aux)

to see that adding the auxiliary variables doesn't restrict the spec (and then I could check, as I do today, that `S_with_aux ⇒ Abstract_S`)

<comment><USER5> > My problem is that some reductions require moving certain transitions > backwards or forwards in time.

This is definitely a more involved problem. A cleaner solution, I believe, requires a new notion of correctness than trace containment/simulation up to stuttering. This is because neither of these notions allow one to move forward or backward _observable_ transitions. The key here is to differentiate between non-observable and observable non-determinism. While the former can be accounted by using prophecy variable in linear time framework or directly in the branching time framework. I will recommend reading the comparison of the two approaches in [1]. However, the later is not what can be accounted by adding auxiliary variables. It is done currently by complicating a simple sequential specification -- create a new specification that includes all possible interleaving of transitions implicitly or explicitly. I will be curious to hear your insights on this while using TLA+ in particular and Lamport's theory of refinement in general.

I must say that the above must be at best considered speculative from a practical methodology point of view. I do suggest a concrete alternative nor do I have extensive experience in validating actual distributed systems.

> Rather than? I'm not sure I understand the question, so maybe

This is the easier part and a less speculative comment :). In the theory of refinement in branching-time framework developed in [1,2] the refinement can be any computable function rather than just a projection of states space. This often is very helpful. For example a refinement map for a pipelined processor is defined by invalidating all partially executed instructions and setting the program counter to the oldest instruction in flight. This is relatively more involved than just a projection map that "hides" the internal states. Notice that, as we have already mentioned earlier, branching time refinement implies trace containment, a linear-time notion. So there is nothing lost. There are follow up papers with case studies illustrating the use of this flexibility to define refinement map that makes automated reasoning more amenable.

Thanks for clarifying the temporal quantification.

[1] [http://www.ccs.neu.edu/home/pete/pub/charme-refinement- branc...](http://www.ccs.neu.edu/home/pete/pub/charme-refinement-branching- time.pdf) [2] [https://arxiv.org/pdf/1502.02942v1.pdf](https://arxiv.org/pdf/1502.02942v1.pdf)

<comment><USER3> I read the paper in the first link, and I may read it again, but I have a few remarks:

1\. Refinement mappings in TLA also allow arbitrary state functions.

2\. There are very good reasons to want machine closure, and one of the things mentioned at the end of that paper as an advantage (that there is no necessity in a separate liveness property), is actually a serious disadvantage.

Let me give you an example, using what Lamport calls "raw" TLA, which is TLA which is not stuttering invariant:

        Spec1 ≜ t = 1 ∧ x = 1 ∧ ◻(t' = t + 1 ∧ x' = x + 10)
        Spec2 ≜ t = 1 ∧ x ∈ Nat ∧ ◻(t' = t + 1 ∧ x' = x + 10 ∧ t = 10 ⇒ x = 100)

The two specifications specify the same behavior. But, in this world, a safety property can imply a liveness property, e.g. x ∈ Nat ∧ ◻(x' = x + 10) ⇒ ◇(x ≥ 100)

Now, this is bad because the second specification is actually not implementable in a computer with complexity similar to that of the behavior as it relies on "angelic" nondeterminism. It is possible to use angelic nondeterminism in TLA (it's important for high-level specs) like so:

        Spec3 ≜ t = 1 ∧ x ∈ Nat ∧ ◻[t' = t + 1 ∧ x' = x + 10 ∧ t = 10 ⇒ x = 100]_<t, x> ∧ ◇(t ≥ 10)

(the square brackets mean that either the action is true, or <t, x> are unchanged) But now, the liveness property is not implied by the safety property, and it requires an explicit statement in the formula. That last formula, Spec3, is not machine-closed, and this makes it easier to see that the algorithm implied is unrealizable as written.

<comment><USER5> > 1\. Refinement mappings in TLA also allow arbitrary state functions.

Could you please point me to a reference for this. The completeness result from "The existence of refinement mapping" only includes projection. I know that TLA does allow existential quantification over internal variables (corresponds to hiding) and one can eliminate the quantifier by defining a function. But notice that is just the logical way of specifying "hiding" and is different from having arbitrary refinement mapping.

>2\. There are very good reasons to want machine closure ...

Definitely. However, these are the concerns when we are specifying a system -- is the specification correctly models the system that I have in mind. While the comments in the paper -- no necessity to separately analyze liveness -- refers to the easy while proving that a concrete specification "implements" an abstract specification. And this is what your example specifications illustrates (thanks for specific examples, it helped me to understand the point you are making).

<comment><USER3> > I know that TLA does allow existential quantification over internal > variables (corresponds to hiding) and one can eliminate the quantifier by > defining a function.

That's what I meant. The functions do not necessarily apply to hidden variables only, but to any variable. E.g., in TLA+, if Spec1 has variable x and Spec2 has variables q and t, you could write:

        Spec2 ⇒ INSTANCE Spec1 WITH x ← q + t

Which implements Spec to by mapping (q + t) to x.

> But notice that ... is different from having arbitrary refinement mapping.

Can you explain what you mean?

> However, these are the concerns when we are specifying a system -- is the > specification correctly models the system that I have in mind.

Right, but you want an easy way to determine if a specification is machine- closed or not. TLA provides a sufficient _syntactic_ condition. A specification is _guaranteed_ to be machine closed if it can be written as

        Init ∧ ◻[M]_vars ∧ Fairness

Where M is an action and `Fairness` is a conjunction of ⬦ clauses containing WF/SF (weak/strong fairness) conditions only. In fact, when testing safety properties alone (which is normally the case), a specification is usually just

        Init ∧ ◻[M]_vars

Which TLA guarantees is machine-closed. Otherwise, a ◻ formula alone may not be machine-closed (and may imply a liveness property, like in my example above), and proving whether it is or it isn't may be non-trivial.

</comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment><comment><USER6> I find myself recreating different versions of state machines for almost all projects I work on. The ability to reduce a problem to a state and reactions to external stimuli, resulting in a new state, is a powerful, simple and easily testable/encompassing process. You can write cohesive and testable pieces of code, and return to them later, and easily understand what you originally wrote. When I start writing callbacks and event-based code, I find myself confused and tracing through a "spaghetti of callbacks".

</comment><comment><USER7> >Yet computer scientists are so focused on the languages used to describe computation that they are largely unaware that those languages are all describing state machines.

That brings back to mind that someone corrected me on that, computers were closer to linear bounded automata. Now I'm even more confused.

[https://news.ycombinator.com/item?id=12557351](https://news.ycombinator.com/item?id=12557351)

</comment><comment><USER8> I am working with computer science students and I see Lamport's point.

Even for simpler programming exercises the students start coding away, writing dozens of nested function calls with different global and local states, when a simple state machine models the problem quite well and building one would lead to much cleaner code.

The problem I see (and what we're trying to repair in our basic courses) is that students learn their basics in CS and then they write java or c and the primitives they know from theory (sets, tuples) aren't very accessible and they can't just use the definitions they've learned (imperative vs. Functional), so they just hack away without thinking.

We're trying to start with Haskell as early as possible to be close to the definitions from the first semesters.

<comment><USER3> This is not quite what Lamport is saying. Lamport is not talking about the process of writing programs, but about reasoning about programs. You build an analog circuit by composing components (that would be programming), but you reason about what it does and how it does it using ordinary differential equations. Lamport notes that state machines are a universal model for discrete dynamical system, and they can be manipulated mathematically using notation analogous to ODEs for continuous dynamical systems. Lamport does not suggest that you should _program_ using a state machine abstraction, nor does he offer any notation for doing so.

<comment><USER9> > Lamport does not suggest that you should program using a state machine > abstraction, nor does he offer any notation for doing so.

On the other hand, David Harel, also a mathematician, did both of these things. [https://news.ycombinator.com/item?id=13157153](https://news.ycombinator.com/item?id=13157153)

<comment><USER3> Yes, and both rely on Pnueli's temporal logic. But Harel and Lamport apply a similar theory to different domains, neither suggesting the exclusion of the application to the other domain. Lamport offers _abstract_ state machines as a universal mathematical formalism for reasoning about discrete dynamical systems regardless of how they're constructed, while Harel offers _concrete_ state machines for _programming_ safety-critical, hard-realtime embedded systems that are easy for both humans and machines to analyze.

</comment></comment></comment></comment><comment><USER10> It's interesting to compare this with the push for functional + immutable design systems, which leads to a similar result: writing programs that operate as "nextstate = f(state,inputs)"

<comment><USER3> Immutable (or "pure") and functional are two very different things. State- machine-based _programming_ languages -- usually called _synchronous_ languages (note that TLA+ is _not_ a programming language) -- may be pure, but are not functional. Functional means that you model a computation as a function. In synchronous language and in TLA, a computation is not a function but a _behavior_ \-- a sequence of states. You can say things like "no matter what happens, the value of x is always greater than 0", or "if y is ever negative, then z must _later_ become negative, too". In addition, in TLA the next state is not a _function_ of the current state; (state, nextstate) is a _relation_ , so a state can have multiple possible next state, chosen nondeterministically. This is essential when modeling nondeterministic programs (like those involving concurrency or distribution), or even when modeling deterministic program at a high level of abstraction where you don't care about the details ("the algorithm will do either A or B for some reason I don't care about").

</comment></comment><comment><USER11> Paper by Leslie Lamport (2008).</comment>


Ask HN: Review my startup, Localo.com - <AUTHOR>

Hi everyone,
We just announced our marketplace and we would love HN readers to give us feedback about overall look & feel, our form to submit itineraries and our affiliate program.
With Localo, travellers can book rooms, houses, sofabeds, boats, domes, and even complete islands from locals willing to share them.
Travellers tell what they want from their stay, and then locals compete for them by submitting personalized offers.
We expect Localo to provide the best matchmaking mechanism for travelers looking to experience a city like a local, so we will appreciate any suggestions related.
Links: http://localo.com http://localo.com/submit http://localo.com/referral_program <comment><USER2> I really liked the site, in general. I could see using this when traveling with my family to find places to stay once there are more listings available.

Some thoughts as I browsed the site:

* Everything was slow. The "About" page was the worst but the listings themselves took 5-10 seconds to load. Later, I waited 30 seconds for the search page to pop up. (I was searching for a town that had no listings from the front page.)

* The search form cleared the location when I navigated "back" to it from a listing. It'd be good to allow for searching, click a listing, back, click another listing, ...

* If I'm looking at renting a house are there restrictions on the times/dates? If so, I didn't see that indicated on any of the listings. Do I get the whole house or am I sharing it with the owners?

* I found the wording for some things a little strange:

      "This place does not welcome pets :(" Seems negative for some reason. How about, "No pets, please."?

      "We don't have security mechanisms as 24-hour security, fire alarms, safe deposits, etc."  I wasn't sure how to parse that the first time I read it. I'd suggest keeping it simple - "Not available"?

      "Invite to Submit Offer" Gives the impression of a lot of steps. "Request a quote" seems more familiar.

* The lack of prices on most of the listings was surprising. What does "Upscale" translate to? If I have to interact with the owner I don't want to waste my time or theirs. Perhaps a price range would help.

<comment><AUTHOR> We have implemented caching mechanisms to speed up the load time. Thanks for the wording suggestions, we will look into them.

About the restrictions on times/dates, usually hosts provide their arrival/departure policy so if you haven't seen the information it maybe because it was not provided yet.

Our marketplace allows hosts to provide opaque personalized rates, so sometimes the hosts prefer not to make their rates public and provide the offers by request. I love your price range suggestion to clarify what is Upscale, we will try to add it soon.

</comment></comment><comment><USER3> Very nifty app - took a look at accommodations for Chicago, where I will be going in two weeks, and was disappointed to have found only one entry. Along with it, there was no price information.

However, your very clean presentation on location, time from touristy attractions, and amenities included is fantastic.

What are you guys using for outbound email? I'd love to talk shop.

Good luck!

</comment><comment><USER4> Your business seems remarkably similar to AirBnB. The only problem I noticed was on your 'about' page. It loads hundreds of thumbnail images (pictures of the "locals"), so it takes far too long to load.

<comment><AUTHOR> Thanks for the suggestion jcr, we want to showcase our community, but it may have the same effect with just a subset of localos.

You are correct about the similarities with AirBnB, we think that personalized offers provide a better matchmaking mechanism for travellers looking to experience a city like a local.

</comment></comment><comment><USER5> I like the idea. I was confused when I searched Toronto and the top result had a price but the rest didn't.

My question is how scalable is this? Is the idea to eventually integrate hotel listings?

<comment><AUTHOR> Localos can publish their rates or only provide personalized offers for travellers, which are usually opaque. So in Toronto's case, only one localo has decided to publish their prices. We do not plan to integrate hotel listings since our current focus are the long tail accommodations. Any local is able to list his/her place in our marketplace, so we think there is enough room to grow.

<comment><USER5> I think you should be looking at the large picture here. Yes the local market makes sense to start but the idea is to scale the business up to take a % of a billion dollar industry not a cottage industry with say $100 mill total market.

</comment></comment></comment><comment><USER6> <http://localo.com>

</comment><comment><USER7> Great idea, pages loading up slowly though.

<comment><USER3> Seconding this. The initial load was mighty slow, but everything else after that was pretty snappy.

<comment><AUTHOR> Thanks for noticing it. We are checking how can we speed up the initial load.</comment></comment></comment>


2048 Directory  - <AUTHOR> http://2048.directory/

<comment><USER2> Wow, there are a lot of TLDs and 2048 clones.

<comment><AUTHOR> Now for a 2048 clone of TLDs!

<comment><USER3> We clearly need a new TLD named ".2048".

</comment></comment></comment><comment><USER4> The 2^53 is missing.</comment>


Technology trends on the web - <AUTHOR> http://trends.builtwith.com/

<comment><USER2> Very interesting, and you can see some new tools that you didn't know about before.

<comment><AUTHOR> Yep. Actually, some stats surprised me quite a bit... obviously, you should take those stats with a grain of salt as they are not 100% accurate.</comment></comment>


Can we solve giving validation to life experience vs. a degree? - <AUTHOR> As someone without a degree a constant problem I have when trying to find a better job is that I have no degree.
- At my employer, want to move up? You need a 4 year degree, in anything, period.
- When applying to jobs, "Bachelors required, Masters preferred" and I've even seen this for administrative assistant listings.
- Even startup at the tens of employees stage often want degrees.
It seems the only way really around this at present is to have successfully exited a startup. Since we're on YC, take Sam Altman. Sam dropped out of Stanford to focus full time on Loopt, ultimately selling Loopt. 2 years after Loopt was purchased Paul Graham handed over the keys of the castle and named Sam president of YC.
Obviously that's not a realistic scenario for 99% of the population. That's a whole lot of luck and right place right time. PG seeded Sam with Loopt, Sam was then offered a part-time partnership with YC where he probably proved himself more, then exited Loopt which gave PG plenty to look at and go "yeah, he's capable of this, let's take a chance on him" but a lot of people have mountains of life experience but get the door slammed in their face before even speaking to a human being because they don't have a 4-year degree listed on their CV (in fact, some online applications make you input your degree, university and GPA in their own fields and will not let you leave them blank).
We've got a lot of smart and talented people here on HN, and a lot of startup types and independently wealthy individuals. Who wants to take a stab at the problem, even if just in a hypothetical approach?
Do, as a society, we start a sort of staffing firm with 'counselors' that evaluate individuals and use some sort of secret-sauce formula to say "this is what we think about this individual" as a sort of professional certification/degree alternative?
What outside of the box ideas might there be that we can discover and work towards implementing? <comment><AUTHOR> I think there are tons of ways to evaluate a given individual if one were to go the 'secret-sauce' route:

\- some standard testing

\- some aptitude testing

\- some sort of manual/physical creativity with some sort of toy construction set (lego, erector) to solve a problem or demonstrate something,

\- give a scenario and then allow unrestricted access to a phone and a computer connected to the internet with a time limit to present one or more potential outcomes/solutions/etc "Global warming is an issue, we face these general problems. Go!" or "It is winter, power goes out at your office, you have these 20 items. There is this much snow, it is this temperature and you live x miles from your workplace and may not use a vehicle. Power will not be restored for x days, what is your plan type things.

\- Asking specifics about past job/club/religious duties and developing a good idea of general skills

Obviously degrees have their uses but college isn't for everyone and not everyone needs a degree to do well in various roles/fields. Yes, I want a doctor/lawyer/engineer to have a degree but apparently 'One in eight billionaires on the Forbes 400 are college dropouts,' [1] (and yes I know that article starts out saying people should get degrees, because it's a business site and this is 'how it is done').

Why can't we find a way to give people without degrees more of a chance?

[1] [https://www.businessinsider.com/billionaire-college- dropout-...](https://www.businessinsider.com/billionaire-college-dropout- zuckerberg-bill-gates-2018-10)</comment>


A Gentle Intro to Monads … Maybe? - <AUTHOR> http://sean.voisen.org/blog/2013/10/intro-monads-maybe/ <comment><USER2> This does not appear to implement a monadic computation. A monadic computation can examine intermediate steps of the computation and perform flow control decisions, i.e. in Haskell:

        showFlowControl :: Person -> Maybe County
        showFlowControl p = do
            country <- personCountry p
            case country of
                USA -> do
                    state <- state country
                    county state
                SomethingElse -> do
                    province <- province country
                    provCounty province
                _ -> Nothing

(The implied data structure is a bit bizarre, but, bear with me.)

Unless I am misreading the code, this is the rather frequent misunderstanding that monads are just fancy chained method calls, which does not suffice to capture the monadic behavior. Note merely trying to stuff "if" statements into the calls won't be sufficient, because you must actually _affect the chain that is called_ ; either you call "state" or you call "providence" but you must decide that "in" the monadic computation, not in a mere method chaining.

Mere method chaining is not monadic. There is, I believe, no clever way to implement monadic computations in Javascript without _nested_ functions; every layer of the monadic computation _nests_ , and in most languages this can not be hidden in any practical way. This is one of the major reasons monadic computations are impractical in most languages.

<comment><USER3> Fancy chained method calls are equivalent to do notation without any binds, which only needs Applicative. Monad is sufficient but not necessary.

That said, this _does_ appear to be a monad - they implemented bind correctly.

Haskell:

        instance  Monad Maybe  where
            Nothing  >>= _      = Nothing
            (Just x) >>= k      = k x

From the article:

            var Nothing = {
                bind: function(fn) {
                    return this;
                }
            };

            var Something = function(value) {
                return {
                    bind: function(fn) {
                        return Maybe(fn.call(this, value));
                    }
                };
            };

It doesn't really _use_ it right, for the most part, though (... I think; I only skimmed).

<comment><USER2> I think bind is essentially correct too, but the failure to use it properly tends to suggest a level of understanding too small to be writing Yet Another Monad Tutorial. I've seen real JS monads, and I believe they are irretrievably ugly to use, with the need for nested functions.

</comment></comment></comment><comment><USER4> So, I don't get all the pain. There are a lot of 'rules' around monads, but conceptually they are widgets in a workflow.

In imperative programming you say what you want to do and do it at the same time.

With monads, you assemble a chain, just like dropping boxes on a workflow UI. Just like a workflow, you have 'defined' what to do, but not done anything with it yet. When you give it input, the chain performs the transforms you have defined for it, and you can pull out the result.

Again, not a 'technical definition' but this paints a picture in people's heads that they can appreciate and follow it up with details.

For serious learning, I recommend Eric Lipperts blog post on the subject: [http://ericlippert.com/category/monads/](http://ericlippert.com/category/monads/)

</comment><comment><USER5> I made a port of some Scala Monads to Javascript. Mostly, Option and Either. Makes for some really nice, and you won't find yourself doing (typeof foobar === "undefined").

[https://github.com/mbseid/Monad.js](https://github.com/mbseid/Monad.js)

</comment><comment><USER6> Suggestion for the author. Reformat the code of your translated example. I truly found it impenetrable until I formatted it more like idiomatic d3.js code. With this reformatting, I'll buy that it is less annoying and tedious than the original.

        console.log(
          Maybe(person)
            .bind(function(p) { return p["address"]; })
            .bind(function(a) { return a["state"]; })
            .maybe("State unknown", function(s) { return s; })
        );

And then I realized that _d3.js code is monads!_ (shout it like Charlton Heston in Soylent Green)

<comment><USER3> I'm not sure this is actually using the fact that maybe is monadic - I think this much can be done with applicative functors. In order to really be monadic, you'd need to run the next bind inside the function passed to the previous bind.

</comment></comment><comment><USER7> This misses the more simple explanation of Monads:

        They're functions provided by the language core,
        that cannot be implemented in the language itself,
        because the language rules forbid it.

This should be the first thing any of these introductions explain. Afterwards they can spend however long they like explaining all the rules for which Monads are solutions and how they get around them, safe in the knowledge that the reader will at least know why monads exist and why people are going to all these efforts.

<comment><USER8> Can you give an example of a language rule which a Monad might implement? I sort of understand what you mean, but an example would help me (and maybe others?) form a more concrete understanding.

And if at all possible, don't use Haskell being unable to mutate state as an example. Maybe an example for Javascript or Python?

<comment><USER7> In Haskell some big rules you get around are:

1\. Functional purity must be maintained for all code. This means for example that no side effects (IO) may happen. Thus some monads exist for the simple purpose of doing IO.

2\. As you mentioned, state may not be mutated. To circumvent that there's a monad that simply marks a code block as "variables of this type may be changed in here, as long as they don't get out of this scope".

3\. I only know about this vaguely, but as i understand it Haskell is strictly typed. This makes some things inconvenient, so Maybe allows some loose typing.

I can't give examples in JS, but i actually got one for python: Decorators. In Python you cannot implement a function with this specification:

        It takes exactly these inputs in this order:
          1) a function object,
          2) a function name,
          3) a multiline anonymous function.
        It calls the function passed as the first parameter,
        with the third parameter as its parameter.
        It takes the result, which is expected to be a function,
        and installs it in its calling scope as a new function,
        with the name passed in the second parameter.

Since this is not possible to implement in pure python, decorators were implemented in the core. I'm not 100% sure, but i think they actually do qualify as monads.

<comment><USER9> 1\. IO doesn't have to be a monad, it simply was designed that way because the monad interface is so nice. In older (very functional) versions of Haskell this wasn't the case.

2\. That's honestly not a bad description of the State monad spoken using procedural vocabular, but it is _very_ far away from an accurate representation. At the very least it's worth noting that this isn't a syntactic thing—all of the elements are first-class.

3\. Maybe is still strictly typed. It causes the possibility of null to be represented at the type level so that nobody ever accidentally forgets to check it. I _never_ get null errors in Haskell.

Finally, decorators are Functors perhaps, if you strain to look at them that way, but not at all monads.

<comment><USER7> > Finally, decorators are Functors perhaps, if you strain to look at them that > way, but not at all monads.

You can't say such a thing and then run away without saying why not. How is anyone supposed to learn about this if nobody explains the why? :)

Edit:

> 3\. Maybe is still strictly typed. It causes the possibility of null to be > represented at the type level so that nobody ever accidentally forgets to > check it. I never get null errors in Haskell.

Let me reword point 3. Haskell is strict about types in that it asks you to do a lot of checking. Using Maybe you can make Haskell behave like a loose language that doesn't require any checking from you.

<comment><USER9> Python decorators are basically as follows

        @f
        def g(args) body

translates as

        g := f(fun(args) body)

If my syntax makes sufficient sense (g := a translates as "assign a to g"). That means that decoration is nothing much more than function composition which forms a very particular functor. That same Functor also provides a monad, but continuing to work this example until that's clear is so far from obvious that I think it'd only serve to mislead.

<comment><USER7> If it isn't a monad, but provides one, then this seems to be the perfect example to explain on, since decorators are understood and monads are something smaller than decorators and as such should be able to be explained by reducing the explanation of decorators.

Also do keep in mind that i didn't say decorators were a monad in any language, just in python, because they cause a curious side effect that is otherwise only attainable with much self-repeating and hard-to-read syntax.

<comment><USER9> Yes. Decorators do not provide a monad at all, not even in Python. The very dim relation between the two is academic at best and completely irrelevant at worst.

<comment><USER7> Yes what?

Also, you wrote:

> decoration [...] forms a [...] functor. That [...] Functor also provides a > monad

That is entirely at odds with the statement:

> Decorators do not provide a monad at all

Frankly, at this point i'm strongly considering the possibility that you're just trolling me.

<comment><USER9> I'm not trolling, we're just talking around one another. There are a significant number of concepts that aren't easily explained in plain english required to get at the heart of "what is a monad?". Even that question itself is already making an interesting choice of definition and set of questions that can be confusing unless analyzed carefully (in particular "Monad" as a noun can mean something very different from monad as an adjective and both meanings are frequently used in descriptions of "what monads are").

I'm attempting to be strongly consistent in everything I'm saying, but it's not necessarily possible to both do that and keep speaking simply.

The why of monads is that they're a very nice API or Interface that allows you to write easily analyzed and easily refactored programs. Most of the other descriptions are the why of a _particular_ type which happens to instantiate the API.

<comment><USER7> That sounds nice and all, but i still don't know why you contradicted yourself.

<comment><USER9> Okay, I'll explain it. Apologies for the apparent contradiction—I'm not trying to move the goal posts, just trying to avoid going into the weeds.

As I said, Python decorators are essentially just a syntax for function composition.

        @deco
        def g(args):           g := deco(g(args): body)
          body

where (g := a) means a is assigned to the name g. As it turns out, functions form a type which is a functor over the result type. The fmap (arrow map) is just function composition. In Haskell you would write this as

        instance Functor ((->) r) where
          fmap g f = g . f

where the (.) syntax is also function composition, very similar to the @deco syntax in Python. In this sense, I stated that @deco is a Functor.

Now, the function data type also admits a Monad instance (which some people abuse terminology to say "functions are monads" but I highly dislike that terminology when speaking carefully).

        instance Monad ((->) r) where
          return a = \_ -> a
          (f >>= g) x = g (f x) x

So in this way, the _function_ type instantiates both Functor and Monad, but composition (which is (.) in Haskell and @deco is Python) is not directly related to the Monad.

Instead, a function's Monad instance allows us to "defer application" and translate something like

        \x -> (1 + x, 2 + x)

into

        do a <- (1+)
           b <- (2+)
           return (a, b)

In this format it's pretty stupid looking, but we also tend to call this monad instance the "Reader Monad" which indicates computations which occur under a fixed set of configuration information—some static global variables.

For instance, if I had a Key type which represented some encryption key I could change my encrypt and decrypt functions from

        encrypt :: Key -> String -> String
        decrypt :: Key -> String -> String

to

        encrypt :: String -> Reader Key String
        decrypt :: String -> Reader Key String

which, allows us to defer that extra `Key` argument until later.

So, that's what I meant by saying that @deco isn't the Monad that its Functor represents—it _only_ has the composition aspect, not the deferred application aspect.

</comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment><comment><USER10> It seems like a good intro, but one of these days I would really like to see a monad explanation that _doesn 't_ use Maybe.

<comment><USER8> What kind of example would you like to see other than Maybe?

<comment><USER2> List is probably the minimal intro you can do without trapping yourself. It's too easy to implement something that can do the 1-or-0 elements in Maybe, but still isn't a Monad. You should also do a conditional, which I believe this code fails; that is, a monadic computation can examine the intermediate results and make flow-control decisions.

The minimal example is probably something like:

        minimal :: Int -> [(Int, Int)]
        minimal i = do
            x <- [i, i + 1]
            y <- [i, 10 * i]
            if i `mod` 2 == 0
                then return (x, y)
                else return (y, x)

Running this in GHCi, I get:

        > minimal 2
        [(2,2),(2,20),(3,2),(3,20)]
        > minimal 3
        [(3,3),(30,3),(3,4),(30,4)]

The evenness or oddness of the parameter controls which side of the tuple gets which element.

It may still be possible to implement this without implementing a full monadic computation, but if you can pass this test using something like bind or join, you're probably at least on the right track.

<comment><USER2> This conversation is dead, but I've expanded on this at [http://www.jerf.org/iri/post/2928](http://www.jerf.org/iri/post/2928) .

</comment></comment></comment></comment><comment> vezzy-fnord Is it bad if I find the null checks to be simpler in this particular scenario?

<comment><USER11> I hope not, for my own sake!

</comment></comment><comment><USER12> Sean Testes » A Gentle Intro to Gonads … Maybe?

Sorry, I had to.</comment>