• Weather troll: unbelievably clear blue skies in upstate New York today, the day before the eclipse. Forecast for tomorrow doesn’t look so promising, but 🤞

  • PSA: Google Calendar on iPhone needs “Precise Location”

    At some point the Google Calendar app on my iPhone stopped letting me search for locations when editing events. I could type in text, but would get no suggestions and would have to type/paste in an entire street address for it to do anything useful. Today I looked into it and after some experimenting, it looks like it was broken because I had turned off “precise location” for that app in Privacy settings. Because, why would my calendar need to know my precise location? In any case, turning it back on seems to have fixed it. And given that I’m putting the location on an event in order to eventually navigate there using Google Maps, I guess I’m not really exposing anything new. Still frustrating.

  • ML/AI is the long-awaited answer to the question “what are we going to do with all these transistors, since we can’t figure out how to write programs that do something useful with more than a handful of CPU cores?”

  • ⚽ Group G

  • ⚽ Group H

    Group H Potential Results

  • ⚽ Group E

  • ⚽ Group F

    Group F result matrix

  • Group C

    #worldcup ⚽

  • Watching the final games of the group stages, I can never keep track of the possible results in my head. So I made a quick and dirty viewer using SwiftUI:

  • Have we reached peak Zuckerberg? Where do we go from here? #holodeck #bondvillain

  • More Nand to Tetris: now self-contained

    After a long break, I spent some time recently fleshing out my Python re-implementation of the remarkable From Nand to Tetris course. You can now compile and run Jack programs without downloading any other tools, and work through the entire course up to and including writing your own compiler for your CPU.

    It’s still seriously good fun to build a (simulated) CPU from scratch in a couple of hours, and then see what it’s like to run an interactive program on it.

    If you’re interested in learning about how an entire computer is built out of dumb electronic components, or you know how this stuff works and you want to play with your own designs, and if you like Python, check it out at github/mossprescott/pynand.

  • After decades(!) of watching from the sidelines, this year I’m putting some real time and energy into #WWDC2021. Feels good to be focusing on making great Mac software, which was my first love after all.

  • It’s on. #coffee

  • Font too big; DR

    From the “Shouting into the wind” department: oversized body text is a menace on programming blogs I read. My theory is that these bloggers are running Linux at odd pixel densities (i.e. 1440p laptops) and they can’t read text at normal sizes, so they size everything up as a matter of course. Today I looked at the CSS on one and found “font-size: 1.2rem” which (I’ve now learned) means “make the body text 20% bigger than the reader wants it to be.” Please don’t do this. I might want to read what you have to say, but I often won’t have the patience to adjust my browser to accommodate your questionable life choices.

  • There have been some tough days recently, but on a day like today it’s good to be Charlie 🐕 🕯🕯🕯🕯🕯 🕯🕯🕯🕯🕯 🕯🕯🕯

  • SwiftUI, Catalyst, and now unmodified iPhone and iPad apps. How many ways do we need for iOS developers to target Mac? All of them, apparently.

  • Does featuring both Docker and Linux under Parallels in the #WWDC keynote suggest that macOS on ARM will have an even less Unix-y terminal environment than in the past?

  • Trying to print a partially-evaluated list in Haskell (with partial success)

    Working on exercises from Okasaki’s Purely Functional Data Structures involving lazy evaluation and wondered if it would be possible to introspect on the structures in memory and see what’s actually going on at runtime.

    Note: Scala’s lazy Stream does this, for the cells but not their contents:

    scala> val fibs: Stream[Int] = 0 #:: 1 #:: fibs.zip(fibs.tail).map { case (a, b) => a + b }
    fibs: Stream[Int] = Stream(0, ?)
    
    scala> fibs(6)
    res6: Int = 8
    
    scala> fibs
    res7: Stream[Int] = Stream(0, 1, 1, 2, 3, 5, 8, ?)
    

    I found this evaluated function on Stack Overflow (with a whole list of caveats), and slapped together a simple hack. It works for me with GHC 8.6.5 and -O0, but not in ghci or with optimization turned on.

    An example test:

      it "shows a list with several cells evaluated" $ do
        let lst = [1..10] :: [Int]
        putStrLn $ tshow $ take 3 lst  -- actually demand the first three values
        showLazyList lst `shouldBe` "1 : 2 : 3 : ..."
    

    And the code:

    import GHC.HeapView
    import System.IO.Unsafe (unsafePerformIO)
    
    -- [stackoverflow.com/a/2870168...](https://stackoverflow.com/a/28701687/101287)
    evaluated :: a -> IO Bool
    evaluated = go . asBox
      where
        go box = do
          c <- getBoxedClosureData box
          case c of
            ThunkClosure     {} -> return False
            SelectorClosure  {} -> return False
            APClosure        {} -> return False
            APStackClosure   {} -> return False
            IndClosure       {indirectee = b'} -> go b'
            BlackholeClosure {indirectee = b'} -> go b'
            _ -> return True
    
    showLazyList_ :: (Show a) => String -> String -> [a] -> IO String
    showLazyList_ elemThunkStr tailThunkStr lst = do
      evaluated lst >>= \ case
        True -> case lst of
          h : t -> do
            hStr <- evaluated h >>= \ case
                      True -> pure (show h)
                      False -> pure elemThunkStr
            tStr <- showLazyList_ elemThunkStr tailThunkStr t
            pure $ hStr <> " : " <> tStr
          [] -> pure "[]"
        False -> pure tailThunkStr
    
    -- |Show the contents of a list, only so far as have already been evaluated. Handles unevaluated
    -- elements and cells. Uses unsafePerformIO shamelessly.
    showLazyList :: (Show a) => [a] -> String
    showLazyList = unsafePerformIO . showLazyList_ "?" "..."
    
  • I went through my drawer and pulled out all the cables with USB (A) on one end. What a great run that port has had. I like the idea of hanging these all up somewhere where I can see what’s what. Also, some of these might not actually be worth holding onto 🤔

  • Made my own mask from stuff in the house. Glad to be able to protect myself and my neighbors without taking a “legit” mask away from someone who needs it more. Thanks, ragmask.com!

  • Spring, 2020

  • Simple Techniques for Debugging SwiftUI Views

    SwiftUI Views are written in a declarative style, which can be a nice, compact way to construct a large tree of sub-views, but it also can make it surprisingly hard to do certain things.

    For example, suppose you’re debugging your View and you’re wondering about the value of some state variable at a certain time. You might try writing this:

    struct MyView: View {
        var label: String
        
        // WARNING: this doesn't compile!
        var body: some View {
            print(“The label is: \(label)”)
            Text(label)
        } 
    }
    

    If you do, you’ll see this very un-helpful error:

    Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type

    Note: error messages are supposed to be much better in Swift 5.2, which is available in Xcode 11.4 as of yesterday, so things might already be better on that front, but the code still won’t compile.

    Fix #1: add return

    If you stare at that error message for a while, you might be inspired to try changing that last line to return Text(label), and in fact that will work.

    Now suppose your View is a little more interesting, with more than one sub-view, and there are different things you want to log:

        // WARNING: this doesn't compile!
        var body: some View {
            VStack {
                print("The label is: \(label)")
                Text(label)
                print("There are \(label.count) characters")
                Text("abc")
            }
        }
    

    This doesn’t work, and you can’t fix it by adding return. If you play around with it a little bit, like I did, you’ll find that you don’t get far by trying to treat these View expressions as regular code blocks. Instead, think of it as a special language just for Views, which just happens to use some of Swift’s keywords (like if and else).

    Fix #2: put it in a regular function

    You can get back to the familar world of “normal” Swift code by just putting the logging in a func and making it so the func’s result is part of the View expression. Here’s one way to do that:

        func trace<T>(msg: String, _ result: T) -> T {
            print(msg)
            return result
        }
    

    Since this function can accept any value, you can wrap it around whatever part of the expression you like, which is either clever or ugly, depending on how you look at it:

        var body: some View {
            VStack {
                trace(msg: "The label is: \(label)",
                    Text(label))
                Text(
                    trace(msg: "There are \(label.count) characters", "abc"))
            }
        }
    

    Fix #3: something prettier

    A nicer idea (thanks to Rok Krulec here) is to set up your func to build a special View that doesn’t draw anything, but just does your logging when it’s constructed and then hopefully stays out of the way:

        func Print(_ msg: String) -> some View {
            print(msg)
            return EmptyView()
        }
    
        var body: some View {
            VStack {
                Print("The label is: \(label)")
                Text(label)
                Print("There are \(label.count) characters")
                Text("abc")
            }
        }
    

    Pretty slick.

    Fix #4: use the UI, Luke

    On the other hand, if you’re using print to debug your UI, in a sense you’re doing it wrong — after all, you’re not writing a command-line app! What’s the UI equivalent of print? instead of spewing debug all over the console, how about spewing it all over the UI? Sounds great, right?

    More practically, printing to the console doesn’t work when you’re using SwiftUI Preview to see the effect of your edits in real time. When you do that, print-ed output doesn’t seem to go anywhere as far as I can tell.

    Here’s one way to make that happen:

        var body: some View {
            VStack {
                Text(label)
                    .annotate("The label is: \(label)")
                Text("abc")
                    .annotate("There are \(label.count) characters")
            }
        }
        
    ...
    
    extension View {
        func annotate(_ msg: String) -> some View {
            self.overlay(
                Text(msg)
                    .fixedSize(horizontal: true, vertical: true)
                    .offset(x: 100, y: 10)
                    .font(.caption)
                    .foregroundColor(.gray)
            )
        }
    }
    

    This way, each line of debug output is displayed in the View itself. Each message floats below and to the right of the sub-view it’s attached to. Using overlay ensures that the View’s layout is unaffected by the dangling messages, and fixedSize allows each one to expand beyond the size of its corresponding View. The rest of the attributes just hopefully make the annotations a little less prominent so the UI is still recognizable.

    Anyway, those are some things I’ve found to be interesting and perhaps useful. SwiftUI’s new, declarative style requires some new tools and some new ways of thinking. I’m enjoying experimenting to figure out what works, and I hope you found these experiments interesting, too.

  • Mouse support in the iPadOS 13.4 beta feels surprisingly natural and also uncanny at the same time. It’s clear a lot of thoughtful design work went into it. I think it’s great that they’re actively expanding the ways the platform can be used.

  • I turned my SwiftUI hack into a (crude) app. Much more fun to play with on the phone.

subscribe via RSS