Wednesday, November 28, 2018

&& short-circuiting considered harmful... or at least, a little weird

I've never adored the && short-circuit operator, where if you write
A && B
and then A is false, B is never evaluated (or if a function, never called)

To me the short circut always felt like a weird, overloading side effect of "parsing this in left to right order", even though it's just such a cute and concise thing to do its become a standard. But when I think about parallelization, it seems like && reduces parallelization, like you can't run the second part in parallel (at least if there are any side effects) since you might end up not having to do so if the left part succeeds. (Of course if you are in a heavenly pure functional program wonderland, you have nothing to fear!)

My discomfort might come from how it breaks with other forms of boolean logic - like in logic, there's kind of a commutative property -
A AND B
is the same as
B AND A
but that is absolutely not the case in this style of programming.

Thinking about it more, I wish there was something that looked more like the ternary operator, like
shouldDoSomething && doSomething()
is less clear in its intent than
shoudDoSomething ? doSomething() : null;

Still, I'm kicking against the sticks. The hip kids really dig things that are that cute and concise and it's a pretty well-established pattern.

Tuesday, November 27, 2018

quick and dirty javascript stacktrace

As I've bemoaned, debugging declarative javascript code, fixing things when you don't get the results you expect, is tough. Sometimes it's handy to take a peek at the stacktrace and try to guess at what's calling what and why, and to get a stacktrace:

var e = new Error("at some function"); 
console.log(e.stack);

seems to work ok.

Wednesday, November 21, 2018

the tag

This tweet mentions the <meter> element and shows how it can easily be used for a Amazon-like star display - hadn't seen it before. (And like all good things it might not work on IE)

Here's the meter tag with defaults:


<meter high="66" id="fuel" low="33" max="100" min="0" name="fuel" optimum="80" value="60"></meter>


 Interesting that it has concepts of "low" and "high" for like warning values...

Thursday, November 15, 2018

touch tablet paint program ui thoughts

I do daydream about making an expanded version of my self-published comic on coping with mortality, something that captures the best of my thinking not just on death but on the best way to get through life. 

I continue to look for the best app to do that with, experimenting with various paint programs (over the year I've spent so much money on devices with touch sensitive screens, each time thinking "maybe THIS will be the one that lets my doodle skills blossom") Apple iPad's "Notes" program probably can't be it (if only because it doesn't deal with layers) but it really has some interesting UI decisions: coloring with the marker is more or less two-toned, paint once then start painting on the same spot to get a darker shade. And the eraser tool is kind of wild: it's like using the "Undo" button (that might erase a set of lines squiggles you made without lifting the stylus all at once) but instead of removing the last thing you did, it removes what ever you poke with the eraser tool. It's disconcerting at first, but kind of encourages a "well if you mess up you can do that whole thing over" approach.

I'm appropriately humble about my doodle skills, but looking at the sesame street characters I drew from memory during a meeting, I wonder if some of the lack of depth most of my stuff has comes from my preferences for using flat flood fill to color with... it's interesting to let go of a kind of "coloring perfectionism". 

UPDATE: Like I said, the way this app implements "erase" (stroke based and "undo"-like instead of merely removing pixels from a location on the screen) is amazing - I've never seen its like. Some other UI bits are not so good and I had to google for them... 
1. if you have a note that's just "handwriting" / drawing, it's not clear how to name it - while editing it, you pull the main canvas down, and then there's a place to edit it.
2. moving a note to a folder is weird, looking at the list of notes you have to swipe the note name to the side and then it's one of the three options, along with lock and delete.
3. Finally, there's some kind of cool "graph paper" backgrounds (or just lines, which might be useful for handwriting words) which you access via the icon that's usually share... I guess if you start with a pristine note you have that option of "Lines & Grids" but once you start drawing the widget reverts to "send to" mode... kind of weird.

