Profile

Cover photo
Dan Doel
Works at Capital IQ
Attended Case Western Reserve University
Lives in Cambridge, MA
199 followers|59,869 views
AboutPostsPhotosYouTube

Stream

Dan Doel

Shared publicly  - 
 
I think this might be a good video to show programmers who are struggling with learning Haskell, monads, category theory, etc. There are, naturally, a lot of parallels between that stuff and learning to ride a backwards bicycle after already knowing how to ride a normal one.

It also kind of illustrates +Brent Yorgey's monad tutorial fallacy. When people's brains snap over to finally understanding, they tend to think it was the last example or analogy they read that did it, when it was actually the period of repeated struggling, and everything eventually snapping into place is just how understanding works in general.
6
Shae Erisson (shapr)'s profile photo
 
As you said yesterday, read repeatedly until understanding appears.
Add a comment...

Dan Doel

Shared publicly  - 
 
More thoughts about sets vs. domains with a lot of digression on how traversals are related to free applicatives.
Last time I looked at free monoids, and noticed that in Haskell lists don't really cut it. This is a consequence of laziness and general recursion. To model a language with those properties, one needs to use domains and monotone, continuous maps, rather than sets and total functions (a ...
2
Add a comment...

Dan Doel

Shared publicly  - 
 
I saw a youtube comment complaining that a two Michelin star rated chef in the video said, "English feta cheese," while feta is a Greek cheese, and it reminded me of this.
Paul Chiusano. Functional programming, UX, tech, econ. Twitter • GitHub • LinkedIn • RSS. Consulting services. I offer Scala and FP consulting services. If you're interested in working together, please contact me. About my book. My book, Functional Programming in Scala, uses Scala as a vehicle ...
1
Add a comment...

Dan Doel

Shared publicly  - 
 
Scott obviously likes math, because he can do 497 × 35 in his head!
1
Add a comment...

Dan Doel

Shared publicly  - 
 
French onion soup.

Took a lot of prep time, but it was a pretty good payoff.
2
Dan Doel's profile photo
 
Looks like google's technology for determining the ingredients of food in pictures isn't very good yet.
Add a comment...
In his circles
71 people
Have him in circles
199 people
Chris Smith's profile photo
Emily Chahal's profile photo
Mat Morton's profile photo
Dustin Getz's profile photo
Runar Bjarnason's profile photo
Raphael Kron's profile photo
Shae Erisson (shapr)'s profile photo
David Feuer's profile photo
Keegan McAllister's profile photo

Dan Doel

Shared publicly  - 
 
Speaking with +Edward Kmett about some operad stuff he's been doing caused me to realize a new way of seeing that all the GADT stuff in Haskell is not really dependent types.

