HN user

implicit

420 karma
Posts2
Comments96
View on HN

Some older PS1 games didn't work with the Dual Shock in analogue mode. You needed to press the button to disable the analogue sticks to play those games.

Sure. I don't mean to say that imperative programming is going anywhere.

If you're looking for programming languages with no support for imperative programming, Excel is pretty much it. Even Haskell has robust support for sequencing actions. If it didn't, I don't think we'd be talking about it at all.

What I predict is that ideas from FP will continue to bubble up into the mainstream. Like prior inventions, they won't be presented as a thing that asks you to rework all of your algorithmic code. They will instead be polished and presented in a way that makes them work more as extensions to what you can already do.

If you squint a little bit, Haskell's do-notation is already available in mainstream languages in the form of async/await syntax. async/await is not quite as general as the original Haskell solution, but it also doesn't ask you to completely rethink the way you design algorithms.

I agree. It's happening.

Lambdas are now a requirement for any modern programming language. Monadic promises are how asynchronous programming is done. Rust is basically OCaml except with typeclasses and without a GC.

Inch by inch, ideas from FP bubble up into mainstream programming.

I predict that algebraic effects (a la Koka) are next, but it'll be a while before the industry is ready for it.

Richard has a whole series of these.

I was going to try to pick out one of my favourites from this series, but I really can't. Every last one is a treasure.

EDIT: Oops! This is based on Aphyr's work. My bad!

Luau already has some notion of nominal types to deal with the fact that the Roblox API is basically a ton of C++ classes that have been mapped over via the FFI. Some of these types are totally structurally identical and yet incompatible.

We'd like to afford the same kind of ideas for native Luau code, but we're not there yet.

There is a transpiler in the source tree, but I expect it needs a polish pass. We haven't put it to use in quite some time.

In particular, I am pretty sure that it won't do the right thing when it encounters the extra syntax we've added to Luau. So far, that's an if-else expression and a continue keyword. Transpiling those accurately will take a bit of work.

I did a little bit of GBA homebrew development back in its heyday. It is a really fun little bit of hardware to hack on.

It's powerful enough that you can write your whole game in C++, but not so powerful that you should rely on the system allocator for anything.

It's in a lovely sweet spot for a particular dialect of zero-overhead C++ that's really fun to write.

As a simple example, I had a little class library of data types that were crafted to exactly map to the hardware registers of the GBA. The whole thing inlined away to nothing.

The right balance here can depend on the specific business you're working in.

Some companies earn the privilege of a super tenured core team of engineers who work on their product for an extended period of time. They will choose different tradeoffs from a team that needs to adapt to higher turnover.

I understand what you're saying, but something I have noticed over the years is that the amount of code that I can make sense of at any given time is actually proportional to what I can see onscreen at any given moment.

I have had pretty good luck cheating this in a bunch of ways: I use a small font, a big display, and I use a terse programming style.

Once you internalize a bunch of common higher-order functions, you learn how to draw a ton of meaning out of a relatively small number of terms.

I switched to the Dvorak keyboard layout sometime in the early 00s.

I've never owned a keyboard that was actually physically labelled for Dvorak, so I _cannot_ type if I can see my fingers. The lies printed on the keycaps throw me off every time. :)

Most modern C++ code that I've seen restricts code to declaring a single variable per statement.

It's not really a big deal because things are also always introduced at the latest possible position. Each is also typically given an initializer. I'd consider it suspicious if I were to see C++ that declared 3 uninitialized pointers back to back like this.

And then you get to the codebases where the authors have chosen to embrace auto and type inference... :)

Hello! I am the lead developer of the Luau type checker.

I actually hadn't heard of Teal! I don't think it existed when we started.

Judging from the Teal documentation, I'd venture that it looks pretty similar to Luau in everyday use.

If I were to guess, I'd venture that the biggest differences are probably the kind of type inference we do (Luau's inference engine draws inspiration in equal parts from OCaml and TypeScript), a bunch of Roblox-specific features, and that our inference engine is all C++ for performance.

I don't think many people would enjoy that at all.

The appeal of games like Dark Souls (and Slay the Spire!) at high difficulties arises from the knowledge that you _will_ win if you are clever and observant enough.

It's the learning process that keeps players coming back to these games, not the Game Over screen.

I did a bunch of professional Haskell work in a prior life[1]. Most of its 'secret weapon' status springs from the way Haskell lets you control side effects.

We had a fantastic unit testing harness[2]. With effect tracking, you can arrange for your harness to put precise fences around nondeterministic code. You don't need to rely on experience and discipline to ensure that tests are reliable or fast. The type system does it for you.

