Monday, September 05, 2022

Crowdsourcing programming language

With the embarrassment of riches in the low level programming language space (Zig, Jai, Odin, Beef, V, Jakt, Val, Rust?), I was thinking about what the next high level programming language might look like...

It's got to be open source and have super support for the web. IMHO it should support regular programming as well as interactive (i.e. shell) use. It ought to be fully reactive!

Simply, "reactivity" makes programming more like spreadsheets. Though the flapjax project did this with javascript many years ago, all the hot new javascript frameworks are finally adopting this kind of approach (svelte, solidjs, vue). It's good for GUIs, and it could be great for programming in the general. Imagine View Source for all of programming!

As long as we're talking "high level", it could have all sorts of cool features thrown in, like transparent persistence, CALM consistency support, easily querying JSON and other formats. Implementing features takes a lot of engineering effort however. That's a big lesson from Zig's increasing success: engineering matters a lot. Tangentially, here're a few links in praise of Zig engineering:

Unfortunately, I have the first virtue of great programmers, so won't muster the necessary engineering myself. That could be the major feature of the next high level language: a notebook/IDE/repl that supports inline extension of itself, and sharing of extensions!

Querying JSON

The state of the art in querying JSON outside of a full program:

Friday, April 29, 2022

Lisp Disappointment

Once, I was really excited about "code as data". If you enjoy the power of code, you should love code that works on code, right?

Problem is, almost anything interesting about code working on code isn't about syntax.

Compilers have been optimizing code forever, and there's no indication that it's any easier to optimize lisp.

Static types help to reason about, navigate, and refactor code, and those are not part of traditional lisp. The same goes for referential transparency.

And bidirectional programming provides other amenities that having nothing to do with lisp.

Thursday, April 28, 2022

Peer to peer social

Of the communication modes discussed in the previous post, "perusal" is the one that needs the most love right now. We're talking Twitter territory, so this post is timely. Remember "perusal" refers to communication which is both asynchronous and involves broadcasting to many people.

The wonderful thing about Twitter is that, when you reada something interesting anywhere, you can speak directly to the author about it on Twitter, and they'll respond, as often as not. This feature springs from the famous social media "network effect"; Twitter is where all the authors hang out now, so it's where everyone goes to talk to authors, creating a self-reinforcing cycle. People say that only a paradigm shift can beat an existing network. If new competitors try to compete on features, they'll get squashed by the existing network, which can relatively easily copy those features.

So let's talk about what peer to peer social might look like. That is, a system for perusal-type communication which doesn't depend on any central authority. People agree this is a hard problem. So we'll talk about it in avowedly handwavy manner.

1) It should be http based. Among other advantages, this will help with ramp up and network effects: even before our system takes over the world, its posts will be no worse than blog entries.

2) Its scope should include direct messaging, partly because that'd be nice to have too, but mostly because we can use the relationships between people to bootstrap some kind of trust graph. Much of the social graph is already available in people's phone's Contacts app.

3) It'll have two different roles in the system, which I'll uncreatively refer to as "people" and "servers"b. Both roles have public-private key pairs. People's apps record each others' public keys, so the system is local first that way. I don't need Facebook to manage my electronic contact for my brother. His app signs his posts, so any server can send them to me, and I'll know that they're really from him.c

Even for perusal, I don't really need Facebook fanciness to highly rank posts from my brother, or from my favorite musician. It's now easy to communicate with anybody in the world; the hard part is preventing enough of that communication to keep it useful.

4) So servers differentiate by recommending new posts that people want to read, that is, posts from strangers. Continuing the spirit of search engine and social media companies, there's a tension between what I want to read, and what other people want me to read. "Advertising" is when intermediaries get paid to sacrifice the former for the latter. If people know the public keys of all the people that they're already interested in, and those keys are used to sign all posts, there's no longer any robust way to insert advertising into those posts. The natural place for advertising then becomes the feed of new recommended posts.

