494 karma · joined May 17, 2023
[1]: https://dl.acm.org/doi/epdf/10.1145/1411203.1411220
* Monoids: Theme and variations (functional pearl): http://ozark.hendrix.edu/~yorgey/pub/monoid-pearl.pdf
A major supermarket chain in Australia (Coles) is literally a client of Palantir.
https://investors.palantir.com/news-details/2024/Palantir-Pa...
https://github.com/ESWAT/john-carmack-plan-archive/blob/mast...
Perhaps they are useful to you. If you need dependency bounds relaxed or revised to work on modern GHCs, let me know and I'll take a look.
https://forceflow.be/2013/10/07/morton-encodingdecoding-thro...
Here's an example from AWS, where lat/long pairs are put into a Z-index, which is used as a DynamoDB sort key, letting you efficiently query for items near a point.
https://aws.amazon.com/blogs/database/z-order-indexing-for-m...
The `IO` type is a type constructor of one argument (a type), and returns a type: we say that it has kind `Type -> Type`, using the word "kind" to mean something like "the 'type' of a type". (I would also think of the Zig function `std.ArrayList` as a type constructor, in case that's correct and useful to you.) `IO String` is the type of a potentially side-effecting computation that produces a `String`, which can be fed to other `IO`-using functions. `readLine` is an example of a value that has this type.
The Haskell function arrow `(->)` is also a type constructor, but of two arguments. If you provide `(->)` with two types `a` and `b`, you get the type of functions from `a` to `b`:
`(->)` has kind `Type -> Type -> Type`.
`(->) Char` has kind `Type -> Type`.
`(->) Char Bool` has kind `Type`. It is more often written `Char -> Bool`. `isUpper` is an example of a value that has this type.
The partially-applied type constructor `(->) r`, read as the "type constructor for functions that accept `r`", is of the same kind as `IO`: `Type -> Type`. It also turns out that you can implement the functions required by the monad interface for `(->) r` in a way that satisfies the necessary conditions to call it a monad, and this is often called the "reader monad". Using the monad interface with this type constructor results in code that "automatically" passes a value to the first argument of functions being used in the computation. This sometimes gets used to pass around a configuration structure between a number of functions, without having to write that plumbing by hand. Using the monad interface with the `IO` type results in the construction of larger side-effecting computations. There are many other monads, and the payoff of naming the "monad" concept in a language like Haskell is that you can write functions which work over values in _any_ monad, regardless of which specific one it is.
I tried to keep this brief-ish but I wasn't sure which parts needed explanation, and I didn't want to pull on all the threads and make a giant essay that nobody will read. I hope it's useful to you. If you want clarification, please let me know.
It definitely won't, which is what I was trying to get at with the discussion of monads and data dependencies. Applicatives by definition cannot have one "effectful" computation depend on the result of another. You could do a large bunch of parallel work until you need to pass a result into a function that decides what additional work to perform, at which point you need a monad. More advanced frameworks like Haxl apparently make this distinction explicit, so your computation proceeds as a sequence of batched parallel options, combining as much work as possible.
Other programming traditions also have this pattern of inventing minilanguages and interpreters for them; regular expressions are by far the most successful example. You could hand-roll string matching by writing your matching functions by hand, but it's often much easier to ask a regex library to run an interpreter over a string describing the pattern to match.
In Haskell, it's really cheap to invent data structures, so using the same language to describe the work is quite convenient. Laziness also means you almost never materialise the entire intermediate "work to be done" structure - you build little bits as the interpreter demands them. So it doesn't feel as heavyweight as an eDSL in some other language.
> The key insight of the Hypothesis library is that instead of shrinking generated values, we instead shrink the samples produced by the PRNG.
Hedgehog loses shrink information when you do a monadic bind (Gen a -> (a -> Gen b) -> Gen b). Hypothesis parses values out of the stream of data generated by the PRNG, so when it "binds", you are still just consuming off that stream of random numbers, and you can shrink the stream to shrink the generated values.
Here is a talk that applies the Hypothesis idea to test C++: https://www.youtube.com/watch?v=C6joICx1XMY . Discussion of PBT implementation approaches begins at 6:30.
> It gets weirder: in Haskell, exceptions can be thrown to other threads!
What's really interesting is that because of purity, you have to have asynchronous exceptions otherwise you give up a lot of modularity. At least that's what Simons Marlow and Peyton Jones argue in Asynchronous Exceptions in Haskell (2006): https://www.microsoft.com/en-us/research/wp-content/uploads/...
> While the semi-asynchronous approach avoids breaking synchronization abstractions, it is non-modular in that the target code must be written to use the signalling mechanism. Worse still (for us), the semi-asynchronous approach is simply incompatible with a purely-functional language, such as Concurrent Haskell. The problem is that polling a global flag is not a functional operation, yet in a Concurrent Haskell program, most of the time is spent in purely-functional code. On the other hand, since there is absolutely no problem with abandoning a purely-functional computation at any point, asynchronous exceptions are safe in a functional setting. In short, in a functional setting, fully-asynchronous exceptions are both necessary and safe — whereas in an imperative context fully-asynchronous exceptions are not the only solution and are unsafe.
If you can read PLTese, it's really quite a nice paper.
If you think of Monads in terms of `fmap` + `join :: Monad m => m (m a) -> m a`, then you don't need to imagine an "extraction" step and your intuition is correct across more instances. Understanding `join` gives you an intuition that works for all the monads I can think of, whereas the container intuition only works for `Maybe` or `Either e` (not even `[]`, even though it _is_ a container). You can define each of `>>=`/`join`/`>=>` in terms of `pure` + any of the other two, and it is an illuminating exercise to do so. (That `class Monad` defines `>>=` as its method is mostly due to technical GHC reasons rather than anything mechanical.)
As for text size, the CSS expresses no opinion: I'm expecting your user agent to act in your best interest.
Do you have any suggestions?
I hear that class/skill variety was often greater on LPMuds because it was so much easier to code up custom behaviour. Do you know of any especially good articles on LPMuds class (in the OO sense)/object hierarchies and the software architecture of mudlibs?