HN user

_rend

577 karma
Posts2
Comments56
View on HN

To expand a bit, too, on how these definitions make side effects easier to represent in Haskell:

One way to represent side effects in a purely functional language is to model them as if they aren't side effects, by representing them as state changes in the "outside world". You don't need to grok the specifics of this, but the definition of the `IO` monad is:

  newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
i.e., it's a "pure" transformation of the "real world".

This allows you to define a "box" called `IO` that represents a computation that can perform a side-effect (by affecting the "real world"), then returning a value.

The real trick to this is that the "box" is entirely opaque to you: unlike a list or a `Maybe` where you know how to reach in and pull values _out_ (e.g., `head`, `last`, `fromJust`, etc.), `IO` doesn't allow you to do this*. Once you have something inside of an `IO` box, it's stuck there.

This means that you can separate the "impure" world from the "pure" world: you can't perform side effects arbitrarily — you're can only do so in an `IO` context that's intentionally "viral".

The functor/applicative/monad rules just make `IO` easier to use and consume:

  1. `Functor` allows you to "map" over the results of a computation
  2. `Applicative` allows you to chain computations together in order so side effects happen in sequence
  3. `Monad` makes it easier to repeatedly chain computations within a single `IO` context (so if you need to perform repeated side effects, you can "stay" in the outer context — `IO a` instead of `IO (IO (IO (IO (... (IO a)))))`)
This is just one way to represent side effects, and the monad rules are only really needed to make this representation ergonomic to actually use.