5) People's apps should employ multiple servers. People can rate posts, and those ratings will be automatically shared and used to pick the best servers, forming a kind of market place for servers. Depending on how expensive it is to run a server, the market will efficiently price the fraction of the recommendations that will be advertising. Now we're really getting handwavy. Who says this can work? Well, we can talk about it more; first let's finish the sketch. :)

6) We also need to support a way for ratings to flow from readers to publishers. Partly because this is something that publishers like, but it needs to be supported natively so that servers can demonstrate to advertisers how effective they are. It's the trust graph which defeats sybil attacks. Attackers can create as many fake people as they like, and those won't matter because the rest of the world doesn't connect to them.

7) So everybody's rating everything, does that mean that all ratings have to be copied everywhere? Distributed consensus and trust algorithms appear to assume so. And of course, there's blockchain. But I hope we can do better. We don't need any of this to be precise, we just need it to more expensive for spammers to game the system than it would be for them to simply run a competing server. Therefore, we ought to have a protocol that enables participants to exchange ratings in a sparse, just in time fashion. We're talking about a kind of guerilla PageRank. Unfortunately, the term "gossip protocol" is already taken, so once we actually figure out his protocol, we'll need a new catchy term for it.

OK, that's about enough speculation for now. There are some major ideas that'll I'd like to talk about in the future: How do we support decentralized topics or groups? How can multiple independent parties evolve the software and the protocol? And of course, how would all of this effort make a system much better than existing centralized networks?



a When I say "read", I mean "read, listen to, watch, or otherwise experience".
b Once I say "server", many readers will immediately think "oh, that's federated, not peer to peer". Well I'm still hoping this'll be a bit more dynamic than your typical federated system like email. We'll see :).
c There are a lot of details to get right here, for example post-compromise security

Thursday, April 14, 2022

Communication modes

Nobody answers the phone anymore. 😁

Seriously, nowadays there are three distinct modes of communication:


Synchronous

One to one

Waiting for your call

yes

yes

Direct messaging

no

yes

Perusal

no

no

"Waiting for your call" means picking up or responding immediately. Nowadays it's restricted to: enjoying real conversations, getting to know somebody, when you're paid to be available, and failures of direct messaging.

"Direct messaging" means that somebody sent a message just to you. I believe the etiquette is that these deserve some response within a day or so, lacking any other clear time constraints. Assuming you're not famous.

Of course asynchronous messaging tools can also be used for synchronous communication. The three dots that indicate the other part is "typing" help distinguish a little.

What we want for "perusal" is a ranked list of items that we can check as infrequently as we want. The things that we are most likely to want should be at the top. Timeliness could certainly be a factor.

From the technical point of view, we want this stuff to be more standardized and unified. For example, it'd be nice to initiate a message to somebody from their Contact in my phone, via Whatsapp because that's their favorite, without me having to remember it. It'd also be nice to have a conversation with two friends at the same time without having to think about compatibility.

We also want our communication to be local first software, and that's what the next post is about.

Friday, February 25, 2022

SBE Repeating Group Gotchas

SBE is the fastest off-the-shelf way to serialize data.

