THE LACK THEREOF

Various musings of Brock Wilcox (@awwaiid)

2020.09.04 Rusty Slides

To keep from getting rusty, and also because all the cool kids are doing it, I'm picking up a bit of Rust.

I remember at one point I was learning a few new programming languages pretty deeply every year or so and then all of a sudden a year went by when I thought "hmm. I didn't learn a new language this year! I'm getting soft!". Upon further reflection I realized that I was learning languages previously at a high rate by working on them 20+ hours a week instead of doing any homework in college. So then I started thinking it's not so much that I'm getting dumber or learning more slowly, but that I am learning completely different things like how to make real-world software or how to make a business effective at all levels and things like that. Ahh tradeoffs.

Annnnyyywaayy. Rust.

I had a lot of fun with OCaml, doing all sorts of things but especially building tiny toy programming languages and parsers. The strong types and type inferencing there are very cool, and I took great joy in trying to NOT put any explicit types in my code and let the language figure it out. Earn it's keep. If I accidentally write something more general-purpose, type-wise, than I intended, well so be it. OCaml would delight in telling me when I contradicted myself, though often the errors felt backwards. Part of this is because it was written by some cool Frenchies, but also because you'd often make a type-mistake first and a correct-type second. Then it'd get to the correct-type and complain because it was expecting the type-mistake, complaining "I expected an Int but here you used an Str" or similar. You think to yourself... well yeah I used a Str because that's what it's supposed to be. After a while you learned the language of its errors and it all gets much faster, but still.

So the cool kids told me that Rust has type inference and much better error messages. So far... it definitely has good error messages, but I'm not too impressed by the type inference. This is probably because I'm doing some real-world work or something.

The learning project that I've made the most progress on is porting Pinpoint, an old gtk-based slide-presentation software that I like, from C to Rust. I call it Pertinent. Well not really porting -- I tried porting and that didn't work out very well. So more like re-implementation. This program takes in a nice text file of slides separated by dashes like this:

HELLO

--

WORLD

--

of

-- [kittens.jpg] [fit]

kittens

and it displays "HELLO" and "WORLD" in big letters on the center of the screen as slide 1 and slide 2 and so on. It has some ways to indicate background image and some other markup, but that's the core of the thing.

First up is to parse the file, and for that I've turned to nom, a parser-combinator. This part of the project has gone pretty well, and has made for nicer code than the hand-written parser in the C code (though not TOTALLY dissimilar). Here's a snippet:

// Slide headers look like "-- [a] [b]\n"
fn header(input: &str) -> nom::IResult<&str, SlideOptions> {
    let option_list = many0(option);

    let (input, options) = terminated(
        preceded(many1(tag("-")), option_list),
        alt((whitespace_or_comment, line_ending)),
    )(input)?;

    let options = options.iter().map(|s| s.to_string()).collect();
    return Ok((input, options));
}

Now for unrelated reasons I did at one point make a Raku parser for this slide format. It is way prettier in Raku ... here is roughly the same thing (where "setting" is like the "option_list" kinda):

token header { ^^ "--" [ \h* '[' <setting> ']' ]* \h* \n }

So it's kinda like writing a regex out longhand in nom. This might be a little prettier in OCaml, but that is mostly because of needing less parenthesis and having more operators overloaded. Probably there is some macro sugar in Rust that could make this shorter.

More interesting to read is the data structure that I'm parsing this thing into.

type SlideOptions = Vec<String>;

#[derive(Debug, PartialEq)]
pub struct Slide {
    options: SlideOptions,
    content: String,
}

#[derive(Debug, PartialEq)]
pub struct SlideDeck {
    global_options: SlideOptions,
    slides: Vec<Slide>,
}

Pretty cool. Have a nicely readable SlideDeck, individual Slide, and a list of options. The derive stuff is neat too, kinda auto-generates functions to pair with the datastructures. Recent OCaml (or libs) have some helpers for this sort of thing too, and they are pretty common in Haskell I think.

You can see the current Pertinent parser on github!

Next up is turning out to be the hard bit, actually displaying the slides. Pinpoint uses Gtk and Cairo with Pango markup. Looks like I have all of those things already all wrapped up in Rust crates (libraries) and I'm making progress. I don't really plan on implementing all the features of Pinpoint, and I MIGHT not ever actually USE this application. But it's fun anyway :)