Those first two things are at the heart of why I don't feel confident doing serious work on the iPad... I guess there's this "Files" app that might be coming up to speed, but the conventions for file management are really wonky relative to MacOS or even Windows. (And the third thing is another general problem for iOS that Gruber talks about all the time - there's nothing that matches the discoverability and power of the desktop menu bar, no universal, cross-app way to browse through ALL the commands you have handy, so every app has to work it out for itself.

Tuesday, November 13, 2018

grep / search for multiple terms across files in php

"Hey, what's the matter?" 
"I'm sad because you're going to die." 
"Yeah, that bugs me sometimes too. But not so much as you think... ...When you get as old as I am, you start to realize that you've told most of the good stuff you know to other people anyway." 
--Richard Feynman and Danny Hillis. 

After blogging for almost two decades my site kirk.is is an increasingly important supplement to my memory - it's great to have an archive for the full text of half-remembered quotes and excerpts.

A long while back I wrote a simple "grep" in Perl to find stuff - it could only find one exact string match across all the files, but often that was enough, and unlike Google, results were in chronological order, which was often useful. And its sense of usefulness (albeit mostly just to me) has increased over the years, so I decided to make it a little smarter and able to search for multiple terms and sentence snippets.

The logic turned out to be slightly trickier than I bargained for - the logic I finally realized I wanted was, for each file, go through each query term. Then go through the lines in the file. If for any query term none of the lines match, bail on this file, otherwise remember which lines matched. If we get to the end of all the terms, every term has at least one match - so for all lines that matched any term, sort 'em (having already made sure they were dedup'd), escape the HTML, bold the matching terms, and then return the lines as an array.

Here's the code for that:
function getLinesMatchingQueryStringInFile($file,$query) {
    $linesInFile = file($file);
    #break up query, using CSV (split on spaces after collapsing whitespace)
    $query = preg_replace('/\s+/',' ',$query);
    $queryterms = str_getcsv(trim($query), ' ');

    $matchingLineNumbers = array();
    
    foreach ($queryterms as $queryterm) {
        $lineNumbersMatchingThisTerm = array();
        foreach ($linesInFile as $linenum => $line) {            
            if (preg_match("/$queryterm/i",$line)) {
                $lineNumbersMatchingThisTerm[] = $linenum;
            }
        }
        #if any query term doesn't match we bail!
        if (count($lineNumbersMatchingThisTerm) == 0) { 
            return array();    
        } else {
             foreach($lineNumbersMatchingThisTerm as $linenum) {
                if (! in_array($linenum,$matchingLineNumbers)) {   
                    $matchingLineNumbers[] = $linenum;
                }
             }
        }
    }
    asort($matchingLineNumbers);
    $res = array();
    foreach ($matchingLineNumbers as $linenum) {
        $matchline = htmlspecialchars($linesInFile[$linenum]);
        foreach ($queryterms as $queryterm) {
            $matchline = preg_replace("/($queryterm)/i","<b>$1</b>",$matchline);
        }
        $res[] = $matchline;
    }
    return $res;
}
A slightly clever bit (i.e. slightly deeper in the StackedOverflow) was trimming the query expression, and then using str_getcsv(), which does a good job of breaking something like 
find "something good"
into 
find
and 
something good
which is what I'd want to search on.

Friday, November 9, 2018

grumble grumble

Trying to follow a piece of data get mangled in a relatively declarative-style code base is a pain.

(FOLLOWUP: More specifically, the code in question was implicitly manipulating a lot of data for a financial estimation calculator - there was the display format (and my task was internationalizing that), the internal format used to do the math, and then the format for editing (i.e. you might want to display a money value with a currency symbol and a thousand place separator, but you remove it when the user is editing that value) It was frustrating when the "to" and "from" functions used in the reducer were each called like 2 or 3 times for every field. The correct solution turned out to be doing a "typeof" in the "toNumber()" routine, and if the argument was a string call the locale-aware number parsing function, and if it was a number just returning the number.  Now I'm not the biggest fanboy either of declarative styles (I still think event-driven code be a bit more sane- or at least easy to figure out what's messing up when things go awry) or of strict typing in javascript, but this experience made me thing the two might go well together.)