To help support that, it really prefers you to use fixed length fields. If you need an unknown number of submessages, it's a littler harder...
  1. There is very little documentation in general, and for repeating groups it's incomplete. A big omission: you must always explicitly decode every member of every repeating group, in the same order as they are defined in your schema. If you do not do this, your results could be mysteriously corrupted. Alternatively, you could explicitly "skip" the ones you are not interested in, with the relatively new sbeSkip() call. E.g.

           
            weWantSerialNumber(carDecoder.serialNumber());
            weWantModelYear(carDecoder.modelYear());

            FuelFiguresDecoder fuelFigures = fuelFigures(); //we don't want fuelfigures

            if (fuelFigures.count() > 0)                    //but if it was encoded...

            {                                               //we must iterate over it anyway

                while (fuelFigures.hasNext())

                {

                    fuelFigures.next();

                    fuelFigures.sbeSkip();

                }

            }

            PerformanceFiguresDecoder performanceFigures = performanceFigures();

            if (performanceFigures.count() > 0)

            {

                while (performanceFigures.hasNext())

                {

                    performanceFigures.next();

                    weWantOctaneRating(performanceFigures.octaneRating());
                    
    performanceFigures.sbeSkip(); //actually needed in the sample

                }                                 //because there's a nested repeating group!

            }

  2. If you're working with code that does not use repeating groups, and you add one, lots of things might break unexpectedly. Because wrapping a decoder with an incorrect blockLength will mostly work if you don't need to decode repeating groups or variable length data fields. You need to use the blockLength on a header message, which in turn needs to be the BLOCK_LENGTH of the encoder. This excludes the length of the repeating groups. On the bright side, nowadays there are wrapAndApplyHeader() convenience methods on both the encoder and decoder objects. 
  3. If you want to read repeating groups from the same decoder more than once, you need to call sbeRewind().
  4. There's currently a bug in which toString can break repeating groups

Thursday, February 17, 2022

Bookmarks and Full Text Search

Bookmarks suck. They don't have to.

They suck because they don't actually work for their primary use cases:
1. where did I read about that cool thing X? 
2. where was that cool article about Y that I didn't have to time to read, but would like to read now?

Heck, at some point I found it easier to google for pages instead of trying to find them in my bookmarks. 

Other use cases should be handled slightly differently:
3. "Bookmarks Bar", "Pin a tab", and "Save a shortcut" features are all useful for commonly-used pages or web apps.
4. "Manage Search Engines" feature is useful for sites that you want to search often.
4. I'll bet Chrome soon adds support for saving tab groups, which will be useful for gathering lots of pages for a particular project, e.g. a bibliography.

Practically speaking, labels and categorization are annoying to add, and don't really help find things later. The titles that maintainers give to web pages are also often unhelpful for retrieval.

The solution is automatic full text search of all bookmarked pages.

That way, you can retrieve a page by anything that you see on the page, without any additional organizational work.

On a Mac, you can get already get this via Spotlight and print with "Save to PDF", though there's some user interaction involved.

Worldbrain's Memex supports full text search of bookmarks well, and for free. I have mixed feelings about it. On the one hand, this feature is implemented by an open source plugin, and the maintainers support local first software. On the other hand, they have a much bigger vision, and it's unclear how much they want to support the free part of it. It currently sports a non-dismissible reddish prompt to register for the cloud support.

Probably I'm Dunning Krugering, but it just doesn't seem like that difficult of a feature to implement. And it feels like a core service. I wonder how Apple and Google might decide to take on a feature like this. There's no reason to end on a low note. If you don't care about sync-ing across your devices, go ahead and install free Memex on your PC, and on your tablet!

Friday, January 28, 2022

iPhone Shul Shush

 Now it's finally possible to silence iPhones in shul (or church or whatever)!

  1. Settings / Focus / Do Not Disturb / Add Schedule or Automation / Location
  2. enter something to identify your shul, e.g. part of the name
  3. select the right one
  4. touch Done
Woo!

Now if only there were a way to automatically prompt people to do this when they arrive, using something like airdrop. "This location wants you to be in Do Not Disturb. Is that alright? Yes - silence notifications here, No - continue to notify me and don't ask again, I Don't Know - ask me next time I visit"

Monday, January 03, 2022

Vim plugin idea

When working with verbose logs, you want to suppress uninteresting lines.

So, wouldn't it be cool if you could select two lines, and vim would hide all lines that are similar to them.

1237 boring is blah, all good

1238 boring is foo, all good

1238 WARNING

1240 boring is blah, again

When the user selects the first two lines above, vim would hide lines matching the pattern \d+ boring is \w+, all good.

