Hacker News new | ask | show | jobs
by ukj 1820 days ago
This is a trivial and obvious implication of Turing completeness. Why do you even bother making the point?

With the right amount of indirection/abstraction you can implement everything in Assembly.

But you don't. Because you like all the heavy lifting the language does for you.

First Class citizens is what we are actually interested in when we talk about programming language paradigm-choices.

https://en.wikipedia.org/wiki/First-class_citizen

3 comments

I mean, GP said "you can't have routers" and maybe I'm being dense by interpreting that as "never or almost never," but even with a generous "too hard to be practical," I still don't think it's correct.

And I explicitly said "escape hatch" meaning language feature. You don't need that much indirection to get routers in Haskell, Rust, Go, C, C++... like I fail to see how implementing routers are a barrier in strict type system languages.

Is it easier in python or js? Sure. can't? hardly.

E: here's some vtable dispatch (unless that doesn't count as "dynamic dispatch") in Rust. Looks really straightforward.

https://doc.rust-lang.org/1.8.0/book/trait-objects.html

What C++, Haskell, Rust, Go etc. call "dynamic dispatch" is what a router calls "static routing". The defining characteristic is that the lookup table is pre-determined and immutable at runtime.

What routers call "dynamic routing" is having the lookup table mutable at runtime.

There can be no equivalent to that in type-safe languages because when you mutate the dispatch table you lose type-safety.

> What routers call "dynamic routing" is having the lookup table mutable at runtime.

> There can be no equivalent to that in type-safe languages because when you mutate the dispatch table you lose type-safety.

That's exactly what I mean by escape hatches. Rust has unsafe. You can write a WTF FastInverseSqrt in Rust. You shouldn't 99% of the time. But you can.

Rust also has Box, Rc, Arc, and other tools. I'm not fluent enough in rust to know how to, but I'm quite confident and will eat my hat if you can't accomplish what is effectively a mutable vtable dispatch in Rust.

But also...why? Yeeting a function call into the void without any type knowledge seems way more bug-prone than interface/trait-based dispatch, to what benefit? Save developer time? I've spent countless dev-days of my life I won't get back debugging exactly this kind of dynamic-dispatched, json-dsl, frankly loosey-goosey untyped bullshit. There's so many better ways.

Did you mean something other than "dynamic dispatch", or what do you mean by "first class support"?

No offense, but your claim sounds like you're confused to me, but maybe I am the one confused. AFAIK I do dynamic dispatch all the time in strongly typed languages. Can you show an example that a strongly typed language can't accomplish?

>I do dynamic dispatch all the time in strongly typed languages.

I believe semantics is getting in our way of communicating.

You don't do dynamic dispatch ___ALL___ the time. You only do it at runtime. And you only do static type safety at compile time. Those are different times.

You can't have both of those features at the __SAME TME__, therefore you can't have both features ALL the time. They are mutually exclusive.

"All the time" is an english expression, please, look it up before going all-caps Python name mangle convention on me.

Yes, of course dynamic dispatch is a runtime phenomenom, that's the dynamic part of it. But there's nothing stopping the code that performs dynamic dispatch from being strongly typed. Strong types are instructions used to prove that the code holds certain properties, they are a separate program from the final binary the compiler gives you. Do you also think that code that gets unit tested can't perform dynamic dispatch?

If your point is that "types don't exist at runtime anyway" (reflection aside) then you don't understand what the purpose of a type system is, nor what strongly typed code means.

> "All the time" is an english expression, please, look it up before going all-caps Python name mangle convention on me.

I know reaction comments are discouraged on HN, but this had me in stitches. Top-tier gourmet dig right there.

There is something stopping you from running the proof against your implementation!

Proof-checking happens only at compile time.

The implementation that you want to prove things about is only available at runtime!

Non-availability (incompleteness) of information is what is preventing you…

Dynamic dispatch is literally a runtime feature of a language. That's why it's called "dynamic". GP can absolutely use dynamic dispatch "all the time" in the sense that they use it regularly in their program, perhaps in every or nearly every program they write.

Your statement is verging on the nonsensical, like saying to someone "You don't use integers all the time, sometimes you use strings, they're different things." Well, duh?

EDIT: Also, it's discouraged on this site in general to use all caps for emphasis. *phrase* produces an italicized/emphasized form with phrase.

I am using a perfectly sensible notion of "dynamic" (e.g NOT static) when I am talking about "dynamic dispatch".