Add Comment

2020.06.21 Sugary Sunday - Javascript Object Property Shorthand

In today's sugary exploration, let's take a look at a shorthand for object key expansion in Javascript (ECMAScript 2015+, es6+). For an overview of the TON of delicious sugar that was added to Javascript at around es6, check out es6-features.org.

Most of the "scripting" languages (Python, Ruby) have "dictionaries" or "hashes", a datastructure of key-value pairs. In JS these are called "objects", and they do a lot of other things beyond this basic data-storage behavior. In JSON -- JavaScript Object Notation -- you can see the basic literal declaration syntax. Let's build an addressbook, keyed (indexed) by name.

let addressBook = {
  "alice": {
    "street": "123 Elsewhere Lane",
    "zip": "32341"
  },
  "bob": {
    "street": "221 Baker Street",
    "zip": "00234"
  },
  "clarence": {
    "street": "P.O. Box 17",
    "zip": "88877"
  }
};

console.log("Bob's info", addressBook["bob"]);

For our first dash of sugar, we can remove the string notation on keys. They are still strings! We can also access bob using property notation.

let addressBook = {
  alice: {
    street: "123 Elsewhere Lane",
    zip: "32341"
  },
  bob: {
    street: "221 Baker Street",
    zip: "00234"
  },
  clarence: {
    street: "P.O. Box 17",
    zip: "88877"
  }
};

console.log("Bob's info", addressBook.bob);

Pretty fun. But that's not what we're here for. Let's say we have a variable, doug, which contains an address entry. We might do something like this:

let doug = {
  street: "6453 Canyon Canyon",
  zip: "53425"
};

let addressBook = {
  doug: doug
};

It's that last bit where we can add The Sugar. Remember that the key is actually a string, "doug", whereas the value is a reference to the variable doug (which contains an address). What we can do here when these two things are exactly the same is collapse them!

let addressBook = {
  doug
};

This crazy bit of sugar does something pretty strange -- it uses the NAME of the variable as the stringified key! In most circumstances and languages the name of a variable doesn't really matter to the running of the program. This sugary feature sort of breaks the 4th wall, reaching into the source code and lifting the specific variable name into the key (property) of the object.

Real World Example

In VueJS a component can use other components inside the template, but only if they are declared first. Here's a snippet extracted and summarized from the covidcanidoit.com codebase:

<template>
  <div>
    <SearchBar />
    <SearchResults />
  </div>
</template>

<script>
// Importing alone does NOT register the components for use in the template
import SearchBar from "@/components/SearchBar.vue";
import SearchResults from "@/components/SearchResults.vue";

export default {
  components: { SearchBar, SearchResults }
};
</script>

Pretty slick. If you wanted to, you can give the components local-names (or I suppose register a single component with several names, weirdo).

components: { coolSearch: SearchBar, coolSearchResults: SearchResults }

Providing a "list" of components in VueJS is done by providing this object, which gives VueJS not only the component but also the NAME of the component, without VueJS needing to reach inside the component to ask it what its official name is.

BONUS

I like to sprinkle console.log all over the place while exploring code. In addition to static strings, I often throw in the contents of variables. But generally I want to know the NAME of the variable, in addition to the content. So I used to do:

function addAddress(newAddress, otherStuff) {
  console.log("Adding newAddress:", newAddress, "otherStuff:", otherStuff);
  // ...
}

Using today's sugary shorthand, I now usually do this:

function addAddress(newAddress, otherStuff) {
  console.log("Adding", { newAddress, otherStuff });
  // ...
}

This will output something like:

Adding
{
  newAddress: { street: "...", zip: "..." },
  otherStuff: ...
}

You can see that the variable name is handily provided. Here when I had multiple variables I want to log it is even more handy.

DELICIOUS!

Add Comment

2020.04.26 What Even Is Firebase

We're using Firebase as the hosting service for https://covidcanidoit.com and the experience has been kinda weird, coming from a more traditional server-database world.