Thursday, November 1, 2018

git-r-done ux

Once upon a time, the comments section on my regular blog kirk.is made it a social place, with regular commentators, only about half of whom I knew IRL.

Social media has moved on, alas. And adding injury to insult, my homebrew comment system got invaded by dirty rotten link spammers. These guys are the biggest jerks in the world. I have about 4,000 days with comments on them (!) but I'd say at least 3/4 of those will turn out to be nothing but comment spam.

Even after eliminating the ability for people (and bots) to post links, they kept on coming, and I decided not to give up, rather than invest more deeply into a fix.

But, being nostalgic and wanting to shake my fist against the onslaught, I'm going back and eliminating the spam while preserving the conversations. So I built a tool to efficiently drive through each entry and with a click, determine where the good comments started and the bots began, showing the actual entry to the right.

If there's no good comments, I click "KILL ALL" and the thing is removed. Other wise, I click a link that says "truncate the comments section here". (In the screenshot you get the idea of my crude but functional system: the first line is a count of how many comments in the file (for efficient "See X Comments" labeling when building the blog entry page), then each comment is followed by special lines in brackets "[[]]", with key/values for their name, and the time they posted.

To improve the UI, I make use of the fact that most of the 'bot entries seem to be posted 3000 or so days (i.e. 8 years) after the original entry date - so anything over 1000 days old gets labeled in gray, with a red highlight on the day differential. Anything older than 10 days still gets a light gray treatment.

I could probably just write a script to discard any comment coming 1000 days or more after, but besides the fact it would throw out some good stuff and keep some bad stuff, I kind of like the nostalgia of the old conversations. Right now I'm up to 2003, as we move into Iraq.

UPDATE: write after I wrote this I realized I could it one better - 98% of the time I just click on either "KILL ALL" (where there's nothing) or at the transition point from "good" to "bad" comments. So I really git-r-done'd a javascript thing (long story short I wanted to keep things one pass in my PHP) to add a css style to the id of the "cut here" link and turn it bright yellow, and then another button that says "DO THE YELLOW" if I accept the verdict - that button is plunked at the top of the page so I don't have to find it with the mouse.

So, it's cruft-y as hell, but very efficient, and transparent: make it easy for the user (me) but "show your work" at all time, along with being clear what action you are going to do...

Dang, I wish "Git-R-Done" wasn't so Larry-the-Cable-Guy-ish!

minimalist css callout/tooltips

http://cssdeck.com/labs/bv45bh6p has a pretty good, minimalist CSS callout. I got rid of the fixed height and shadows and just use the one direction I needed:

div.callout {
  width: 200px;
  display: none;
}

div.callout {
  background-color: #444;
  background-image: -moz-linear-gradient(top, #444, #444);
  position: absolute;
  top: 0.5em;
  left: 8em;
  color: #ccc;
  padding: 10px;
  border-radius: 3px;
  margin: 25px;
  min-height: 50px;
  border: 1px solid #333;
  z-index: 100;
}

.callout::before {
  content: "";
  width: 0px;
  height: 0px;
  border: 0.8em solid transparent;
  position: absolute;
}

.callout.bottom::before {
  left: 45%;
  top: -20px;
  border-bottom: 10px solid #444;
}

After whipping up a version of that (and having to have the onmouseover/onmouseout for both the triggering text and the callout itself it was pointed out to me that the bootstrap we use has a decent library for what it calls tooltips - it can be "onclick" ( the code is like 
$('[data-toggle="tooltip"]').tooltip({trigger:'click'});
) , it can be made to not escape  HTML,
data-html="true"
and you can style it with CSS - selector is
 .tooltip .tooltip-inner
And also it's pretty good that it fallsback gracefully using the html5 default "title" tooltip behavior