HNHacker News
TopNewBestAskShowJobs

jasim

8,134 karma · joined May 1, 2010

https://protoship.io

https://twitter.com/jasim_ab

jasim.ab@gmail.com

submissionscomments
jasim··on Show HN: Virtual breadboard in the browser, inspired by Ben Eater's 6502
Tejotron, how did you build this? Is it Rust compiled to WebAssembly? The code editor is quite well-made. Is it completely hand-rolled? Curious to hear more!
jasim··on The values of Emacs, the Neovim revolution, and the VSCode gorilla
doom-emacs looks wonderful from the get go. https://github.com/hlissner/doom-emacs
jasim··on Payment processor for Amazon India had leak of over 100M users
As a counterpoint, Juspay's payments has always worked well for me, and the rotating logo personally inspires more trust than certain other wallet-cum-payment providers. I do all payments thru the web, and never on the phone, so that might apply.
jasim··on How I write Elm applications
To add to that, there is a profusion of fine-grained actions like SetFirstName, SetLastName is endemic in Redux-inspired codebases. They are a lot of noise, but more importantly, they force us to keep all our domain logic in a single massive reducer, with no encapsulation between different sections.

This can be improved by using coarse-grained actions like say `UpdatePerson(person)`. And this new `person` value can be obtained by the event handler itself when the user changes the name. However, the actual logic can sit inside a Person module, allowing us to do something like: `onChange=dispatch(UpdatePerson(Person.setFirstName(newName, currentPerson))`

This approach lets us keep the behaviour close to the view while encapsulating the actual implementation inside a rich domain model.

jasim··on Python overtakes Java to become the second-most popular programming language
But even static types (even the Hindley-Milner variety) won't be of much help in a team of juniors with no technical leadership. The codebase will end up in the same place - needing a rewrite - because even a good typing discipline cannot immediately substitute hard-won lessons in program design.

In fact I never thought I'd say this - for the first two years of discovering sum types, I thought they were the silver bullet of computing, and certain experiences have sadly tempered me there :)

I still agree that types _can_ make the flow and transformation of data in the system clearer than if there were no types. Though without experience, we could very well end up with convoluted designs and that can be as difficult to untangle as a dynamic spaghetti.

jasim··on Early Work
There are two places where it is possible to see early, "lame" work. One is YouTube - most channels with well-made videos would retain their earliest works, and the first few dozen can be quite instructive.

Second is GitHub - the first few hundred commits of many successful open-source projects. It is a wonder to see sprawling codebases starting at its first commit, and plodding its way over years before gathering momentum.

jasim··on The Gap: Where Machine Learning Education Falls Short
I've oscillated between these two positions for a few years now, when in truth neither positions are really in conflict.

When we complain about universities not preparing students better for jobs, what we really mean is that universities are not doing the bare minimum that they should be doing - in case of CS, students should at least know how to program well, and be well versed in the practicalities of computing. That does not exclude learning the fundamentals (which is often denigrated as "theory").

It is just that students often have neither the theory nor the practice, and at a minimum, we're asking, they should know the practice so they can at least be useful in their jobs.

jasim··on State of Self-Serve Website Building in 2020
Thanks, that works. It was erroring when I had tried last time.
jasim··on State of Self-Serve Website Building in 2020
I'm working on a beginners computing course, and of all the self-host options for HTML and CSS, the easiest one is glitch.com. You can drag and drop files, edit code right there in a nice environment, and also get a subdomain that you like. Surge and Netlify on the other hand requires Terminal knowledge and requires npm installed. Glitch can all be done on the browser.
jasim··on Exploring new forms of chess using artificial intelligence
Agglutinative grammar seems to be a difficult nut to crack, and I think Malayalam (my native tongue) is likely the most complex language in that sense. I happened to read "A quantitative analysis of the morphological complexity of Malayalam" (https://kavyamanohar.com/post/malayalam-morphological-comple...) which was published just a few days ago which describes some of the challenges, and has an interesting comparative study of various languages including Finnish and other Indian languages that are known for agglutination, inflection, and derivation from a common root.
jasim··on Algebra Driven Design
I'm naive, but I think the adoption of Typed FP, and the resulting cultural education and change, is one route to solving this problem. But it is a social and economic problem more than anything else. Or I guess - since the spectrum of competence in other fields of human endeavor is distributed widely (a bell curve?), we'll have to expect it to happen to software as well. Not a very optimistic thought, but at least we can be content with what we have :)
jasim··on Algebra Driven Design
Correctness and robustness happen to all software, when exposed to end-users for a long enough time.