(*There is technically a way to "escape" the `IO` monad called `unsafePerformIO`, but you basically never need to use this. If you find yourself reaching for it, don't.)

Sure! "Box" here is used to just abstractly describe a value that contains other values. Let's take a list as an example:

  [1, 2, 3] :: [Int]
Here, the "box" is a list, and inside of it are the values 1, 2, and 3.

As you know, `map` is an operation that converts the values inside of the box into other values; for example, adding 1 to every element:

  [1, 2, 3] :: [Int]
   |  |  |     (+ 1)
   v  v  v
  [2, 3, 4] :: [Int]
But the operation you perform with `map` doesn't need to keep the values of the same type:
  [ 1,   2,   3 ] :: [Int]
    |    |    |      (show)
    v    v    v
  ["1", "2", "3"] :: [String]
The operation can also produce new boxes! Since `String` is actually itself a list (`[Char]`), the result above is the same as
  [  1,     2,     3  ] :: [Int]
     |      |      |       (show)
     v      v      v
  [['1'], ['2'], ['3']] :: [[Char]]
In some cases, you might want to "flatten" this box-of-boxes together. In some languages this operation is called "flatten"; for lists in Haskell, it's called `concat`
  [['1'], ['2'], ['3']] :: [[Char]]
     |      |      |       (concat)
     v      v      v
  [ '1',   '2',   '3' ] :: [Char]
This example isn't terribly motivating, but you can see when you have deeper lists-of-lists how this might be handy:
  [[1,2,3], [4,5,6], [7,8,9]] :: [[Int]]
      |        |        |        (concat)
      v        v        v
  [1, 2, 3, 4, 5, 6, 7, 8, 9] :: [Int]
Here, we took a collection of boxes (`[[Int]]`) and combined them in order (sequentially) to produce a new box (`[Int]`).

What other languages call `flatMap` is just a `map` operation followed by a `flatten` operation. Very roughly, `Functor` gives you "map" (`map`), `Applicative` gives you "flatten" (`concat`), and `Monad` gives you "flatMap" (`concatMap`).

The power of these comes from considering different types of "boxes". `Maybe`, for example, works almost like a list that can contain up to 1 element, and its operations behave pretty much identically. Other types are interesting because how you define their "box-ness" can lead to interesting/useful results. It can be tough to envision how, e.g., a function could look like a "box", but it turns out that you can define rules for it that make it useful. (What does "map" look like for a function? Well, it turns out that mapping a function over another function is already just... function composition!)

You can go a lot deeper into these definitions, and it helps to look at some implementations to grok them better, but the core concepts themselves are not very complicated. The "magic" is in how you define the "boxes".

My personal alternative take to the usual monad tutorial — greatly simplified:

"Functor", "Applicative", and "Monad" are all just generalizations of the concept of `map` and `flatMap`.

  1. Something is a "Functor" if you know how to call `map` on it, nothing more. "I can take a box of things and turn it into a box of other things, 1-to-1". On lists, for example, this is just `map` itself
  2. Something is an "Applicative" if you know how to call `map` on it, but also know how to take a non-boxed value and put it in a box, and also know how to combine boxes in order
  3. Something is a "Monad" if you know how to do all of the above, but also know how to call `flatMap` on it, nothing more. "I can take a box of things, turn each thing into a new box, and then combine them all in order". On lists, for example this is just `concatMap`
There's nothing really more complex to it, besides how you squint at various things (like functions) to fit them into the concept of `map` and `flatMap`.

To answer your questions more directly:

  1. Monads themselves are neither necessary nor sufficient to perform side effects in Haskell; they don't directly enable the effects, but they *do* help place guardrails on the actual unsafe, low-level code which *can* perform the effects, safely and in an ergonomic and composable way
  2. Yes, "Monad" is just a name for a recurring way to approach a problem. Like in most math and programming, a certain repeating pattern was noticed, and given a name. Because of the math origin of the term, you get "Monad" instead of "flat-mappable"
  3. Like any other tool, you reach for a monad when you have a monad-shaped problem. They're just one (powerful) tool for solving certain problems

This is awesome! Love to see something new in this space, especially so heavily inspired by QuickSilver. The UI is slick and fast, and the fuzzy matching (and the match UI itself) is excellent.

If you're taking feedback, I've been a >decade-long user of LaunchBar, and I've yet to find another launcher that handles my most common actions quite as well (except maybe Alfred):

1. I launch a ton of URLs directly from LaunchBar, and it's a killer feature for me to be able to start typing a URL (not intending to match anything) and as soon as I type a period, LaunchBar converts the search to a URL (and inserts 'https://' and '.com'). e.g., if I type "abc.", LaunchBar will expand to "https://abc.com" with the ".com" highlighted for replacement (and hitting Return will open the URL immediately). Right now, if I want to do the same with Tuna and my default mode is Fuzzy Mode, I believe I need to hit '"' to enter Text Mode, type the URL, hit Tab, then search for the "Open URL" action (which also won't recognize a "bare" URL without the scheme, so won't show up for, e.g., "abc.com") — but happy to be wrong! I think it'd be swell if it were possible to configure Tuna to, on '.', convert into text mode, automatically insert "https://" and ".com", and automatically pre-populate the "Open URL" action so I could just hit Return to confirm and launch

2. I use the inline calculator a lot, and really like the "auto math" switch when typing digits (and really like the carve-out for 1Password, where typing '1' will show 1Password in fuzzy search instead of switching to the calculator); switching to text mode automatically on numeric input would be really helpful to do the same

3. I have a few custom search templates in LaunchBar I use all the time (several different search engines), and I'm not sure if it's possible to set up something similar directly inside of Tuna yet without writing custom services or an extension

Obviously, this is just how I use LaunchBar, and may not fit in with your vision of Tuna, but figured it might be some helpful food for thought! Thanks for your work on this :)

This is awesome! The design is slick, and it really does feel right at home on a portable device. Appreciate the work that went into this.

Even more so, huge kudos for the performance tuning! The app launches instantly, so much so that I was initially a bit shocked. I'd forgotten what it feels like to tap a button and have something functional appear on screen with zero delay. When even the simplest apps have loading screens (and even built-in Settings/Reminders/Notes/Phone/etc. have a delay), this was really refreshing to see! I legitimately force-quit a bunch of apps on my phone to compare and nothing comes close on cold launch. Great work!

This is Swift, where Type? is syntax sugar for Optional<Type>. Swift's Optional is a standard sum type, with a lot of syntax sugar and compiler niceties to make common cases easier and nicer to work with.

