http://www.twiddla.com/ is a kind of awesome shared whiteboard... I remember trying to build something like this for my own use in 2001 or so, with Java applets and Perl CGI. Stuff like this can be so crucial for remote teams!
PS another useful site for scrum teams, especially with distributed teams, is https://www.pointingpoker.com/. It eschews fake cards for a simpler interface, but it's pretty effective.
Wednesday, April 30, 2014
the great works of software
Conceptualizing the Canon of Software Great Works. Not sure if I agree with all of the choices, but I like the idea.
Thursday, April 24, 2014
coding is fun: a simple random text table generator
Today I wanted to make some randomly generated tables, you can set the rows and cols and how words per entry... wrapped up here in my old boilerplate html for your copy and paste convenience...
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>So Random</title>
<link rel="stylesheet" type="text/css" href="style.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
function makeRandomTable(sel, rows, cols, wordsPerCol){
//thanks http://www.lipsum.com/
var rawText =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec fringilla purus metus, sed sagittis erat elementum nec. In fringilla, mauris eu congue blandit, massa ante placerat nisl, at semper diam nibh non lorem. Aenean non nibh ligula. Vivamus luctus ligula ipsum, a euismod sapien varius at. Nam et augue nulla. Cras velit nunc, congue in iaculis vel, consectetur ut massa. Nulla condimentum sit amet nunc sed mollis. "+
"Donec venenatis sollicitudin eleifend. Pellentesque vitae adipiscing nulla, eu consequat sapien. Integer auctor metus dolor, sed venenatis lectus congue non. Curabitur vestibulum sem sed quam tincidunt, sagittis hendrerit orci ultricies. Integer at eleifend mi. Nullam magna lorem, ornare vel varius ut, faucibus at neque. Ut commodo purus ac turpis vulputate condimentum sed non metus. Donec sollicitudin tincidunt eros, non blandit turpis ultrices vel. Etiam feugiat lacus magna, nec pharetra risus congue eget. Fusce vitae augue tellus. Aliquam ut ipsum vitae augue adipiscing ultrices. Nullam id dolor ac nibh sollicitudin fringilla. Fusce vitae libero iaculis, tincidunt ligula nec, varius erat. "+
"Aliquam ornare, tellus sed facilisis tincidunt, dolor leo blandit lorem, rutrum porttitor nulla lorem ut mauris. Sed eu consectetur urna, in vehicula dolor. Vivamus neque nibh, consectetur eget porta vitae, consectetur ut lacus. Aliquam erat volutpat. Quisque in nibh porttitor odio aliquet euismod id ac est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In eget elit tristique, semper lorem nec, porttitor nisi. Cras bibendum tincidunt vehicula. Etiam fringilla nec dolor sed sagittis. "+
"Nullam cursus, turpis sed accumsan condimentum, velit nisi blandit nulla, eget adipiscing quam sapien eget arcu. Sed quis velit leo. Curabitur quis pulvinar lectus. Vestibulum ut nisi ultricies, porta orci at, facilisis ligula. Morbi sed suscipit turpis, vitae tincidunt lorem. Nunc scelerisque nibh gravida mollis consectetur. Cras lacus mi, fermentum vel gravida quis, mollis eu ligula. Fusce porttitor sem quis molestie dignissim. Maecenas sollicitudin commodo libero ut sagittis. Morbi justo dui, commodo vel ipsum vitae, tristique rhoncus diam. Aliquam dapibus iaculis ultrices. Donec tincidunt placerat pellentesque. Vivamus et libero tempor, convallis lorem nec, cursus est. Nam a bibendum eros. Donec et rutrum velit, sit amet dapibus sapien. Vivamus consectetur, diam sit amet molestie sodales, eros elit bibendum est, sed sodales eros eros at ipsum. "+
"Mauris tincidunt faucibus nibh. Vivamus venenatis urna ac est auctor, eget dignissim orci fermentum. Donec eget nisl condimentum, ultrices est quis, sodales justo. Sed aliquam neque nulla, rhoncus lobortis sapien consequat eu. Nullam pellentesque magna turpis, vel consequat massa porttitor ac. Integer sit amet tortor eget ligula ornare sodales. Morbi sollicitudin mi vitae imperdiet volutpat. Nam vel tincidunt est, quis porttitor tellus. Sed aliquet, ante et lobortis laoreet, arcu leo ullamcorper ipsum, sit amet faucibus dui metus ut felis. "
rawText = rawText.replace(/[\.\,\!,\?,\"]/g, '');
var words = rawText.split(" ");
var buf ="<table border='1'>";
for(var r = 0; r < rows; r++){
buf += "<tr>";
for(var c = 0; c < cols; c++){
buf += "<td>";
for(var i = 0; i < wordsPerCol; i++){
buf += words[Math.floor(Math.random()*words.length)] + " ";
}
buf += "</td>";
}
buf += "</tr>";
}
buf += "</table>";
$(sel).append(buf);
}
$(document).ready(function() {
makeRandomTable("#soRandom",5,5,30);
});
</script>
</head>
<body>
<div id="soRandom">
</div>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>So Random</title>
<link rel="stylesheet" type="text/css" href="style.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
function makeRandomTable(sel, rows, cols, wordsPerCol){
//thanks http://www.lipsum.com/
var rawText =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec fringilla purus metus, sed sagittis erat elementum nec. In fringilla, mauris eu congue blandit, massa ante placerat nisl, at semper diam nibh non lorem. Aenean non nibh ligula. Vivamus luctus ligula ipsum, a euismod sapien varius at. Nam et augue nulla. Cras velit nunc, congue in iaculis vel, consectetur ut massa. Nulla condimentum sit amet nunc sed mollis. "+
"Donec venenatis sollicitudin eleifend. Pellentesque vitae adipiscing nulla, eu consequat sapien. Integer auctor metus dolor, sed venenatis lectus congue non. Curabitur vestibulum sem sed quam tincidunt, sagittis hendrerit orci ultricies. Integer at eleifend mi. Nullam magna lorem, ornare vel varius ut, faucibus at neque. Ut commodo purus ac turpis vulputate condimentum sed non metus. Donec sollicitudin tincidunt eros, non blandit turpis ultrices vel. Etiam feugiat lacus magna, nec pharetra risus congue eget. Fusce vitae augue tellus. Aliquam ut ipsum vitae augue adipiscing ultrices. Nullam id dolor ac nibh sollicitudin fringilla. Fusce vitae libero iaculis, tincidunt ligula nec, varius erat. "+
"Aliquam ornare, tellus sed facilisis tincidunt, dolor leo blandit lorem, rutrum porttitor nulla lorem ut mauris. Sed eu consectetur urna, in vehicula dolor. Vivamus neque nibh, consectetur eget porta vitae, consectetur ut lacus. Aliquam erat volutpat. Quisque in nibh porttitor odio aliquet euismod id ac est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In eget elit tristique, semper lorem nec, porttitor nisi. Cras bibendum tincidunt vehicula. Etiam fringilla nec dolor sed sagittis. "+
"Nullam cursus, turpis sed accumsan condimentum, velit nisi blandit nulla, eget adipiscing quam sapien eget arcu. Sed quis velit leo. Curabitur quis pulvinar lectus. Vestibulum ut nisi ultricies, porta orci at, facilisis ligula. Morbi sed suscipit turpis, vitae tincidunt lorem. Nunc scelerisque nibh gravida mollis consectetur. Cras lacus mi, fermentum vel gravida quis, mollis eu ligula. Fusce porttitor sem quis molestie dignissim. Maecenas sollicitudin commodo libero ut sagittis. Morbi justo dui, commodo vel ipsum vitae, tristique rhoncus diam. Aliquam dapibus iaculis ultrices. Donec tincidunt placerat pellentesque. Vivamus et libero tempor, convallis lorem nec, cursus est. Nam a bibendum eros. Donec et rutrum velit, sit amet dapibus sapien. Vivamus consectetur, diam sit amet molestie sodales, eros elit bibendum est, sed sodales eros eros at ipsum. "+
"Mauris tincidunt faucibus nibh. Vivamus venenatis urna ac est auctor, eget dignissim orci fermentum. Donec eget nisl condimentum, ultrices est quis, sodales justo. Sed aliquam neque nulla, rhoncus lobortis sapien consequat eu. Nullam pellentesque magna turpis, vel consequat massa porttitor ac. Integer sit amet tortor eget ligula ornare sodales. Morbi sollicitudin mi vitae imperdiet volutpat. Nam vel tincidunt est, quis porttitor tellus. Sed aliquet, ante et lobortis laoreet, arcu leo ullamcorper ipsum, sit amet faucibus dui metus ut felis. "
rawText = rawText.replace(/[\.\,\!,\?,\"]/g, '');
var words = rawText.split(" ");
var buf ="<table border='1'>";
for(var r = 0; r < rows; r++){
buf += "<tr>";
for(var c = 0; c < cols; c++){
buf += "<td>";
for(var i = 0; i < wordsPerCol; i++){
buf += words[Math.floor(Math.random()*words.length)] + " ";
}
buf += "</td>";
}
buf += "</tr>";
}
buf += "</table>";
$(sel).append(buf);
}
$(document).ready(function() {
makeRandomTable("#soRandom",5,5,30);
});
</script>
</head>
<body>
<div id="soRandom">
</div>
</body>
</html>
Monday, April 21, 2014
­ the anti-
Many of you probably know about the "non-breaking space" -- I think it gets used more often for padding than anything else. Lesser known but possibly more useful is ­ the "soft-hyphen", like when you have a compound word or what not, maybe something separated by - or /, and you want to say "it's ok to insert a linebreak here, but otherwise keep these parts next to each other." Word is most browsers now support this. (For a while it was in competition with the oddball tag <wbr>, but it seems to have won in this HTML5-ish world.)
2018 UPDATE: I've started using ​ which is ugly as hell but works in IE, for when you want to say "this can break but please don't but a hyphen in there" (I had some data that had no spaces after commas, so I replaced "," with ",​")
2018 UPDATE: I've started using ​ which is ugly as hell but works in IE, for when you want to say "this can break but please don't but a hyphen in there" (I had some data that had no spaces after commas, so I replaced "," with ",​")
Saturday, April 19, 2014
cheap infinite scroll in jquery
"Infinite scroll" is what you see on twitter and tumblr, where scrolling to the bottom of the current set of entries triggers the load of the next set.
I was looking for something similar for a work project. The jscroll project seemed to be it at first, but on closer inspection is about loading the next set of static content, and I was looking for a "simpler" trigger of a callback to load the next set of data via jQuery.
I ended up rolling my own, with a little help from the stack overflow entry Check if element is visible after scrolling. (Note for this and the next code bit: for this project, my company uses the more verbose "jQuery()" rather than "$()" because of namespace conflicts. Also, I am encouraging the use of "jqo" as a Hungarian Notation prefix to indicate a jQuery Object.)
So when I set up the table, it's something like:
jqoTable = jQuery("<table class='mixgrowGrid'></table>");
jqoSpot.append(jqoTable);
jqoScrollTrigger = jQuery("<div class='mixgrowGrid_scrollTrigger'></div>");
jqoSpot.append(jqoScrollTrigger);
jQuery(window).scroll(checkForInfiniteScroll).resize(checkForInfiniteScroll);
I made a variable noMoreToLoad that is set to true when the query has returned all available records (in practice, this is set when the "next batch" query returns an empty array). So at the end of of my data receiving function, I write
if(!noMoreToLoad) checkForInfiniteScroll();
And then the supporting code is:
var checkForInfiniteScroll = function(){
if(isTriggerScrolledIntoView()){
if(!noMoreToLoad){
loadNextSet();
}
}
}
var isTriggerScrolledIntoView = function(){
var docViewTop = jQuery(window).scrollTop();
var docViewBottom = docViewTop + jQuery(window).height();
var elemTop = jQuery(jqoScrollTrigger).offset().top;
var elemBottom = elemTop + jQuery(jqoScrollTrigger).height();
return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
&& (elemBottom <= docViewBottom) && (elemTop >= docViewTop) );
}
So why am I doing all this? "mixgrowGrid" is meant to be a substitute for jQuery datatables. Datatables is great if you want to take an existing html table on a page and bless it with client side functions like sorting and filtering. It handles infinite scrolling too, but poorly in my opinion: you pretty much have to have a fixed height display window (datatables seems more geared at pages where a table is just one element in the page flow, rather than being the center of attention, like it is on my company's dashboards) For the most part, I am less happy with widgets that carry their own scrollbar. My other beef with Datatables (besides its general complexity, which is par for the course for a genral purpose tool that tries to solve a LOT of different people's problems...) is that it is not great for mixed data; like if you have some parent/children rows, and especially if you would like some of the children rows to use "colspan". My mixgrowGrid says what it features in the name: mix-ed data (each type can have different column definitions) and designed to grow on the page.
There's a lot to do before this is ready for prime time: I will be making a footer that uses position:fixed to glue it to the bottom of the page, independent of the scrolling, and will probably have to do something similar for the headers, as well as install a "loading" notification. But one of the nice thing about rolling your own (the classic "Make vs Buy" conundrum) is that you can have a lot of control over that kind of detail, without having to learn and/or hack on someone else's big code base.
FOLLOWUP:
The header turned out to be pretty cool to do; I had to make a separate table above the main table (which means I had to make a separate function to make sure the widths were consistent with the main table, I didn't get that for free) and I had it be position:relative - the nice thing about that setting vs float is that it still preserves its space in the page flow, so things are simpler when it's "normally" visible, I don't have to think about making space for it. Then, on window scroll and resize (i.e. the same events as for infinite scrolling) I had this function, for the wrapper element (also position:relative, so that any children will be repositioned relative to it) and header table:
var adjustHeaderLocation = function(){
var eTop = jqoTableWrapper.offset().top;
var scrollTop = - jQuery(window).scrollTop();
var newOffset = -(scrollTop + eTop);
if(newOffset < 0) newOffset = 0;
jqoHeaderTable.css("top",newOffset+"px");
}
FOLLOWUP TO THE FOLLOWUP:
I re-ran into a problem we had already solved, where it can be surprisingly tricky to make two tables the exact same width: border and padding and what not makes things weirder than you might expect. Some people suggest making a div version of the headers instead, but the short of it is you want to read the .outerWidth, not the .width()of the "td"s in question, and then apply that as the width, max-width, and min-width of the respective matching "th"s. Stackoverflow had some good discussion, including this bit of democode -- it looks like it could be a plugin, almost. I wasn't sure if the complex table I was building (with multirow headers) would use it as a drop-in, but I'll keep it in mind for the future.
I was looking for something similar for a work project. The jscroll project seemed to be it at first, but on closer inspection is about loading the next set of static content, and I was looking for a "simpler" trigger of a callback to load the next set of data via jQuery.
I ended up rolling my own, with a little help from the stack overflow entry Check if element is visible after scrolling. (Note for this and the next code bit: for this project, my company uses the more verbose "jQuery()" rather than "$()" because of namespace conflicts. Also, I am encouraging the use of "jqo" as a Hungarian Notation prefix to indicate a jQuery Object.)
So when I set up the table, it's something like:
jqoTable = jQuery("<table class='mixgrowGrid'></table>");
jqoSpot.append(jqoTable);
jqoScrollTrigger = jQuery("<div class='mixgrowGrid_scrollTrigger'></div>");
jqoSpot.append(jqoScrollTrigger);
jQuery(window).scroll(checkForInfiniteScroll).resize(checkForInfiniteScroll);
I made a variable noMoreToLoad that is set to true when the query has returned all available records (in practice, this is set when the "next batch" query returns an empty array). So at the end of of my data receiving function, I write
if(!noMoreToLoad) checkForInfiniteScroll();
And then the supporting code is:
var checkForInfiniteScroll = function(){
if(isTriggerScrolledIntoView()){
if(!noMoreToLoad){
loadNextSet();
}
}
}
var isTriggerScrolledIntoView = function(){
var docViewTop = jQuery(window).scrollTop();
var docViewBottom = docViewTop + jQuery(window).height();
var elemTop = jQuery(jqoScrollTrigger).offset().top;
var elemBottom = elemTop + jQuery(jqoScrollTrigger).height();
return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
&& (elemBottom <= docViewBottom) && (elemTop >= docViewTop) );
}
There's a lot to do before this is ready for prime time: I will be making a footer that uses position:fixed to glue it to the bottom of the page, independent of the scrolling, and will probably have to do something similar for the headers, as well as install a "loading" notification. But one of the nice thing about rolling your own (the classic "Make vs Buy" conundrum) is that you can have a lot of control over that kind of detail, without having to learn and/or hack on someone else's big code base.
FOLLOWUP:
The header turned out to be pretty cool to do; I had to make a separate table above the main table (which means I had to make a separate function to make sure the widths were consistent with the main table, I didn't get that for free) and I had it be position:relative - the nice thing about that setting vs float is that it still preserves its space in the page flow, so things are simpler when it's "normally" visible, I don't have to think about making space for it. Then, on window scroll and resize (i.e. the same events as for infinite scrolling) I had this function, for the wrapper element (also position:relative, so that any children will be repositioned relative to it) and header table:
var adjustHeaderLocation = function(){
var eTop = jqoTableWrapper.offset().top;
var scrollTop = - jQuery(window).scrollTop();
var newOffset = -(scrollTop + eTop);
if(newOffset < 0) newOffset = 0;
jqoHeaderTable.css("top",newOffset+"px");
}
FOLLOWUP TO THE FOLLOWUP:
I re-ran into a problem we had already solved, where it can be surprisingly tricky to make two tables the exact same width: border and padding and what not makes things weirder than you might expect. Some people suggest making a div version of the headers instead, but the short of it is you want to read the .outerWidth, not the .width()of the "td"s in question, and then apply that as the width, max-width, and min-width of the respective matching "th"s. Stackoverflow had some good discussion, including this bit of democode -- it looks like it could be a plugin, almost. I wasn't sure if the complex table I was building (with multirow headers) would use it as a drop-in, but I'll keep it in mind for the future.
Friday, April 18, 2014
more improvements to object like encapsulation in js
An improvement to what I wrote about here-- I was still thinking in terms of a singleton pattern, but I think it can be easily extended to be reusable:
With a setup
function someObject(init){
var somePrivateVariable = init;
var somePrivateFunction = function(){
alert('shh');
}
this.somePublicVariable = null;
this.somePublicFunction = function(){
//still has access because of function level scoping
alert(somePrivateVariable);
somePrivateFunction();
}
}
You can make multiple instances
var m = new someObject(1);
var n = new someObject(2);
m.somePublicFunction();
n.somePublicFunction();
With a setup
function someObject(init){
var somePrivateVariable = init;
var somePrivateFunction = function(){
alert('shh');
}
this.somePublicVariable = null;
this.somePublicFunction = function(){
//still has access because of function level scoping
alert(somePrivateVariable);
somePrivateFunction();
}
}
You can make multiple instances
var m = new someObject(1);
var n = new someObject(2);
m.somePublicFunction();
n.somePublicFunction();
Wednesday, April 16, 2014
simple pixel programs for OSX
There were two things I started missing when I switched from Windows to OSX: one is the program IrfanView for folder based image viewing and basic manipulation (resizing, cropping, rotation). Those functions are handled pretty well though with a combination of Finder's builtin viewer (hit space when a file is selected, then use arrow keys to navigate through the folder itself) and OSX Preview.
The other was an editor more advanced pixel work, without running smack into the cost and/or usability hurdles of Photoshop or GIMP. On Windows I enjoyed Paint.NET (after a decade of getting by with an ancient copy of Paint Shop Pro). It was tougher on OSX -- Pixelmator was somewhat ok, but did a lot of things oddly, and fell into the mistake of assuming its native format was preferable for everything. (Sometimes you just want to open a PNG, edit some pixels, and save it again, but that wasn't trivial in Pixelmator.)
Now, however, I think I might have found my new favorite: "Acorn". I didn't experiment with it before, but it (temporarily?) knocked down its price from $50 to $15, and it seems well worth it.
So: "Acorn" for OSX. Recommended, and well-priced. (For the time being at least.)
The other was an editor more advanced pixel work, without running smack into the cost and/or usability hurdles of Photoshop or GIMP. On Windows I enjoyed Paint.NET (after a decade of getting by with an ancient copy of Paint Shop Pro). It was tougher on OSX -- Pixelmator was somewhat ok, but did a lot of things oddly, and fell into the mistake of assuming its native format was preferable for everything. (Sometimes you just want to open a PNG, edit some pixels, and save it again, but that wasn't trivial in Pixelmator.)
Now, however, I think I might have found my new favorite: "Acorn". I didn't experiment with it before, but it (temporarily?) knocked down its price from $50 to $15, and it seems well worth it.
So: "Acorn" for OSX. Recommended, and well-priced. (For the time being at least.)
Monday, April 14, 2014
ye olde joins
Just a summary of some stuff I used to know pretty well, but it has been a while: (and also a little exercise in differences in how people visualize stuff...)
JOINS IN SQL LAND
Roughly, there are for kinds. Lets say you have a table A and a table B, joined via some ID.
(this reminds me of how odd it kind of is, how much of my SQL background was Oracle's flavor. Its old PL-SQL used a kind of wonky "(+)" symbol to indicate an outer (or optional, as they thought of it) join.)
JOINS IN SQL LAND
Roughly, there are for kinds. Lets say you have a table A and a table B, joined via some ID.
- INNER JOIN - gets you rows that appear in both A and B
- LEFT JOIN - would return rows that appeared in A, whether or not they showed up in B
- RIGHT JOIN - same as left, but swapping A + B
- OUTER JOIN - would return all rows from A and B, whether or not they had entries in other table
To keep track of that, I found it useful to draw it out, the chart on the left. The syntax is a little wonky, of course. Scott, who was going over this stuff with me, thought Venn diagrams made more sense- his stickie is there on the right. I find it harder to wrap my brain around it, but I'm including it here as a nice display of different learning/visualization styles.
(this reminds me of how odd it kind of is, how much of my SQL background was Oracle's flavor. Its old PL-SQL used a kind of wonky "(+)" symbol to indicate an outer (or optional, as they thought of it) join.)
Friday, April 11, 2014
a more joyous phone ui
I admit I kind of want a phone that looks like
with a UI that moves like
I was pointed to this interesting work from the site Minimally Minimal. I'm not sure if such happy bouncing and what not is within minimalism principles or not, but I dig it.
Wednesday, April 9, 2014
TRIPLE EQUALS CONSIDERED OVERRATED === = :-(
So, a recent popular idea in Javascript land is that it's always better to compare values using "triple equals", ===, which won't do type conversion, over good ol' "double equals", ==.
So quick example:
var foo = 123; //foo is an integer
foo == "123"; //true, foo "double equals" the string 123
foo === "123"; //false, because they are different types
I find myself disagreeing strongly with the preference for "===", for 3 iffy reasons, and I think one good one.
1. What I'm used to
I can sound like a cranky old hack sometimes, and remembering to type === and !== seems like a pain, and just kind of looks ugly. Moreover, I cut my teeth on loosely-typed "Duck languages" like Perl. So if I read in a string and wanted to compare it to a numeric value such as 123, I wouldn't want to fail just because they were "different things", or came from a different source.
2. How I think about Java
Java's String primitive/object has a parallel situation that is deceptively similar, with .equals() vs ==, and I think I draw a false analogy. To whit: for Java objects, == is only true when you have two references to the same object in memory, while equals() should be true if two different objects are equivalent. So for instance,
String foo = "bar";
foo.equals("bar"); //of course true!
foo == "bar"; //don't count on it, buddy
So I learned to be careful, and use the "less strict" equals() for most stuff I was interested in. This makes me nervous about ===. Like, I'm almost skeptical that Javascript
"foobar" === "foo" + "bar"; //true (amazingly ;-)
Yeah it works, but because I think of it in terms of what it looks like in Java, I don't want to trust it.
FOLLOWUP in 2017: I just noticed that
foo == new String('foo')
but not
foo === new String('foo')
which is exactly parallel to the Java case, but arguably much less significant because String as an object is rather rare in Javascript land.
3. === is used to show off
It feels like an elitist shibboleth to me. (And yes, I love that knowing what "shibboleth" means is in itself a shibboleth.) To be fair, these people probably point to funkiness in the truthiness tables, and just odd asymmetries there, but in my heart of heart I think it's just a way for the with-it hipster coders to show they are with-it hipsters.
4. if(arg == undefined) //true for argument not passed, and null, but not for zero
Finally, this might be my most serious argument.
Say I have a function funk(arg), and returns its argument or a default of -1, if no argument is passed. (And of course it should work with 0 as a value) So:
function funk(arg){
if(arg == undefined) arg = -1;
return arg;
}
That works pretty well... (if I used the conditional if(!arg), that would accidentally catch 0 and return -1, and so in general I shun simple boolean expressions, preferring to check for undefined.)
So what I like about == is that is that funk(null) would be caught, and replaced with -1, but if I used === funk(null)would return null, because undefined === null is false. (Of course, "null" and undefined are kind of odd beasts in Javascript land; some people think the "undefined" value is just an accident of history anyway.) (PS don't do stuff like using undefined as a real value, like calling funk(undefined) - that's just R,O,N,G, wrong.)
So basically, I prefer the behavior of double equals, and prefer its made up "this would fail!" arguments to the ones made in favor of trouble equals.
(Quick tip: if you don't know it already, the "console" you can get to in Chrome inspector or Firefox Firebug is great for doing this kind of little functional definition, tooling around with making and calling functions, just ignoring the page it's on. And of course it's an invaluable tool for poking around your Javascript in general.)
FOLLOWUP: A friend of mine points out "NaN" kind of messes up my simple != undefined filter, since it is defined, but probably not what we were expecting to work with what I described.
POSTSCRIPT FROM THE END OF 2017
Listening to a podcast with scientists pontificating, I realize I treat triple-equals usage similarly to how I treat the correct usage of "data are plural", and for similar reasons: a begrudging respect for people using a shibboleth correctly, set against a personal bias for the looser usage; with that split masking a philosophical difference in worldview.
My worldview is: people and things are more important in how they interact than in their internal makeup. Take "Data". It's technically a plural world from Latin, with "Datum" being the singular. But a "Datum" is useless to the point of meaninglessness on its own - ONLY through multiplicity does a datum go from being a one-off anecdote to a statistically meaningful bit of information. Casual use would treat "Data" as a kind of singular group noun - "what this data suggests" vs "what these data suggest", and since that group-making is the only useful way people interact with data, "this data", the street usage, makes much more sense.
(I'm not a big fan of the old tradition of presuming Latin rules need apply to English anyway- like how you should never split an infinitive ("to boldly go") since such a construction is impossible in Latin where the infinitive is a single word.)
With triple equals, I return to the basic idea that it means "reject the comparison if the things being compared aren't exactly the same type" - an internal analysis. Double equals says if two things have the same value when they interact, that's fine! We don't care about the history or composition of the things, just how they'll interact now. (A long history with Perl and other duck-type languages helps inform my view, I think.)
To wax philosophical, I've realized this difference in worldview- whether what's important is the history and internals of a thing (since that will be the surest guide to predicting long-term behavior, and/or give you a special revelation of how things "should be") or whether we should attend to how things are capable of interacting with the outside world - is profound and tough to bridge.
I'd recommend the book "Surfaces and Essences: Analogy as the Fuel and Fire of Thinking"... and then when I hear some punk like Scott Adams say that because analogies are always imperfect they can never be persuasive, and that it's where "reason is embarrassed to show its face"... balderdash. Finding parallels in how different systems are interacting makes up one of the most critical tools in understanding the world, no matter that there will ALWAYS be some difference in intrinsic makeup. (Of course, saying there are only surfaces or only essences is a false dichotomy; some analogies run deep, that two systems are interacting in parallel ways because of parallel functioning in their guts. And some analogies are just shallow and rhetorical and are of less value.)
PS to the PS: Regarding point 4, my college buddy (and freshman roommate) Rob points out some cases where keeping the null vs undefined is useful - the differenc between a missing key and a key that points to a null valye - like an API where the object.user will not be set at all if a call hasn't been null, or if the call completed but there was no user or whatever, it would be null....
So quick example:
var foo = 123; //foo is an integer
foo == "123"; //true, foo "double equals" the string 123
foo === "123"; //false, because they are different types
I find myself disagreeing strongly with the preference for "===", for 3 iffy reasons, and I think one good one.
1. What I'm used to
I can sound like a cranky old hack sometimes, and remembering to type === and !== seems like a pain, and just kind of looks ugly. Moreover, I cut my teeth on loosely-typed "Duck languages" like Perl. So if I read in a string and wanted to compare it to a numeric value such as 123, I wouldn't want to fail just because they were "different things", or came from a different source.
2. How I think about Java
Java's String primitive/object has a parallel situation that is deceptively similar, with .equals() vs ==, and I think I draw a false analogy. To whit: for Java objects, == is only true when you have two references to the same object in memory, while equals() should be true if two different objects are equivalent. So for instance,
String foo = "bar";
foo.equals("bar"); //of course true!
foo == "bar"; //don't count on it, buddy
So I learned to be careful, and use the "less strict" equals() for most stuff I was interested in. This makes me nervous about ===. Like, I'm almost skeptical that Javascript
"foobar" === "foo" + "bar"; //true (amazingly ;-)
Yeah it works, but because I think of it in terms of what it looks like in Java, I don't want to trust it.
FOLLOWUP in 2017: I just noticed that
foo == new String('foo')
but not
foo === new String('foo')
which is exactly parallel to the Java case, but arguably much less significant because String as an object is rather rare in Javascript land.
3. === is used to show off
It feels like an elitist shibboleth to me. (And yes, I love that knowing what "shibboleth" means is in itself a shibboleth.) To be fair, these people probably point to funkiness in the truthiness tables, and just odd asymmetries there, but in my heart of heart I think it's just a way for the with-it hipster coders to show they are with-it hipsters.
4. if(arg == undefined) //true for argument not passed, and null, but not for zero
Finally, this might be my most serious argument.
Say I have a function funk(arg), and returns its argument or a default of -1, if no argument is passed. (And of course it should work with 0 as a value) So:
function funk(arg){
if(arg == undefined) arg = -1;
return arg;
}
That works pretty well... (if I used the conditional if(!arg), that would accidentally catch 0 and return -1, and so in general I shun simple boolean expressions, preferring to check for undefined.)
So what I like about == is that is that funk(null) would be caught, and replaced with -1, but if I used === funk(null)would return null, because undefined === null is false. (Of course, "null" and undefined are kind of odd beasts in Javascript land; some people think the "undefined" value is just an accident of history anyway.) (PS don't do stuff like using undefined as a real value, like calling funk(undefined) - that's just R,O,N,G, wrong.)
So basically, I prefer the behavior of double equals, and prefer its made up "this would fail!" arguments to the ones made in favor of trouble equals.
(Quick tip: if you don't know it already, the "console" you can get to in Chrome inspector or Firefox Firebug is great for doing this kind of little functional definition, tooling around with making and calling functions, just ignoring the page it's on. And of course it's an invaluable tool for poking around your Javascript in general.)
FOLLOWUP: A friend of mine points out "NaN" kind of messes up my simple != undefined filter, since it is defined, but probably not what we were expecting to work with what I described.
POSTSCRIPT FROM THE END OF 2017
Listening to a podcast with scientists pontificating, I realize I treat triple-equals usage similarly to how I treat the correct usage of "data are plural", and for similar reasons: a begrudging respect for people using a shibboleth correctly, set against a personal bias for the looser usage; with that split masking a philosophical difference in worldview.
My worldview is: people and things are more important in how they interact than in their internal makeup. Take "Data". It's technically a plural world from Latin, with "Datum" being the singular. But a "Datum" is useless to the point of meaninglessness on its own - ONLY through multiplicity does a datum go from being a one-off anecdote to a statistically meaningful bit of information. Casual use would treat "Data" as a kind of singular group noun - "what this data suggests" vs "what these data suggest", and since that group-making is the only useful way people interact with data, "this data", the street usage, makes much more sense.
(I'm not a big fan of the old tradition of presuming Latin rules need apply to English anyway- like how you should never split an infinitive ("to boldly go") since such a construction is impossible in Latin where the infinitive is a single word.)
With triple equals, I return to the basic idea that it means "reject the comparison if the things being compared aren't exactly the same type" - an internal analysis. Double equals says if two things have the same value when they interact, that's fine! We don't care about the history or composition of the things, just how they'll interact now. (A long history with Perl and other duck-type languages helps inform my view, I think.)
To wax philosophical, I've realized this difference in worldview- whether what's important is the history and internals of a thing (since that will be the surest guide to predicting long-term behavior, and/or give you a special revelation of how things "should be") or whether we should attend to how things are capable of interacting with the outside world - is profound and tough to bridge.
I'd recommend the book "Surfaces and Essences: Analogy as the Fuel and Fire of Thinking"... and then when I hear some punk like Scott Adams say that because analogies are always imperfect they can never be persuasive, and that it's where "reason is embarrassed to show its face"... balderdash. Finding parallels in how different systems are interacting makes up one of the most critical tools in understanding the world, no matter that there will ALWAYS be some difference in intrinsic makeup. (Of course, saying there are only surfaces or only essences is a false dichotomy; some analogies run deep, that two systems are interacting in parallel ways because of parallel functioning in their guts. And some analogies are just shallow and rhetorical and are of less value.)
PS to the PS: Regarding point 4, my college buddy (and freshman roommate) Rob points out some cases where keeping the null vs undefined is useful - the differenc between a missing key and a key that points to a null valye - like an API where the object.user will not be set at all if a call hasn't been null, or if the call completed but there was no user or whatever, it would be null....
Monday, April 7, 2014
gender difference in 3d perception
Fascinating article on the gender difference in 3D perception systems by gender, and how women might be more prone to feeling sick using systems based on parallax (which is what most binocular-based systems are based on) at the cost of "shape-from-shading".
It's one of those cases where all-dude teams, even "well meaning ones", may be making gender assumptions they don't even realize.
It's one of those cases where all-dude teams, even "well meaning ones", may be making gender assumptions they don't even realize.
Subscribe to:
Posts (Atom)