Internally the software would be a ball of mud, a fragile patchwork of incoherent code that nobody wants to touch. A lot of these programs become immutable logs - if you want to change a feature, you add more code because you're afraid to touch anything that already exist.

Yet they are robust - all the bugs in the commonly traficked code paths have already been sussed out, and from outside the software looks like an impenetrable, rugged piece of craftsmanship. This has happened to me in the early years of my career (proud of the robustness, not proud of the code), and I've witnessed it so many times outside. So yeah - robustness and correctness is either a matter of correct-by-construction, or correct-over-time.

jasim··on Lunar – macOS utility to set brightness and volume on external monitors
Alongwith ddctrl (https://github.com/kfix/ddcctl), these aliases serve all my external monitor brightness needs:

    alias verydull="~/Software/ddcctl/ddcctl -d 1 -b 3 -c 15"
    alias dull="~/Software/ddcctl/ddcctl -d 1 -b 6 -c 35"
    alias decent="~/Software/ddcctl/ddcctl -d 1 -b 10 -c 40"
    alias medium="~/Software/ddcctl/ddcctl -d 1 -b 25 -c 50"
    alias bright="~/Software/ddcctl/ddcctl -d 1 -b 30 -c 50"
    alias morebright="~/Software/ddcctl/ddcctl -d 1 -b 35 -c 60"
    alias superbright="~/Software/ddcctl/ddcctl -d 1 -b 100 -c 80"
jasim··on Algebra Driven Design
This notion that functional programming is about correctness kept me away from static types and FP for the better part of my career.

I mean - who cares about correctness when I'm trying to ship yet another cookie-cutter web application that I don't even know is going to see more than a thousand users in its life.

What I cared about was speed. A few bugs in production is a better trade-off than all the mathematical mumbo-jumbo and slow, meticulous programming that Typed FP seemed to demand.

But to my surprise, after getting started with ReScript/Reason/OCaml, I recognized that correctness is just a side-effect (!) of this mode of programming. (Note that unlike Haskell, OCaml is an imperative programming language with as much or as little mutation as we need. We can almost line-by-line translate a regular mutation-heavy piece of Python or JavaScript code to OCaml, if we wanted to.)

Typed FP, contrary to what I'd came to expect, is all about speed. Quoting from something I wrote a while ago:

"Refactoring a typed FP program is safe, but menial. When we say safe - it means no anxiety. There is going to be tons of mechanical work for every major type refactor - going thru all the compiler errors and fixing them one by one. That can't be avoided. We've been in multi-day refactoring sessions where we had to dredge thru page after page of compiler errors before we could even run the application. But - it is safe - we know that once it compiles, there won't be any mistakes.

That gives us the freedom to build fast and loose - and take stock periodically before we have to abstract things out and tidy up the place. We should compulsively rely on that safety. Typed FP forces us to go slow in places where a dynamic environment would've allowed us to blaze through. For all that trouble, we get unmitigated refactoring dexterity and we must exploit it to benefit from the paradigm."

There are many places where a dynamic, imperative approach is faster than a typed FP approach. But there are as many or more places where it is the other way round. I'm now fastest with this way of programming than anything else, and I wish that more literature around Typed FP communicated this rather than go about the indirect route of correctness, and expect people to pick up that correctness-by-construction results in increased velocity. That's a difficult jump to make unless one has written non-trivial amount of code in that style.

This book by Sandy - I've read the introduction and skimmed thru the Tile construction equations - is to me poetry. I'm having trouble fitting in the idealistic notion of the Escher tile to my imperfect real-world domain, but otherwise, what a book!

jasim··on Tailwind CSS: From Side-Project Byproduct to Multi-Million Dollar Business
If you were building a themeable website, then you'd not be using bg:white; instead you'd use something like bg:main or something similar with a semantic meaning. This kind of customization is one of Tailwind's core workflow.
jasim··on How McKinsey helps companies avoid responsibility
Consultants do it; I have done it, and I have been in companies where we did it as a matter of policy.

Even people who started out by grabbing everything they can get learn to be selective with their clients later. This is just enlightened self-interest -- small companies can only work with a limited number of clients every year, and you want those projects to do well and the customers to be happy with you. This is simply because this is a word-of-mouth, relationships based industry. Every project that does not do well is an opportunity lost in building a healthy pipeline down the line.

This dynamic however goes out of the window as the company becomes successful and becomes brand-driven - customers reach out because of the aura of the company and not necessarily because a friend of a friend talked about how you once helped save their company from a cliff.

jasim··on Not a Wheelchair [video]
My feelings as well. But this one sentence made the book worth it - it is a commonplace thought that should've occurred to most others, but anyway was a clearer articulation than whatever I'd had:

"I'm suspicious of any plan to fix unfairness that starts with 'step one, dismantle the entire system and replace it with a better one,' especially if you can't do anything else until step one is done. Of all the ways that people kid themselves into doing nothing, that one is the most self-serving."

jasim··on Discovering Dennis Ritchie’s Lost Dissertation
I do that a lot with Reason/OCaml. It is surprising how powerful imperative, mutable programming is when most of the code you write is functional, and then you want do something a little difficult in the paradigm, and you make a `ref`, write a while loop, mutate an array, or store stuff in a global variable, and it is done! Mutation in OCaml is like a fine scalpel - extremely powerful, occasionally used, and just right on the table whenever you need it.
jasim··on The Early History of F# [pdf]
Encapsulation aided by types and module interfaces.

In Reason/OCaml, you can create a module interface (.rei or .mli) which makes the type opaque to functions outside the module. So instead of saying `type t('a) = list('a)`, it'll just say `type t`.

The compiler relies on this type information to decide whether functions are well-typed. So if an external module presumes to know the structure of the type and does an operation on it, it becomes a type error because the compiler simply doesn't know about the actual type. This is similar to encapsulation in OO where we don't expose a field to the outside world, but here it is done by virtue of the type signature itself, which I found to be a more powerful guarantee of encapsulation.

The module can then expose functions like `append` which would be the only way you can manipulate the list. This function in-turn can ensure the postcondition, guaranteeing that the list stays sorted. At the point in which we want to use the list for functions not supported by our SortedList module, we can turn it into a regular list with a `to_list` function. Since the underlying type is already a list, the operation is virtually free. The function would look like `let to_list = (xs: t('a)) => (xs: list('a))`. It is an identity function, with just the type changed for the compiler.

Similar rules about `append` applies to the constructor: we can create a new `SortedList` only with a `SortedList.make` which can ensure the postcondition. There will be no other way, thanks to the type being hidden, to create a value of the `SortedList.t` type.

jasim··on Design Principles Behind Smalltalk (1981)
If you're tired of hearing about the virtues of Smalltalk (like I was at one point) and ask "if it is so good, why isn't it popular?", then watch @deech's talk (Aditya Siram) - "What FP can learn from SmallTalk" - https://www.youtube.com/watch?v=baxtyeFVn3w

I think it is the most accessible explanation of the marvel of Smalltalk, for those who were not lucky to work with it during the late 80-90s.

Also I think Ruby is the mainstream language that is closest to Smalltalk today, with the idea that everything is an object and late-binding as much as possible.

One thing I can't help point out is that Smalltalk / Alan Kay's vision of interconnected objects forming a recursive computing system is not the only "true" vision of OO out there. From Simula thru C++ and then Java and C# also are object-oriented (or class-oriented for those who care about the distinction), but they are statically typed. It is also OO because OO is a word, not a mathematical definition, and when people talk about OO, there is enough similarity between all these variations that we consider all of them to be in the same camp. Plus with the growing move towards strict static typing even in interpreted languages, which I think is driven by actual industry needs and programmer preferences than external marketing, it is becoming a disservice to think of statically typed OO languages as being somehow inferior to the true ideal of object-oriented programming.

jasim··on We can no longer ignore the potential of psychedelic drugs to treat depression
There is _something_ that one can discover when they try psychedelics. It is a non-normal experience and so by definition will have novel data. Is it useful though? So many people couldn't have been flatly wrong (but they said that about religion) - so there could be something to it. One of that is the realization that our sensory inputs, how we process them, and also how we understand ourselves, the world, and the separateness of the self from everything around us - are not preordained. The way we look at all of them is not the only way. We think the way we think because we've evolved to it. What we see is not the objective reality; it is just a filter through which we perceive it. This experience can allow someone who's prepared for it, to break out of their own patterned ways of thinking.

But - there is a reason we've evolved to perceive reality in this way. It allows us to survive. Material concerns - health, wealth, status etc. and extractive consumption which messes up "nature", and having ego/identity distinct from nature -- all of this serve useful, vital purposes. A person who's constantly tripping will find it difficult to survive both in modern and ancient times long enough to propagate their genes.

People search for profundity in these experiences, and sure there are some. But to really understand the world and nature, I think the lens of science - observation, empiricism, and the scientific process is a better bet. There unfortunately really isn't anything _deeper_ about life. And whatever there is, personal introspection aided by psychedelics cannot hold a candle against organized human scientific endeavor.

Also, messing with one's brain is dangerous. It is one of the least understood human organs, irreplaceable, and fundamental to life like few other things.

jasim··on Ruby vs. Crystal Performance
https://cs.brown.edu/~sk/Publications/Papers/Published/kf-pr...

Programming Paradigms and Beyond, Shriram Krishnamurthi and Kathi Fisler:

OO is a widely-used term chock-full of ambiguity. At its foundation, OO depends on objects, which are values that combine data and procedures. The data are usually hidden (“encapsulated”) from the outside world and accessible only to those procedures. These procedures have one special argument, whose hidden data they can access, and are hence called methods, which are invoked through dynamic dispatch. This muchseems to be common to all OO languages, but beyond this they differ widely:

* Most OO languages have one distinguished object that methods depend on, but some instead have multimethods, which can dispatch on many objects at a time.

* Some OO languages have a notion of a class, which is a template for making objects. In these languages, it is vital for programmers to understand the class-object distinction, and many students struggle with it (Eckerdal & Thune, 2005). However, many languages considered OO do nothave classes. The presence or absence of classes leads to very different programming patterns.

* Most OO languages have a notion of inheritance, wherein an object can refer to some other entity to provide default behavior. However, there are huge variationsin inheritance: is the other entity a class or another (prototypical) object? Can it refer to only one entity (single-inheritance) or to many (multiple-inheritance), and if the latter, how are ambiguities resolved? Is what it refers to fixed or can it change as the program runs?

* Some OO languages have types, and the role of types in determining program behavior can be subtle and can vary quite a bit across languages.

* Even though many OO aficionados take it as a given that objects should be built atop imperative state, it is not clear that one of the creators of OO, Alan Kay, intended that: “the small scale [motivation for OOP] was to find a more flexible version of assignment, and then to try to eliminate it altogether”; “[g]enerally, we don’t want the programmer to be messing around with state” (Kay, 1993).

In general, all these variations in behavior tend to get grouped together as OO, even though they lead to significantly different language designs and corresponding behaviors, and are not even exclusive to it (e.g., functional closures also encapsulate data). Thus, a phrase like “objects-first” (sec. 6.1)can in principle mean dozens of wildly different curricular structures, though in practice it seems to refers to curricula built around objects as found in Java.

jasim··on Ruby vs. Crystal Performance
There has never been a "true" OO language. And if it there was, Smalltalk was not it. Alan Kay did coin the term, but Simula existed long before Smalltalk. The tree of languages that include C++, Java, and C# can be traced back to Simula while Smalltalk inspired Ruby. There is a distinct camp of "statically typed OO" (Simula and its children) and "dynamically typed OO" (Smalltalk and its children).

Yet none of this is the one true OO. All of it remains a way of describing a human mode of expression, and so is rightly subjective.

jasim··on Frameworkless Movement
It is necessary to have a fundamental understanding, and often frameworks, as you said, can cast us to a single shape, making it difficult to see how else the problem could be solved.

But life is too short to always start at first-principles and rebuild civilization brick by brick.

Frameworks have three things: the code artifact: a Rails or a React, then the reified knowledge it abstracts: like how to structure a web application, and the community and their cultural knowledge.

Sometimes it makes sense to forego all of them and make it your own - especially when that layer of abstraction is core to the value you're creating and you want complete creative control over it. But often times they can be the difference between business success and failure.

jasim··on Most book clubs are doing it wrong (2017)
It makes me sad to read that statement - strangers are insulting James Somers (the original author) simply because I posted one of his articles in this forum.

He's written articles like https://www.newyorker.com/magazine/2018/12/10/the-friendship... which was one of the best pieces of writing on technology and humans that I've read recently. This is not the work of a recluse (the undesirable negative connotations aside).

jasim··on Security Flaws in Adobe Acrobat Reader Allow Gaining Root on macOS Silently
The "Folder Location" option determines the parent directory where the "Creative Cloud Files" folder is stored, which is the actual sync folder. You can verify this by creating an empty directory and moving the sync location there. Bad, alarming UI though.
jasim··on For Donald Knuth, good coding is synonymous with beautiful expression
After Michelangelo died, someone found in his studio a piece of paper on which he had written a note to his apprentice, in the handwriting of his old age: “Draw, Antonio, draw, Antonio, draw and do not waste time.”

(The Writing Life by Annie Dillard)

jasim··on How UI-driven state increases accidental complexity
Don't model data based on the component hierarchy, but model it in the best possible way. But the "best" approach depends - the same trade-offs as in database schema design applies here.

If most operations require data joins then pick a de-normalized structure. Then most data is often available with a single hash lookup. Though this makes updates difficult and mistakes can and often do lead to inconsistent data.

A normalized data-structure which use ids to refer to related elements makes it easy to add, update, and delete entities; but reads might require multiple joins which in turn makes the code complex.

We can choose between the two by listing out the possible operations and deciding which cases are more frequent. But this is often a moving target in a growing application.

The best approach I've so far found is to design data structures in a way that "invalid states are impossible". I will not link to Yaron Minsky and Richard Feldman's excellent talks on this here. This principle often results in elegant data structures that would've eluded me otherwise.

The second addition that is necessary is to use a statically typed language. Elm, Reason, and PureScript are the only choices in front-end at the moment because of soundness, sum/union types, and exhaustive pattern matching. A typed functional language makes refactoring an easy, mechanical, and reliable process which suddenly makes our code much more malleable and hospitable than before.

jasim··on Tailwind UI
> On a side note, if some bored person out there wants to make a huge splash in the CSS community, figuring out a way to target a specific DOM element, create the equivalent of an AST and specify a "dictionary", which would be a utility-css framework (tailwind, tachyons, basecss, etc) and finally reimplement the targeted DOM element in the chosen framework would be amazing.

This is quite doable for us at Protoship (we've built both a design-to-Tailwind CSS+HTML converter as well as a webpage-to-Sketch Chrome extension). It is a very appealing idea - to be able to recast any webpage into a Utility CSS framework, but I'm curious to hear about situations where it would've been useful in commercial work.

jasim··on The general value of typed functional programming lies in leaving no edge cases
This is a wrong and surface-level reading of the original post. If there are "cases" in a system, then there are going to be "edge-cases" as well. The problem is in classifying certain cases as "edge" and others as regular. We do this because the regular cases are very obvious and are either the perfect happy-path, or a failure condition that we would immediately consider.

But between these two exist a permutation of conditions that exists in the domain and often manifest in production. Either we can actively manage them thanks to types, or we can let it end up in production with hard-to-track bugs.

← PreviousPage 3 of 17Next →