For completeness, this description of alignment is misleading:

Well, dear reader, this padding is added because the CPU needs memory to be aligned in sets of 4 bytes because it’s optimized in that fashion.

...

Remember: since structs are aligned to 4 bytes, any padding is therefore unnecessary if the size of the struct is a multiple of 4 without the padding.

Individual data types have their own alignment (e.g., `bool`/`char` may be 1, `short` may be 2, `int` may be 4, `long` may be 8, etc.), and the alignment of a compound type (like a struct) defaults to the maximum alignment of its constituent types.

In this article, `struct Monster` has an alignment of 4 because `int` and `float` have an alignment of 4 for the author's configuration. Expanding one of the `int`s to a `long` could increase the alignment to 8 on some CPUs, and removing the `int` and `float` fields would decrease the alignment to 1 for most CPUs.

Hyperspace 1 year ago

Which means if you actually edited those files, you might fill up your HD much more quickly than you expected.

I'm not sure if this is what you intended, but just to be sure: writing changes to a cloned file doesn't immediately duplicate the entire file again in order to write those changes — they're actually written out-of-line, and the identical blocks are only stored once. From [the docs](^1) posted in a sibling comment:

Modifications to the data are written elsewhere, and both files continue to share the unmodified blocks. You can use this behavior, for example, to reduce storage space required for document revisions and copies. The figure below shows a file named “My file” and its copy “My file copy” that have two blocks in common and one block that varies between them. On file systems like HFS Plus, they’d each need three on-disk blocks, but on an Apple File System volume, the two common blocks are shared.

[^1]: https://developer.apple.com/documentation/foundation/file_sy...

Award-winning photo editing, graphic design and page layout software for Mac, Windows & iPad.

They've supported Windows and iPad for years, too.

You're right, good question. At least partially, habit and muscle memory. I'm used to the keybindings, and the behavior for navigating tabs/splits/panes (across macOS, Windows, and Linux).

But also, native splits/panes and tabs cover 90% of what I really want from a multiplexer, so it's easier for me personally to stick with familiar behavior than to integrate another tool into my workflow just to recreate it.

Zellij is pretty great, and I recommend others check it out. The UI is extremely slick, and getting a comfortable setup is nicer (to me) than tmux or screen.

Unfortunately, it's missing one key feature that keeps me from using it as a daily-driver: it doesn't appear to be possible to attach to an existing session by automatically creating a new tab or pane. iTerm2 has fantastic integration with tmux that allows it to directly create a new tmux tab for every native iTerm2 split or tab, and I was hoping to recreate that with Zellij, outside of iTerm2.