It'd need the ability to unhide the next or previous line.

We could also get fancy, and support generalizing previous patterns. In the example above, you could later choose the fourth line, and vim would amend the previous pattern to \d+ boring is \w+, [ a-z]*. We'd have to document some decisions about how general to be, e.g. it's not obvious above that we should use \d+ instead of 123\d.

Closest existing thing I could find: https://www.vim.org/scripts/script.php?script_id=2302

Thursday, December 30, 2021

Scrolling two files curmudgeonry

It's useful to be able to synchronize scrolling through two files, especially for comparing two files that have too many intraline differences.

Vim supports this, with this annoying incantation:

vim -O oneFile anotherFile -c "windo set scrollbind" -c "windo set scrollopt+=hor" -c "windo set nowrap" -c "windo set cursorbind"

Switch between the windows with: ctrl-w ctrl-w

Nowrap is useful because otherwise different sized lines don't display nicely.

Other annoying things: 

  • The documentation can't decide what this feature is called, scroll binding or scrolling synchronously.
  • Gvim doesn't seem to support cursorbind.
Though maybe vimdiff is actually better for most similar use cases. It gets the defaults right, and with gvim it seems to get cursorbind right too.

Tuesday, October 19, 2021

Toki Pona

Toki Pona is a supremely simple constructed language. It's a real Newspeak (from Orwell's 1984), but for good and not evil.

It's very cool. Unfortunately nobody seems to seriously use it for international collaboration. (Apparently this is called auxlang, and obviously English right now is it.)

I was initially grumpy about the "pre-verb" part of speech, but now I believe it's consistent with the general rule that a word modifies its preceding word. For example, a noun precedes its adjectives, and a verb precedes its adverb. So: mi ken pali ==  I can work. mi wile e ken pali == I want the ability to work.

Wednesday, July 21, 2021

Nullaway @Initializer

Nullaway is the currently most practical solution to the billion dollar mistake.

Its @Initializer annotation is one of its practical features; if an object is not expected to be used until one of its methods in executed, that method can be annotated. Then fields can still be statically guaranteed to be non-null if they are initialized in that method, even if they are not initialized earlier.

Nullaway does not, however, statically guarantee that @Initializer methods are actually executed. Therefore, it's better to initialize non-nullable fields in a constructor.

Friday, June 04, 2021

Poor Man's Projectional Editing

Projectional Editing is programming that goes beyond simple text entry. Notable projects are Lamdu and Jetbrain's MPS and to some extent Intellij itself.

Historically, programmers have been reluctant to stray too far from textual programming. A good compromise approach is to represent language features both textually and graphically. For example, the exponents in LegibleMathematics are both superscripted and ^ prefixed.

We could support the property of always keeping the program valid, and still give the feel of textual editing:

  1. as the user types, insert code to make the program valid
    e.g. if the user types if, then we'll insert (true) {}

  2. in the inserted code, automatically select the next bit that isn't forced, to clarify that it will be replaced as the user types
    e.g. in the case of inserted (true) {}, we'll select the (true)

  3. in general, whenever the user types the same thing as the next required bit, just advance the insertion point over it
    e.g. in some existing code if (x) { y(); } the user clicks before the semicolon and types ; }, we won't change anything at all, and just advance the insertion point to after the }

    If we want to support whitespace, then we could change the traversed text to use the new whitespace that the user types.
The above behavior enables the user to just obliviously type brand new code and have it work like in a traditional editor.

The third point is inspired by Parinfer, and Parinfer also has an idea for editing existing code. Inserting a brace somewhere automatically removes the following brace, and removing a brace will insert one as late as possible. e.g.
void f() {
    if (foo) {
        bar();
        baz();
    }
    qux();
    quux();
}

Removing the } after baz() will automatically insert one after quux().

Inserting } after bar() will automatically remove the other } in the function.