Registering pointers to new implementations at runtime (adding entries to the dispatch table) Unregistering pointers to old implementations at runtime (removing entries from the dispatch table).

If your dispatch table is immutable (e.g static!), there's nothing dynamic about your dispatch!

> Why do you even bother making the point?

Maybe because you said:

> they prevent you from implementing dynamic dispatch.

and

> Routers. You can't have routers.

Which just isn't true. You can implement dynamic dispatch and you can have routers, but they come at a cost (either of complex code or of giving up compile-time type safety, but in a dynamic language you don't have the latter anyway, so with a static language you can at least choose when you're willing to pay the price).

> First Class citizens is what we are actually interested in when we talk about programming language paradigm-choices.

But that's not what you said in your other comment. You just said you can't have these things, not they're not first class citizens. Besides, some static languages do have first class support for more dynamic features. C++ has tools like std::variant and std::any in its standard library for times you want some more dynamism and are willing to pay the tradeoffs. In Java you have Object. In other static languages, you have other built-in tools.

Everything comes at a cost of something in computation!

That is what “trade offs” means.

You can have any feature in any language once you undermine the default constraints of your language. You can implement Scala in Brainfuck. Turing completeness guarantees it!

But this is not the sort of discourse we care about in practice.

https://en.wikipedia.org/wiki/Brainfuck

Yes, and? How is that relevant here?

You said "you can't", kortex said "you can" and then you moved the goal posts to "you can because of turing completeness, but its bad, Why do you even bother making the point?" to which I replied "because its a valid response to you're `you can't`" now you moved them again to "everything comes at a cost" (which... I also said?).

Of course everything comes at a cost and yes, that's what "trade off" means. Dynamic languages come at a cost too (type checking is deferred to run time). So, this time, let me ask you: Why do you even bother making the point?

Tractability vs possibility.

You don't grok the difference.

You can implement EVERYTHING in Brainfuck. Tractability is the reason you don't.

The goalposts are exactly where I set them. With my first comment.

"Every programming paradigm is a good idea if the respective trade-offs are acceptable to you."

Its perfectly tractable though. Just because you don't understand it or don't think it is, doesn't make it true.

> "Every programming paradigm is a good idea if the respective trade-offs are acceptable to you."

That's not what we are responding to. Nobody here is arguing over this statement. We are responding to you assertion that static typed compile-time checked languages _prevent_ you from having dynamic dispatch and that you _can't have_ routers because of that. Neither of which are true.

Dynamic languages prevent you from having compile time checks. Does that make them bad? Static languages give you compile time safety, but if you're willing to forego that [1], then you can get the EXACT SAME behavior as dynamic languages give you.

You literally said:

    For example, one good reason why strong static types are a bad idea... they prevent you from implementing dynamic dispatch.

    Routers. You can't have routers.
Nowhere did you say anything about trying to implement it at compile time. Also, if strong static types are a bad idea because you can't maintain them all the time, then dynamic typed languages are a bad idea because you don't get static types ever, its always at runtime.

Just because a hammer can't screw in screws doesn't mean its a bad idea, it just means that you can't use it for all use cases. This is the same. You can use static types and for the few cases where you need runtime dynamism, then you use that. That doesn't make the static types in the rest of your code bad. It just gives you additional tools that dynamic types alone don't have.

[1] to various degrees, its not all or nothing like you seem to be implying, there are levels of middle ground, like std::variant which maintains safety but you need to enumerate all possible types, or std::any which is fully dynamic but you give up compile time checks

You can't actually implement everything in Brainfuck. You can implement something which is performing an equivalent computation in an abstract, mathematical sense. But there's no way to write Firefox or Windows or Fortnite in Brainfuck. Turing completeness means you can evaluate any computable function of type N -> N (and the many things isomorphic to that), it doesn't give you anything else.
Just as a Turing machine requires an infinitely sized tape to compute any computable function, so too would brainfuck require an indirect sized tape (or whatever it’s called in BF) to compute any computable function. Since memory is finite, neither of these properties are actually available on real hardware.
I am interested in computation. Period. Not any particular model of computation (programming language); and not merely computation with functions from N->N.

Quoting from "http://math.andrej.com/2006/03/27/sometimes-all-functions-ar..."

"The lesson is for those “experts” who “know” that all reasonable models of computation are equivalent to Turing machines. This is true if one looks just at functions from N to N. However, at higher types, questions of representation become important, and it does matter which model of computation is used."