First, I didn't even realize that Firebase was more than a cloud-JSON-blob! Our initial use of it was only for static asset hosting. This is a VueJS application that so far mostly lives in the browser, so static hosting is perfect. I generally use Github Pages for this purpose, but another team member had already set up Firebase for us.

Once I learned that it was even a thing, all was good. I wrapped the CLI that Firebase provides into yarn land, and now you can do yarn deploy and it'll build up the local assets and push them up. Works quite well! The auth is stored somewhere else on my computer. If a new dev wants to deploy, you add their google account to the Firebase project and presto!

We now have 3 devs on this project, and I'm starting to wonder about staging environments and such. As far as I can tell this isn't a thing in The Cloud, and instead we need to set up a separate project. I guess I'll do that eventually?

Our first user-serviceable data store was the almighty Google Sheets. You might not know this, but you can craft a URL to a public sheet and get down a very lovely CSV. When our app "boots" in the browser, we pull down this csv, parse it, turn it into a nice JSON tree that we stick into Vuex. If you edit the Google Sheet, the next app reload gets the new data. This is a disturbingly effective way to prototype. I grabbed a markdown rendering Vue component and ... now we have a SheetyCMS(tm) (haha).

As we look to expand the type of data we are storing and who can edit the data, I decided that we should switch over to the only thing I thought Firebase did in the first place, a hosted and magically live JSON "database". This is called the "Real Time Database", and for good reason -- all of the client libs know how to watch this thing for changes. In the VueJS one-way world, it goes Firebase -> Vuex -> component -> [edit-event] -> Firebase, and then wraps back around.

I built this admin tool to edit content, and now when you change the description text of something it updates every browser viewing it ... LIVE. Like, even as you type things. We went from having to do a lot of work to make things that live to now maybe having to create a "commit" button so that people can't see you typing and re-typing your text.

Things to discuss further:

  • Firebase Rules
  • Staging environments vs staging data
  • I wonder how much this is going to cost?
  • Firebase data migrations
  • Tracing the round trip of a text update

Add Comment

2020.01.19 Mobile WebAudio Synth

PO33 I got a Pocket Operator PO-33 a while back, and find it super fun -- it is minimalist and usable. It inspires me to play around with beats and tunes ... and it also inspires me to work on my own samplers, sequencers, and synths.

Let's see what we can do with Web Audio these days! Starting with a basic VueJS app, I googled lots of stuff. Somewhat randomly, I wanted a Wicki-Hayden hexagon style keyboard, so had to find some CSS that makes hex-buttons. I'll probably switch to this Hexi-Flexi-Grid at some point, to make dynamically sized hexagons easier, but these work.

sample_mob_screenshot1 Combine that with a simple synth, and now we have a playable thing. I had to make some tweaks to get it to work tolerably on my phone -- fix the layout size, use touch events. Unfortunately it isn't very usable at this point due to a sizeable delay between pushing a button and the sound. I'm going to write a pure-JS snippet to eliminate any overhead from VueJS, and if that is also slow then I'm not sure what I'll do. It's kinda cool that I can use Web Audio ... and I'd sure like to keep using it. We'll see!

Other ideas ... so many. I want to give it access to your microphone so you can record a live sample. The PO-33 then lets you pitch the sample (by speeding it up and slowing it down) and maps it to the keys. I'd like to do that, as well as try a pitch change while keeping the duration. Probably after that is doing some sequencing and/or looping. The PO-33 also has one-shot mode for drums, so might do that. So many things!

Check out the Demo Build and the Github Repo.

Add Comment

More...


META INFORMATION: This is the technical blog and wiki of Brock Wilcox (awwaiid). Entries focus on my current projects, interests, and sometimes life events. If you'd like you can check out the list of All Entries or the RSS Feed. I also have a LiveJournal syndication feed for LJ friends.

Navigation

Blog

https://thelackthereof.org/pics/16px-Feed-icon.svg.png Blog RSS Feed

Tweets


2020-09-27

2020-09-23

2020-08-16

2020-08-15

2020-07-29

Toots (Mastodon)

2018-07-02

2018-06-30

Code

Follow @awwaiid

Wiki Edits

https://thelackthereof.org/pics/16px-Feed-icon.svg.png Wiki RSS Feed

... more changes