3 ms·
From the article: "For instance, to actually encode a Cap’n Proto message, I couldn’t just allocate a buffer of zeros and then go through each field and set its
by rybosome 13y ago
From the article: "For instance, to actually encode a Cap’n Proto message, I couldn’t just allocate a buffer of zeros and then go through each field and set its value. Instead, I had to compute all the field values first, sort them by position, then concatenate the results."
Sounds reasonable to me, but I think it's just personal preference. Without actually seeing the code (which I'm sure makes a huge difference), the imperative algorithm actually doesn't read as easily as the functional one does to me. We've become accustomed to imperative since it's what we all learned on, but there is a massive amount of book-keeping (although I'm sure the execution speed is much faster). At any rate, here is Scala with imaginary API's detailing how I would envision the functional algorithm...
def encodeFields(fields: List[Field]): String =
fields.map { f => f.position -> f.computeValue }.sortBy { _._1 }.map { _._2 }.mkString
- kentonv 13y agoIn terms of code complexity, I honestly can't decide whether the difference is objective or subjective. The functional version sure feels complex to me, but yeah, it may just be because that's not what I'm used to. That said, given my knowledge of how the hardware actually works, the purely-functional approach makes my efficiency sense tingle. A lot. Of course, whether or not it's a good idea to scratch it is something we've been debating for decades...
- rybosome 13y agoThis is kind of a strawman, but if you were to use a similar pseudo-API, what would the imperative code look like? Very fair point about efficiency; for all my love of the FP style, I'll never claim it to be faster or leaner w.r.t memory usage. I'd be curious to see if the simple parallelization made possible by the functional approach could close that performance gap, though. ...and I'm almost positive the difference is subjective. =) Reading over your website, it's clear that you are a very talented engineer, especially in areas where C++ shines. Given this plus your experience at Google developing Protobuf, I'd hazard a guess that you have a significant amount of experience writing OO or imperative code, and probably somewhat less writing in the functional style.
- DannoHung 13y agoUnfortunately, non-strictness is the biggest hindrance to understanding operational semantics of Haskell. To my knowledge, people using Haskell in serious production use strictness annotations like there's no tomorrow.
- enigmo 13y agoI did a simple survey of our codebase... as I think we meet your requirements for serious production Haskell. Here's the terribly inaccurate and probably misleading results: Raw line count: destroyer:alphaHeavy$ find . -name "*.hs" | xargs wc -l | tail -1 132272 total Things that look like bang patterns or strict fields: destroyer:alphaHeavy$ grep "\!" -rI . --include "*.hs" | egrep -v "\\$\!|\.\!|\!\?|\!\!" | wc -l 1344 Strict returns: destroyer:alphaHeavy$ grep "\\$\!" -rI . --include "*.hs" | wc -l 762 Unboxed fields: destroyer:alphaHeavy$ grep UNPACK -rI . --include "*.hs" | wc -l 298
- Peaker 13y agoStrict fields in records are probably a better default than lazy fields. So you indeed see lots of strictness annotations in data structures. It also helps with unpacking/unboxing. However, strictness annotations in functions is a relatively rare thing.
- Peaker 13y agoTo do it the way he described (buffer of zeros + fill): import qualified Data.Vector.Unboxed.Mutable as V encode len fields = do buffer <- V.replicate len 0 forM_ fields $ \(fieldPos, fieldData) -> V.copy (V.slice fieldPos (length fieldData) buffer) fieldData return buffer