Of course we want tab completion, and once we have that and the above, we don't have to worry about intermediate invalid edits. In languages with stronger types than java, it could be that there isn't necessarily any automatically choosable value, in which case we'd have to use typed holes. In mainstream languages, we could use null at the worst.

It's nice to default to a value that causes the new code to essentially be an identity function, e.g. if the user types + then we'll append 0. If the user types * then we'll append 1. But of course this isn't always possible.

Changing a symbol usage changes just that one usage. Changing a symbol at its definition (or initialization in python for example), instead refactors the symbol and renames all of its usages. Changing a signature to remove parameters just updates all usages to not supply those parameters. Changing a signature to add parameters updates all usages to supply a default value as above.

Lastly, with this approach we can be very clever about cut & paste. A "cut" which would involve an invalid program is no trouble; when the "paste" comes, we can do the appropriate refactoring. In the example function above, if the user cuts the baz() call, and pastes it outside the function, we can automatically change the code to:
void f() {
    if (foo) {
        bar();
        g();
    }
    qux();
    quux();
}

void g() {
    baz();
}

And of course select the new function name g so that it can be easily changed. And some pastes would be illegal, e.g. pasting qux(); quux() in place of foo.

So, this "always valid" approach is very nice for refactoring. Its main benefit however is to enable powerful live coding, e.g. devcards.

Git gotcha

Since the rise of github, most teams build software using a pull request workflow. Therefore the local "master" branch is a bit of a gotcha. You can commit to it, but that will only ever cause you grief. That is, you'll forget about those commits, and they'll eventually cause a conflict, and you'll get something mysteriously broken when you just want "master". Fix it with this:

       
            git branch -D master
            git branch master origin/master
       
 
And actually consider not having a local master at all, and always using origin/master instead.

Wednesday, March 03, 2021

Watches making sure you're "up"

Smart watch alarms used for waking up in the morning should have a feature: sound until standup!

That is, you can configure a alarm to continue playing or vibrating until the watch detects that you are standing up (using barometer, accelerometer, gyroscope or whatever). Obviously all other easy ways to turn off the alarm would have to be disabled.

Wednesday, September 23, 2020

IPython replacing shell

IPython is a good UNIX shell replacement, for those who want an interactive shell with a better programming language.

You can use unadorned shell commands, file globbing, and pipes, as well as regular python.

Regular shell: ls *.py | grep -v foo

Regular python: for x in range(3): print(x)

There is special syntax for running shell commands to be easily used by python code...

Shell in python: my_python_var1 = !ls #only for assigning variables

Python in shell: ls $my_python_var2

For anybody pining for python2: print "foo" #works!

Also works for any outermost python function: dir enumerate(list())

But unfortunately doesn't currently work with other code: for x in range(3): print x

For some reason, the maintainers are no longer publishing a profile for this stuff, so you can use the following to get up and running:

pip install ipython

mkdir -p ~/.ipython/profile_default
cat >> ~/.ipython/profile_default/ipython_config.py <<EOF
from IPython.terminal.prompts import Prompts, Token
import os

class MyPrompt(Prompts):
    def cwd(self):
        cwd = os.getcwd()
        if cwd.startswith(os.environ['HOME']):
            cwd = cwd.replace(os.environ['HOME'], '~')
            cwd_list = cwd.split('/')
            for i,v in enumerate(cwd_list):
                if i not in (1,len(cwd_list)-1): #not last and first after ~
                    cwd_list[i] = cwd_list[i][0] #abbreviate
            cwd = '/'.join(cwd_list)
        return cwd

    def in_prompt_tokens(self, cli=None):
        return [
                (Token.Prompt, 'In ['),
                (Token.PromptNum, str(self.shell.execution_count)),
                (Token.Prompt, '] '),
                (Token, self.cwd()),
                (Token.Prompt, ': ')]