It _is_ possible to open a new tab with the `new-tab` action (or whatever it's called), but unfortunately, there's no way to do that "in the background": one of your open sessions always switches to that new tab when it opens. I don't know if this is a limitation of the session/tab system, but when I dug through the source, I couldn't for the life of me figure out why this was happening.

I did spend some time trying to contribute a flag to allow attaching to existing sessions with a new tab/pane, but the actual architecture in place back then made this very difficult to support without non-trivial refactoring (and at least at the time, Zellij wasn't accepting any major contributions that weren't directly aligned with the roadmap, which I respect: there's only enough time in the day to review random PRs).

I check back periodically; if this is made possible at some point, I'd love to switch to it.

Financially, a doctor won't notice it.

Not all doctors make enough money to not notice it, especially ones earlier in their career.

Regardless, assuming you have two working adults each paying the $15 toll once a day, conservatively, working 250 days a year, that's $7,500/year that has to come from somewhere. I can't imagine an income level (even if it doesn't affect your quality of life) where that's not an insanely frustrating amount to pay… to the MTA of all places. That money getting reinvested into useful infrastructure would be a dream come true!

As a former NYC resident, and as someone whose family still lives and works in the city, I'm curious to see how this'll distribute traffic patterns throughout Manhattan. If you live in the outer boroughs like my family does, getting into certain areas in Manhattan via public transit can be difficult, and time consuming — significantly more so than getting in by car.

My dad is an on-call doctor; getting to his hospital by car takes ~15 minutes, but ~60–90 via public transit. His patients don't have the luxury of waiting for him to take the bus. His hospital is outside of this zone, but I imagine that paying $15 every time he got called in would be extraordinarily frustrating.

My mom does work within this zone, also in places not easily reachable by public transit. I suspect that she, like many others, will still commute into Manhattan, park in areas outside of the zone, then take public transit into it — which will increase congestion in those areas. It'll be interesting to watch for the lead-on effects.

I sympathize entirely with the desire to reduce traffic in the city, but man, for people who live far from work and can't easily commute any other way, what a pain.

jQuery v4.0 Beta 2 years ago

A series of function calls that each do something and each returns a value.

This is not what functional programming is. Functional programming is a completely different paradigm for writing code, and it _is_ declarative by definition. The Wikipedia definition is pretty decent:

    Functional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions that map values to other values, rather than a sequence of imperative statements which update the running state of the program.
The "Comparison to imperative programming" section[1] offers a decent example of the paradigm, but DOM manipulation in JavaScript is pretty much as imperative as it gets. Having functions is necessary, but not sufficient, for functional programming.

[1] https://en.wikipedia.org/wiki/Functional_programming#Compari...

I hacked macOS 3 years ago

This wasn't obvious to me from the appearance of the page (I guess my screen is large enough that I didn't see the arrows in the bottom-right corner), but this site is actually a presentation. So, a heads-up in case anyone else has the same experience: the page is interactive, and you can navigate with arrow keys.

Not quite true, though while I was there, many fellow employees misunderstood the rules to mean that you couldn't use GPL software on your machine. At least as of a few years ago, the official ruling was that any open-source software _required_ for you to do your job had to be approved by an internal oversight group of sorts, and GPL and AGPL software was right out. You could, however, use any open-source software you wanted (including GPL and AGPL) so long as it was (1) for personal use, (2) not absolutely mandatory for you to do your job (e.g. some niche software or library propping up your employment), and (3) there was some other alternative tool that you could use if necessary.

So, for instance, a GPL-licensed git client like GitUp[1] was fine to use, and didn't require clearance. You could totally also install a newer version of Nano if you wanted, too.

But, the rules _were_ somewhat vague and scary-sounding, so many engineers I worked with took the rules to mean "absolutely no GPL software under any circumstances".

What email is actually talking about is the option to bundle Nano _with the OS_, which Apple can't do with GPLv3 software. That's why for years, for example, macOS has had an absolutely ancient version of bash (before the license was updated to GPLv3), and switched to zsh in newer versions of the OS.

[1] https://github.com/git-up/GitUp

Only a few weeks, so the experience is still pretty fresh. :) I do like it, though: dead-simple once the setup was in place, and zero ongoing maintenance so far.

My plan is to keep it entirely organic — so far have I've only really sent a few emails around the more meaningful-feeling milestones. (Arriving home, one week, etc.) I think, long-term, I'd prefer to have the amount of content not feel _overwhelming_ when I hand it off; I'm not planning on having this be the primary way to document my kid's life, but as something "extra" to be able to pass off to them.

[Also, being a few weeks in: I'd warn against making any plans for the first few weeks assuming you'll have much brain capacity available to you. I'd say, don't stress yourself out too much if you can avoid it.]

I recently found myself in exactly the same position for exactly the same reason, so happy to share my experience. :)

I decided a priori to stick with email, both because I can't imagine email going away anytime soon, and also because sending an email two main benefits: (1) the content is plain text, and should remain easily readable at the destination, and (2) you get automatic "backups" in your own Sent folder.

I also wanted to avoid any free services because there's no guarantee they'll stick around, or in the case of something like Gmail, keep the account around unused. And, although I'm a really happy paying Fastmail customer for many years, a new email address on my account felt a bit _too_ expensive for just this purpose (especially since I'll eventually pay for an actual address for them).