Effect tracking also makes concurrent code easier to write. You have a great deal of control over how and when threads can be created, and what kinds of logic is permitted on those threads.

GHC's IO manager offers green threads and async IO, so rather than a select loop, you just fork threads and perform "blocking" IO calls. You get about the same efficiency as a select loop this way.

Lastly, something we learned to appreciate over the years is that Haskell is easier to refactor. When you combine the rigid type system, effect tracking system, and ease of writing unit tests, it becomes very easy to refactor old Haskell. It doesn't really matter how bad the old code was or whether the original author is around to help you at all.

If you intend for your product to evolve continuously forever, this is truly a secret weapon.

    [1] https://andyfriesen.com/2014/03/25/what-its-like-to-use-haskell.html
    [2] https://andyfriesen.com/2015/06/17/testable-io-in-haskell.html

If you are going to expect me to run your software in an always-on manner, I would greatly appreciate a native application.

I frequently do light computing on a Surface Go. It's a delightful little device and I love it, but it is not powerful enough that I can leave gmail, Slack, and Discord open all the time.

I don't have enough RAM to run another web application but I could very easily afford a native app or two.

The properties you describe regarding pure functions are correct and useful, but those ideas miss the point:

Neither OO nor pure FP are required to build high-quality software!

OCaml developers have been living in this world for decades now and they seem to get by just fine. Further, they do this purely by choice! Their language has had OO syntax for decades and they are happy ignoring it!

The problem is the way the author instantly jumps from "98% pure functional programming doesn't work" to "you should use OOP."

In order to bridge the gap, you have to make some pretty terrible assumptions:

If your code is not OO, it must be FP.

If your code is FP, it must be pure. Therefore,

If your code cannot be pure, it must be OO.

The author seems to be implying that we have a hard choice ahead of us with no middleground: We can either accept object oriented programming, or we can turn to pure FP.

The OCaml community presents a pretty compelling third option:

OCaml is billed as a 'functional language,' but it doesn't do anything to prevent you from performing mutations or executing side effects anywhere you want. It even has builtin syntax for "for" and "while" loops.

Interestingly, OCaml does afford classes and objects but hardly anyone seems to use them. It's not that OCaml objects are weird or difficult or bad in some way. People just choose to write records and functions. Some of those records have functions in them. Some of the functions mutate state.

In the OCaml world, at least, pretending OOP never happened seems to have worked out just fine.

Firefox Replay 7 years ago

Nondeterminism is every place where information can enter your program from the outside world.

It's random numbers, the system clock, data off the internet, and input events from the user.

What it sounds like is that Mozilla has come up with a debugging environment that bottles all that nondeterminism up into a neat little package so that it can replay a specific run of your program any time you want.

For what it's worth, I get the feeling that the reason TypeScript is growing so much complexity is not because the language designers expect you personally to use it all.

It's there primarily so that your development environment can give you accurate feedback when you're using your favourite JS libraries.

This happens less often than you might expect.

If you're able to run the code locally, there is a function called Debug.Trace.trace which writes messages to stdout without requiring IO.

If it's a production issue, all you need to do is figure out what is being passed to your function. It's a pure function, so you can always debug it locally once you've got that.

I've done commercial Haskell work and it was fantastic. Almost all of the things you mention are problems with technical leadership and not Haskell itself. These problems are all very, very easy to deal with.

1) Many of us have also seen the C++ library or application that busts out the template metaprograms at every opportunity. Haskell is the same. Use restraint when bringing in new libraries and programming techniques. (as an aside, I have yet to encounter a single problem that I thought merited the use of arrows)

2) You also see this in other languages, though to a lesser degree. The same techniques we use with other languages work just as well in Haskell: As leadership, form a strong opinion on the best way to write control flow and ensure that everything uses a consistent style.

3) Are your engineers running around implementing random research papers? This has nothing to do with Haskell.

4) In practice, this is almost never a big problem. Refactoring a pure function to an effectful function is easy and safe. In a production setting, almost all of your functions run in some monad already, so it's pretty rare to run into a situation where you suddenly need to percolate effects through many functions.

I think you have to use government to force Apple to trust these other software vendors.

It's not so difficult if your MacBook trusts Apple, Google, Amazon, and Microsoft out of the box.

I've done a lot of production Haskell and I've had a similar experience.

In our case, we dealt with it by keeping relatively bare, boring code. We avoided point-free style, crazy combinators like lenses, and complex monad transformer stacks except in the 'plumbing' part of the application that didn't need to change very much.

This paid off in spades as we had a lot of engineers who only ever had to work in the 'porcelain' parts of the application. They got a lot of great work done using abstractions that matched their intuition exactly.