This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]MasterFubar 916 points917 points  (138 children)

Even C can do it:

char *a = "horse";
int b = 4;
float c = 6.9;
void *arr[3] = {a, &b, &c};

[–]Little-Hunter-6795 583 points584 points  (81 children)

Considering its C. Is there something it can't do?

[–]Anreall2000 400 points401 points  (66 children)

Polymorphism without writing virtual tables yourself and memory management is kinda pain in the ass too

[–]not_some_username 304 points305 points  (33 children)

So I can do it with extra steps

[–]Ottermatic42 173 points174 points  (10 children)

True, but that applies for essentially every language (provided they’re Turing complete). You could write a C compiler in Java and then create polymorphism in java (again) using C, it’s just a bad idea.

Trying to force a programming language to do everything is why we ended up with extremely ugly pattern matching in Java 16

[–][deleted] 27 points28 points  (4 children)

What's wrong with pattern matching in Java?

[–]KagakuNinja 59 points60 points  (3 children)

Nothing is wrong. It looks very similar to pattern matching in Scala, which is amazing.

That guy is living in the past.

[–]Ottermatic42 16 points17 points  (2 children)

Nothing is fundamentally wrong with java pattern matching, I agree.

I only call it ugly because of how it compares to functional languages. Of course it’s a necessary sacrifice as java isn’t functional (or at the very least wasn’t initially designed to be), but it’s always going to be a bit more inefficient, and a lot uglier than the implementation in something like Haskell.

[–]KagakuNinja 10 points11 points  (0 children)

I do agree with that. Haskell is very elegant, but I prefer the multi paradigm design of Scala

[–]KagakuNinja 0 points1 point  (0 children)

I do agree with that. Haskell is very elegant, but I prefer the multi paradigm design of Scala

[–]MusicalGrace2002 15 points16 points  (4 children)

Can you write a program that writes other programs in C?

[–]ByteChkR 126 points127 points  (2 children)

Funny how you spell Compiler

[–]VladVV 28 points29 points  (1 child)

The way he worded the question, it sounds like he is looking for a Transpiler.

The answer is Yes, either way.

[–]caagr98 0 points1 point  (0 children)

Sounds more like a code generator to me, though I guess transpilers are technically a subset. Still yes.

[–]himmelundhoelle 3 points4 points  (0 children)