Because I already pay $5/month for a tiny Linode instance for other purposes, I decided to self-host. Because I don't plan on having the account send any emails (the hard part of self-hosting), setup was relatively pain-free with Postfix. It took a bit of configuration and testing, but I now have an email account for my little one hosted on a subdomain which reliably receives email and stores it in plain-text on the server. Because I set things up in the Maildir format, in the future, it should be simple to also run Dovecot to provide an IMAP interface to actually get at the emails, or import them elsewhere.

I ran into a bunch of pain points throughout the learning process here that I can share, but don't have a reason to believe you'll run into them too, so I don't want to overwhelm. More than happy to get into the specifics of any part of the process if you want!

Only intentionally, via setup from a hosting app. If an app uses a WKWebView to display web content, it can use WKUserContentController[1] to inject scripts and additional content into the page dynamically, and can inject functions into JS[2] which will trigger native callback handlers when called.

If your app uses the JavaScriptCore[3] framework to run JS in a VM in-process directly, you have even more options for interfacing between JS and native code.

Note that this has to be explicitly hooked up by the app (i.e., none of this applies within, say, Safari).

[1] https://developer.apple.com/documentation/webkit/wkuserconte...

[2] https://developer.apple.com/documentation/webkit/wkuserconte...

[3] https://developer.apple.com/documentation/javascriptcore

What sorts of improvements are you looking to make? What sorts of troubles are you having? Logic? Coherence? Sentence structure? Spelling/grammar? Finding your own style/voice, cadence, inspiration, etc.? Are you talking about literary writing (books/stories), persuasive writing (essays), technical writing, other forms? There are many different directions to take your question, so details could help!

Like many things, getting good at writing involves both a lot of reading (observing others who excel at their craft, and picking up on what makes their work stand out), and a lot of practice (whether "forced", e.g., at school, or on your own). Having a concrete understanding of where you feel lacking can help you focus on what to improve.

(For what it's worth, your writing here, at least, comes across as coherent, directed, spelled correctly, with good cadence and grammar — it sounds like you're probably looking for higher-level improvements; but I don't want to assume anything.)

I'm not sure if this is exactly what you're asking about, but submitting an app to the App Store currently performs pretty stringent checks on things the app does — including automated scanning for usage of private system APIs and ensuring that apps include appropriate reasons for asking for access to private data (contacts, photos, location data, etc.).

Without certain forms of review, it's much easier for apps to exploit weaknesses (whether in the OS, frameworks, the user, etc.), and I can't imagine that Meta would self-regulate any more than they are forced to now. Their apps and SDKs already hoover up as much data as the system will silently allow, but I'd rather not be forced to expand my device to them, if possible.

My gut feeling is that there's a nontrivial chance that the moment 3rd-party app stores are available on iOS, Meta will immediately move their apps to one to work around privacy limitations the App Store currently imposes on them.

I also strongly suspect that one of those apps would be WhatsApp, which is an app that a not-insignificant portion of the world uses to communicate. Unfortunately, I don't have a choice in what apps my family around the world uses, so I'm stuck with WhatsApp. The choice for me would either be to cut contact with dozens of family members, or enable the Meta App Store.

My hope is that if Apple is forced to allow 3rd-party app stores, they'll make it possible to even more strictly sandbox apps from those stores somehow.

It depends entirely on the size and quality of the candidate pool, but I'd say very roughly:

* Initial candidate screening reduces the pool by 85–95% (leaving 5–15% of the initial pool) * Interview #1 reduces the pool further by 50–66% (leaving ~3–4% of the initial pool) * Interview #2 reduces the pool further by another 66–75% (leaving ~1–2% of the initial pool) * Final chat usually doesn't reduce the pool, but it's one last pass for additional signal * We choose a single candidate of whoever remains

For a position with 400 applicants, it could look like

* Initial screening leaves 40 candidates for interview #1 * Interview #1 leaves 15 candidates for interview #2 * Interview #2 leaves 4 candidates for final chat * We pick from those final 4