My usual go-to example is functions. Whenver people use GADTs and indexing to fake dependent types, they think about data types. And those are amenable to faking (though it's often not pleasant) because, well, indexed data types are data. But functions are wired in, and there's really no good way (that I know of) to represent singleton subtypes of function spaces using GADTs, at least if you want it to look remotely similar to dependent types.

However, I came to the realization that it isn't really even adequate for data types, because the semantics of the type level are different than those of the value level. For instance, I wrote some stuff recently about how (finite) lists aren't the free monoid in Haskell. But Haskell's type level is expected to behave more like a total language, so finite lists are the free monoid at the type level.

And even though we have all kinds of automatic lifting now, the semantics don't actually line up. The 'faking it' procedure is that to have 'dependent types' involving T, you lift T to the type level, create T' :: T -> *, and maybe recover T by working with ∃(t :: T). T' t. But this construction actually isn't the same as having types that depend on your original T (for Haskell), because it is actually constructing the finitary version of T. There are even certain rules for working with existentials that ensure this is the case.

The short version is that reasoning about the lifting of your data type doesn't mean you're actually reasoning about the original value level type, just about a corresponding object with an analogous definition.
6
David Roberts's profile photoDan Doel's profile photoMichael Shulman's profile photoUrs Schreiber's profile photo
10 comments
 
I see. Well, then it only remains to hope that Agda eventually attracts a larger community of real-world programmers. Should make for some interesting synergy effects.
Add a comment...

Dan Doel

commented on a video on YouTube.
Shared publicly  - 
 
Eagerly awaiting the more faithful fan edit, where Bilbo gets hit in the head by a rock, followed by 3 hours of blank video. Then Bilbo wakes up and goes home.
2
Add a comment...

Dan Doel

Shared publicly  - 
 
Deja vu.
2
Erik de Castro Lopo's profile photo
 
Had a scene very much like that here in Sydney a week or two back.
Add a comment...

Dan Doel

Shared publicly  - 
 
Today I was again reminded why having proper tail calls in your language is important (and why optimization of tail recursion doesn't cut it), and felt like ranting a bit. I'm going to use Haskell syntax, but pretend it's a language that's strict (hence the () ->) and only has tail recursion optimization.

We were seeing a stack overflow in some of our code, and tracked it down to some stream processing we were using. The processors looked something like:

    data SP k r
      = Done
      | Emit r (() -> SP k r)
      | forall e. Await (k e) (e -> SP k r) (() -> SP k r)

With this you can make regular and branching processors:

    type Process i o = SP ((->) i) o
    newtype (f + g) a = Sum (Either (f a) (g a))
    type Tee i j o = SP (( ->) i + ( ->) j) o

and you might want to pipe together Processes and Tees:

    tee :: Process i i' -> Process j j' -> Tee i' j' o -> Tee i j o
    tee _ _ Done = Done
    tee l r (Emit o next) = Emit o $ \() -> tee l r $ next()
    tee l r (Await req s f) =
      either
        (\kl -> case l of
          Done -> tee l r $ f()
          Emit i next -> tee (next()) r (s $ kl i)
          Await reql sl fl -> ...)
        (...)
        req

Hopefully this is pretty straight forward. If the branch processor is done, so is the composition; if the branch emits a value, so does the composition; if the branch needs a value, it queries the appropriate single stream being fed in.

But, this code has a problem: tee is not tail recursive. In some cases this is appropriate, because Emit is postponing recursive calls to tee to when more structure is demanded. Recursion is broken by constructor emissions, and the onus is on the consumer to not overflow the stack. But, in the case where we have input to feed into a requesting branch stream, the non-tail recursive cases will break the optimization that prevents a stack overflow from happening. So we are forced to write some boilerplate that separates the tail recursion from the non-tail recursion:

    annihilate l r Done = Done
    annihilate l r (Emit o next) = Emit o $ \() -> tee l r $ next()
    annihilate l r (Await req s f) =
      either
        (\kl -> case l of
          Done -> annihilate l r $ f()
          Emit i next -> annihilate (next()) r (s $ kl i)
          ...)
        (...)
        req

    tee l r t = annihilate l r t

annihilate calls itself in the tail positions and tee in the non-tail positions, so the compiler can figure out how to generate code that actually works.

But wait! annihilate still doesn't work. Why? Well, we've used a function for control flow; either. But this means that the entire rest of the Await case is not in the tail position, so neither are all our calls to annihilate. So we must inline the definition of any such helper functions until our control flow consists only of blessed primitive constructs that the compiler considers eligible for tail recursion optimization.

In other words, lack of proper tail calls requires programmers to write boilerplate, and makes user-defined control flow abstractions less useful.
4
Add a comment...

Dan Doel

Shared publicly  - 
 
Today I discovered that if SQL Server Management Studio is stuck trying to expand a list of tables in a database (which locks up the entire interface, by the way), and you try to kill it using the task manager, it can blue screen your computer.

Isn't that great?
1
Josh Cough's profile photo
 
Totally wicked.
Add a comment...

Dan Doel

Shared publicly  - 
 
After having worked with JavaScript for a few weeks now, I have come to the startling conclusion that it is not a good language. In fact, it's probably worse than some of its peers (which are also not great) in various ways, despite its reputation (I think) for being one of the better 'scripting languages.' And I'm not even talking about stuff like lack of proper tail calls, or weird "wat" (https://www.destroyallsoftware.com/talks/wat) behavior. That's bad, but JavaScript is bad in even more basic ways than that.

1. Slow edit-compile cycle

This seems contradictory, because JavaScript doesn't get compiled. But that's the whole problem. When you don't have a compiler, or some kind of static checker, your method of finding basic mistakes that would have been caught that way is to just run the program. When you're working on a section of code that can only run multiple minutes into the program (like I am, in this case), that means your effective compile times are multiple minutes.

It's actually worse than the Scala project I also work on. That compiles quite slowly, but in the event of actual errors, it tends to fail quickly. But with JavaScript, I have to wait minutes just to find out that I misspelled a variable name. Which brings me to the next problem....

2. Non-local behavior of simple mistakes

What happens if you access a non-existent member of an object? You get back undefined. Not an error, just something similar to null (except there apparently is a null, which is different). This also happens if you access a variable that hasn't been bound. Sometimes this gives you an error right where you made the mistake, if you write:

    noExist.foo

But other times, you'll just pass noExist along, and eventually get an error in a completely different part of the program (or an entirely different program all together). This means that after my 3 minute 'compile,' I sometimes don't even get told that my problem is a basic name error; I have to figure out that's what the problem is from other clues.

3. Modularity

From what I gather, JavaScript has no built-in module capabilities (which doesn't surprise me). This means that, like Scheme, modules are built out of other language features, like first-class functions. This is fine, except that unlike Scheme, JavaScript doesn't have macros to make the syntax acceptable. So you end up with something that looks like (I don't remember the precise syntax, but this is close):

    module ('myModule', ['foo', 'bar', 'baz', 'quux', ...],
      function (F, BR, BZ, Q, ...) {
        ...
      });

Now, we have modules with a dozen or so imports, so to figure out which module any qualifier stands for, I have to count up to the qualifier in the second list, and then count up to the corresponding member of the import list. And if you're adding a new import, and want to keep them organized in some way other than just chronological order of addition, I hope you don't accidentally get elements of the two lists swapped relative to one another, because you'll probably spend half an hour tracking down what went wrong (mostly due to #2).

4. Statements

My brain no longer comprehends languages that aren't entirely expression based.  So I constantly find myself writing things like:

    function (x) { x === 5 }

This function returns undefined, i.e. doesn't return anything (void). You have to put a return in to return the value. This might not be so bad, except JavaScript doesn't have types that would allow it to complain that I have produced a procedure, whereas I meant to write a function that returns a boolean. And of course, there is also the typical, 'if isn't usable as an expression, so we need an additional operator,' stuff.

There's probably more, but this is my list so far.
Wat. A lightning talk by Gary Bernhardt from CodeMash 2012. The sarcasm in this talk does not represent anyone's actual opinion. For a more serious take on software, try Destroy All Software Screencasts: 10 to 15 minutes every other week, dense with information on advanced topics like Unix, TDD, ...
18
2
Stephen Compall's profile photoNami Doc's profile photoKonstantin Solomatov's profile photoMichael Hanke's profile photo
9 comments
 
Unsoundness in action : https://gist.github.com/t0yv0/44493510

Probably better than nothing, indeed. Did somebody try ScalaJS ?
Add a comment...

Dan Doel

Shared publicly  - 
 
Awesome Games Done Quick 2014 is live, raising money for the Prevent Cancer Foundation.

This year with two blindfolded Punch-Out runs (probably).
1
Add a comment...
People
In his circles
71 people
Have him in circles
199 people
Chris Smith's profile photo
Emily Chahal's profile photo
Mat Morton's profile photo
Dustin Getz's profile photo
Runar Bjarnason's profile photo
Raphael Kron's profile photo
Shae Erisson (shapr)'s profile photo
David Feuer's profile photo
Keegan McAllister's profile photo
Work
Occupation
Software Engineer
Employment
  • Capital IQ
    Software Engineer, 2011 - present
Places
Map of the places this user has livedMap of the places this user has livedMap of the places this user has lived
Currently
Cambridge, MA
Previously
Maineville, OH - Cleveland, OH
Links
YouTube
Contributor to
Story
Introduction
Why don't you try making four beats a day for two summers?
Education
  • Case Western Reserve University
    Computer Science, 2001 - 2005
Basic Information
Gender
Male