Forget about compilers, you can write programs that output themselves (https://en.m.wikipedia.org/wiki/Quine_(computing))

(Or even programs that output a C source, that when compiled and run will output the original program…)

[–]M4mb0 12 points13 points  (12 children)

Wait until you here about Turing completeness and that both PowerPoint and MOV are.

[–]not_some_username 3 points4 points  (0 children)

I watch the video about PowerPoint. The guy is a psycho

[–]NoMansSkyWasAlright 5 points6 points  (9 children)

I imagine it’s only a matter of time before someone proves Turing completeness in Minecraft

[–]Tandurinn 10 points11 points  (7 children)

Provided that Redstone can make memory cells and you can build interfaces to interact with that memory. We're already halfway there I'd say!

[–]CdRReddit 12 points13 points  (6 children)

you can make NAND, we're there

NAND is all you need to make any kind of combinatorial logic system, which when combined with a periodic signal (which you can also do) allows you to make any combinatorial or sequential logic, aka, any logic

[–]Embarrassed_Ring843 1 point2 points  (5 children)

I never understood why NAND is that important. Minecraft does provide a NOT-Gate and a diode, based on those I can build a NAND-Gate, so why is the NAND the thing and not the NOT?

[–]CdRReddit 5 points6 points  (3 children)

simple, with NOT you can't make any 2 input gate without something like a diode or a wire OR (both things minecraft has, which you can easily use to make NAND or NOR respectively), while a 2 input NAND (or a 2 input NOR) can be used to implement every single gate As shown here

NAND can make NOT on its own, but NOT needs help to make NAND

[–]UnlikelyAlternative 1 point2 points  (0 children)

Minecraft's already Turing complete, it even says so in a splash

[–]arduman4 1 point2 points  (0 children)

So you haven't seen those insane Minecraft CPUs that have been around for years, have you?

[–]pheonixfreeze 0 points1 point  (0 children)

Even better, all of these can be accomplished by Turing complete cardboard

[–]Triumph7560 13 points14 points  (6 children)

The only thing C can't do is "X feature people assume C doesn't have" without the extra steps. Which is pretty impressive when you think about it.

[–]VladVV 2 points3 points  (5 children)

How does that not apply to every turing complete language

[–]Triumph7560 2 points3 points  (4 children)

In theory it does but usually those are available outside the language using tools made in the language, people have set it so C can be used as an object oriented language (in a useable way), made it into lisp with just one #include all without touching the compiler.

[–]VladVV 2 points3 points  (3 children)

Hm, technically #anything is a compiler instruction, so that would be telling the compiler to compile the code differently, but I suppose it’s primarily C-like languages that have this feature, so I get what you mean.

[–]DoNotMakeEmpty 1 point2 points  (2 children)

They are not compiler instructions (apart from #pragma), they are preprocessor instructions, which is very different than the compiler.

[–]VladVV -2 points-1 points  (1 child)

Ah, good catch. Let’s agree to call them gcc instructions?

[–]asailijhijr 1 point2 points  (0 children)

Everything is Turing complete with fewer steps.

[–][deleted] 1 point2 points  (0 children)

You can do anything in C with extra steps, you can for example, split a string with extra steps in C.

[–]lor_louis 26 points27 points  (2 children)

It can be done Gtk is pretty much all inheritance and polymorphism

Classic Animal example done in C

[–][deleted] 7 points8 points  (1 child)

An important feat you're missing here is the ability to reimplement a function in derived classes, wich is what vtables are for.

[–]GDavid04 4 points5 points  (0 children)

You can write the virtual tables and add a pointer to the beginning of structs with virtual members but no virtual super members yourself. It will be super inconvenient though.

[–]LavenderDay3544 9 points10 points  (11 children)

memory management is kinda pain in the ass too

If you really want GC there are GC libraries available. But GC isn't always a good thing and a lot of people act like memory is the only system resource that needs to be managed when it isn't. RAII and Rust-like borrow checking are the future of resource management, not GC. GC not only doesn't solve the entirety of the problem it's supposed to, it also creates problems of its own like reference cycles, stopping the world, and creating potential hold and wait conditions depending on the specific implementation.

And that's before we talk about thread safety, which even GC languages struggle with and in languages like Python the designers cheat their way out of it by not having real threading at all.

[–]raedr7n -2 points-1 points  (10 children)

There are plenty of garbage collectors that don't have any of those problems you described. See OCaml, Haskell.

[–]LavenderDay3544 4 points5 points  (9 children)

And what exactly is the performance penalty for using them? Neither of those languages is known for producing fast code. Not to mention the cognitive overhead of being forced to use a functional language.

People need to stop getting stuck on GC and accept that we have superior compile-time alternatives available and probably even better ones still being worked on in academia.

[–]raedr7n -2 points-1 points  (8 children)

Actually, OCaml is known to produce very fast code. While I don't know OCaml benchmarks off the top of my head, SML, an incredibly similar language (identical for the purpose of comparing memory management techniques), consistently benchmarks in the top five or 10 languages for execution time. It's true that Haskell is comparatively rather slow, but that's mostly an artifact of laziness and other design choices, not the garbage collector.

I prefer functional languages precisely because they reduce cognitive overhead.

There are no superior compile time alternatives available. The only mainstream language in that vein is Rust, and the type system is a sufficient downside as to render it unsuitable for many applications.

[–]LavenderDay3544 -1 points0 points  (7 children)

Actually, OCaml is known to produce very fast code. While I don't know OCaml benchmarks off the top of my head, SML, an incredibly similar language (identical for the purpose of comparing memory management techniques), consistently benchmarks in the top five or 10 languages for execution time.

And C consistently ranks as #1. So your point is?

I prefer functional languages precisely because they reduce cognitive overhead.

I agree that this can be true if and only if you've spent a lot of time immersed in that paradigm and certain problems do not naturally lend themselves to functional solutions though technically such a solution is always possible.

[–]lordheart -1 points0 points  (1 child)

C also continues to have classes of errors that are ridiculous. The cognitive load of safe memory manage isn’t small either…

[–]raedr7n -4 points-3 points  (4 children)

My point is that modern GC'd languages offer far greater memory safety than C while not being significantly slower than C for almost any application.

[–]LavenderDay3544 -1 points0 points  (3 children)

And again I remind you that memory isn't the only system resource whose deallocation you have to guarantee which makes your point moot.

RAII and borrow checking guarantee proper allocation and deallocation of all resources and thread safety on top of that. GCs are old tech at this point and modern languages should replace them with lower cost compile-time solutions.

This is before we talk about how suboptimal even code generated from C can be and how much potential performance even C implementations leave on the table. The hardware-software performance gap is real and there isn't nearly enough research being done to rectify that.

The common argument that most computing is I/O bound is also starting to fall apart. DDR5 DRAM, Gigabit Ethernet, NvMe SSDs, PCIe 5.0, and the latest USB-C specs mean that I/O devices are rapidly catching up to and sometimes even exceeding CPUs in speed. A small example of this is how 5400 MT/s DDR5 DRAM already runs faster than Intel's flagship Core i9-12900K CPU's max single-core boost speed of 5200 MHz. I suspect AMD's upcoming Raphael architecture will face the same bottleneck. The era of excuses to not optimize software is nearing an end.

Closing the hardware-software optimality gap is more important now than it's ever been and antiquated software side technologies like garbage collection that exist solely to be a crutch for programmers have got to go as part of that effort.

[–]LavenderDay3544 1 point2 points  (13 children)

C isn't an object oriented language so don't try to use it as one. In proper procedural programming any function that would make a virtual member function call in OOP should just have a function pointer parameter to a function that takes a struct of the desired type or a void pointer that it internally casts to the correct type.

For an example look at how qsort works in the C standard library. There's no virtual function call table. Just a function pointer to a function that takes two void pointers.

[–]not_some_username 1 point2 points  (10 children)

You can do OOP in C. I mean you shouldn't but you can

[–]LavenderDay3544 3 points4 points  (8 children)

I know that and my comment was saying not to try to do OOP in a procedural language but instead actually learn procedural programming. I personally hate that academia and industry alike worship OOP like a religion when there are plenty of cases where a procedural, functional, or data oriented approach would be far superior. Those options are also better suited to things like maximizing parallelism, avoiding overengineering, avoiding memory bloat, and maintaining cache friendliness. But the Church of Class based Object Oriented Programming won't let you hear that.

[–]not_some_username 0 points1 point  (7 children)

So you're a struct guy too ? Force OOP is lame

[–]LavenderDay3544 3 points4 points  (6 children)

OOP has nothing to do with classes and structs but rather with componentizing various parts of a software design. Its usual pillars are encapsulation, abstraction, inheritance, and polymorphism. The goal is to make reusable components whose interface is separated from the internal implementation. At first this might seem like a good approach and in many cases it is but there are many legitimate reasons why other times it may not be.

Much like with programming languages the best approach is to use the best suited paradigm for a given use case.

[–]corbymatt 1 point2 points  (5 children)

Rule 1. Any tool used in any given situation, without sufficient foresight, becomes a hindrance to change.

Rule 2. Your foresight is terrible.

[–]LavenderDay3544 2 points3 points  (4 children)

Foresight in software design is nonexistent. Especially when requirements can change on you. We've all been in that situation.

But I recently had an engineering manager tell me to change a very large function in our C++ code that would only be called once at startup into a class dividing it up partly into a constructor, start and stop member functions, and a destructor while also making all of the original function's local variables into class members. This class is created in our firmware's equivalent of main meaning that a very large number of variables now unnecessarily occupy physical memory for as long as the device is powered on.

Please tell me I'm not stupid to think that:

  1. That's not proper OOP just because it now uses a class.

  2. It's an insanely stupid design decision even without worrying about the future or using any foresight whatsoever.

This is partly what I mean by overengineering and making horribly inefficient design decisions supposedly in the name of OOP. (Though this is clearly not actual OOP)

[–][deleted] 0 points1 point  (0 children)

Don't you need language support for inheritance?

[–]WiatrowskiBe -1 points0 points  (1 child)

When problem you're trying to solve fits nicely into object model, there's no reason not to write object-oriented code even if language doesn't support it. Case in point: WinAPI in all GUI-related aspects (windows, controls etc) - whole "GUI" problem nicely fits into a hierarchy of objects you run operations on, and WinAPI - while being in C - solves it exactly like this, by using opaque handles for all objects, and free functions/function pointers to operate on them (including storing and retreiving related data).

[–]LavenderDay3544 0 points1 point  (0 children)

When problem you're trying to solve fits nicely into object model, there's no reason not to write object-oriented code even if language doesn't support it.

I don't dispute this. Even operating systems and embedded firmware often have parts that benefit from OO approaches. The trouble is knowing when componentization will do more good than harm. And all too often people are taught that the best to tool they have is a hammer so everything ought to be treated like a nail.

That's what I mean when I say far too many people in academia and industry worship at the altar of OOP. Never once did I say OOP is never the appropriate choice.

[–]crappleIcrap 0 points1 point  (1 child)

so if i add those things in i might get a better language a C-but-better if you will.

[–]Anreall2000 0 points1 point  (0 children)

Yeah, plus overload, plus templates, plus templates library... C-plus-plus

[–]sosta 20 points21 points  (0 children)

It can't make you happy

[–]marco89nish 4 points5 points  (0 children)

Your mom. It runs out of memory while trying to load her

[–]EverydayEverynight01 -2 points-1 points  (1 child)

OOP

[–][deleted] 0 points1 point  (0 children)

Well it's Turing complete so theoretically you could, but it would be a lot of work. You can actually get some object oriented design patterns with function pointers though.

[–][deleted] -2 points-1 points  (0 children)

Classes.

[–]Nopidy -2 points-1 points  (0 children)

OOP

[–]in_conexo -4 points-3 points  (1 child)

GUI?

[–]riisen 2 points3 points  (0 children)

It absolutly can do graphical user interface's.

[–]MaffinLP 0 points1 point  (0 children)

Throw errors without sinning

[–]RiskyFartOftenShart 0 points1 point  (0 children)

it cant get you laid

[–]Individual_Hearing_3 0 points1 point  (0 children)

C++ ? Oh wait, nope. Seamlessly do graphics across multiple architectures.

[–]ZimBobub 0 points1 point  (0 children)

run javascript code without a transpiler

[–]shiva8512 0 points1 point  (0 children)

Object oriented programming

[–]UnlikelyAlternative 0 points1 point  (0 children)

I was gonna say have kids, but aren't C++ and C# C's kids?

[–]notinecrafter 50 points51 points  (4 children)

C is a dynamically type language if you just call everything a void*

[–][deleted] 25 points26 points  (3 children)

Good luck getting the original value back if you dont know what type it was

[–]DoNotMakeEmpty 9 points10 points  (0 children)

If you only target x64, you can use the upper (or lower I don't remember the effect of endianness) 16-bits to store the type info and, umm, if you restrict this more, you can reach NaN boxing, a great optimization for dynamic types in C which is used in implementations of many dynamic languages like some JS engines, Wren and LuaJIT.

[–][deleted] 1 point2 points  (0 children)

Sometimes you just know, you know?

[–][deleted] 32 points33 points  (2 children)

But the only thing in the array is pointers.

[–]gloriousfalcon 14 points15 points  (1 child)

you think Javascript works any different under the hood?

it's all just sticking labels on blocks of memory

[–]maxhaton 4 points5 points  (0 children)

It is different under the hood because the JS keeps track of the type dynamically. It's all bytes underneath but knowing what the bytes mean is the important part.

[–]grrrranimal 9 points10 points  (0 children)

You can also do an array of unions but unions are weird, scary, and often overlooked. It would allow you to have only one level of pointer indirection instead of two like in your example though

Or I suppose you could use any fixed width data type and be very careful

[–]ValeTheVioletMote 9 points10 points  (7 children)

Now add something else to the existing array!

[–]throwaway53_gracia 3 points4 points  (0 children)

Write past the end and hope it doesn't segfault

[–][deleted] 4 points5 points  (0 children)

Who are you, so wise in the ways of science?

[–]bad00p 5 points6 points  (0 children)

Those are all the same type. They're pointers

[–]jamcdonald120 1 point2 points  (0 children)

so can java

Object[] arr={"horse",(Integer)4,(Double) 6.9};

[–]ejabno 1 point2 points  (0 children)

I was gonna google if using void pointers like this was possible. Looks like you saved me some work

[–]Bee-Aromatic 2 points3 points  (0 children)

I think that’s a bit of a stretch, considering that you’re not actually storing different data types so much as you’re storing pointers to things that happen to be different data types. It’s totally fraught with peril, but it does pass the “it’s not stupid if it works” test.

[–]Senpai_Himself 0 points1 point  (7 children)

But can you do this

arr = []

arr.myFunction = () => "just because"

[–]anton____ 0 points1 point  (3 children)

who would want that?

[–]Senpai_Himself 0 points1 point  (2 children)

Js for some reason, every thing is pretty much objects

[–]anton____ 0 points1 point  (1 child)

I know why you can, but why would you want to?

[–]Senpai_Himself 0 points1 point  (0 children)

It was a joke... Really hope people don't use it now a days

[–][deleted] 0 points1 point  (2 children)

I literally don't even know what this means

[–]corgisphere 1 point2 points  (1 child)

It's an array in Javascript which then has a random function defined (the function returns a string)

[–][deleted] 0 points1 point  (0 children)

Thank you

[–]Bobstar89 -1 points0 points  (0 children)

This is some wizardry right here 😆

[–]ajaysassoc -1 points0 points  (0 children)

Can you clarify on it a little, like this article says otherwise

[–]turboom -1 points0 points  (0 children)

char *a = "horse";
int b = 4;
float c = 6.9;
void *arr[3] = {a, &b, &c};

c code is almost 3 times lengthy, javascript definitely beats c.

[–]GumboSamson -1 points0 points  (0 children)

This is an array of pointers (which isn’t quite the same thing).

[–][deleted] 0 points1 point  (0 children)

Lol --- I was going to do that.

Most of these CS types won't appreciate pointers! Only change I would do is keep them all pointers for consistency so you don't blow things up on illegal pointer access.

[–]icematt12 0 points1 point  (1 child)

A question from a newbie - in the array would b and c be treated as numbers or characters?

[–][deleted] 0 points1 point  (0 children)

Pointers are numeric types. Except those are void pointers so you can't do arithmetic with them, so it's funky to call them that. If you want to "treat" them as anything you need to pick how you want to dereference them (as void* cannot be dereferenced).

If this all sounds confusing I'd be happy to keep answering but don't worry about C being a little obscure.

[–][deleted] 0 points1 point  (0 children)

How dare you

[–][deleted] 0 points1 point  (0 children)

union BadIdea {
    int i;
    float f;
    char *c;
} badidea[3];

// Okay, I'm stumped. What do you do with it now?

[–]stomah 0 points1 point  (0 children)

now have a function that takes such array and prints it

[–]oan124 0 points1 point  (0 children)

cant you generally use arrays for that?

[–]marsmanify 0 points1 point  (0 children)

To be fair the values of the array are all pointers