c.TerminalInteractiveShell.prompts_class = MyPrompt
c.TerminalInteractiveShell.editing_mode = 'vi'
c.InteractiveShell.autocall = 2 #insert parens around functions as much as possible
c.InteractiveShellApp.exec_lines = ['%rehashx']
EOF

Wednesday, May 27, 2020

Select and Lookup

Select

Computers let you select something you see and do useful things with it. This interaction should be improved and standardized.

Whether it's with a mouse or a finger, when you select text, a little menu should pop up, and its first choices should be: Lookup, Copy, Share.

It's fine if applications want to customize and support additional choices, but those three should always be the first choices. Currently each application has those mixed up differently with other choices, sometimes not visible without an additional action, and sometimes even missing them entirely.

Just like on mobile devices, the menu should pop up immediately on traditional computers without needing a separate action like a right click. That the menu should not interfere with changing the selection.

Lookup

"Lookup" should be customizable, in terms of whether the first screen is a local search, a query to a popular search engine, a dictionary lookup, etc. Other forms of lookup should be linked from that first screen.

For example, selecting the word "customizable" above could show the content of the Wikipedia page for "customizable", with links on the first page there to the dictionary entry, to the Google search, and to the Memex page.

Ideally the first screen would display the page returned by Google's "I'm Feeling Lucky" feature. As the linked page mentions, making this feature more prominent might have revenue implications for Google. As long as we're talking about Google, we should also mention that Google already tries to implement something like what I'm describing. So many Google searches have a dictionary definition and a wikipedia link in their first pages. That would be redundant if operating systems were already presenting that information even more prominently.

Another important possibility for the main lookup page, when the selected text is a hyperlink, would be the actual linked page. For example, the two pages linked above are a lot more useful for understanding how I'm using the terms of the linked text than any of the other sources of information discussed here (at least the time of writing), so should be the primary destination for readers looking them up.

Monday, February 24, 2020

Automated tests and new bugs

Can unit tests find unexpected bugs?

Russ Cox offhandedly mentions that, no, unit tests only "make sure bugs you fix are not reintroduced over time", in his brilliant recent piece on versioning in go.

On the other hand, Hillel Wayne has a nice example using property based testing and contracts. See if you can spot the bug yourself before following that link:
def mode(l):
  max = None
  count = {}
  for x in l:
    if x not in count:
      count[x] = 0
    count[x] += 1
    if not max or count[x] > count[max]:
      max = x
  return max

Hillel analyzes several different approaches to testing. The punch line is that you catch the bug with the following steps:
  • annotate the mode function with its specification:
    
    @ensure("result must occur at least as frequently as any member", 
      lambda a, r: all((a.l.count(r) >= a.l.count(x) for x in a.l)))
    
    
  • also prepend the following:
    
    from hypothesis import given
    from hypothesis.strategies import lists, integers, text
    from dpcontracts import require, ensure
    
    @given(lists(text()))
    def test_mode(l):
        mode(l)
    
    
  • install the prerequisites with: pip3 install hypothesis dpcontracts pytest
  • run pytest on the file that you created with the mode function and its test code
Though this particular specification looks like an alternate implementation, it isn't intended to be. Using one implementation to test another is a kind of "test oracle", but that doesn't feel like an elegant way to find bugs. The test implementation could have its own bugs, and if maintained by the same programmers, it could even have the same bugs as the regular implementation.

In contrast, a specification can be easier to read than any practical implementation. At least with current technology, there are limits on how clear pragmatic code can be. Specification-like code can be too slow. Perhaps the specification could be an oversimplified implementation, and still be useful for testing.

Testing based purely on the specification is not enough however. We need some automated equivalent of "clear box testing".

Thursday, January 16, 2020

Zig

It'd be great for Zig to replace C/C++.

Zig's main advantage over C is robustness. That includes features like:
It aims to keep all of the advantages of C, including:
Its main architectural trick is good support for compile-time code execution. This is even how it implements generic types.

It also includes (or will include) tooling that's as good any platform's: