DEV Community

Cover image for Proof of that OOP is "Applied Brain Damage"!
Thomas Hansen
Thomas Hansen

Posted on

Proof of that OOP is "Applied Brain Damage"!

I went to ChatGPT and I asked it the following question ...

Create C# code that encapsulates a C# controller returning data from my "todo" database and its "items" table. I want you to use all best practices, such as data repository patterns, ViewModels, DataModels (entity framework), dependency injection of interfaces implementing the service class, etc, etc, etc. Everything considered "best practices" in today's software development industry related to C#. Return ALL required code, as separate code blocks, including mapping configuration, IoC configuration, ViewModel classes, etc. Only return code for the Read verb from CRUD.

It produced 9 files for me, with 110 lines of code. You can find these at the bottom of the article. But before I show you the C# code, I want to show you the Hyperlambda equivalent.

.arguments
data.connect:todo
   data.read
      table:items
   return-nodes:x:@data.read/*
Enter fullscreen mode Exit fullscreen mode

The Hyperlambda example is 5 lines of code, the C# example is 110 lines of code. This implies that from a technical debt point of view, C# is 22 times more "expensive" than Hyperlambda. And yes, code is technical debt!

And since we know for a fact that a software developer can produce the same amount of lines of code, regardless of the language, this implies that if your developer needs one day to implement the Hyperlambda code, the same developer would need 22 days to implement the C# example.

The problem isn't C#, the problem originates from OOP. This is because everything that's "interesting" in computing is basically a function; SQL (functions), HTTP (each verb is a "function"), inference (GPT transformation using LLMs are also "functions"). Since OOP ties the verb to the subject, this creates awkward situations, and adds complexity, forcing you to "remap" your OO code for "functional" scenarios - And since everything that's interesting in computing is a function, this implies that everything needs to be "mapped" to a functional solution, bypassing OOP's associations between the verb and the subject ...

Of the 23 original Design Patterns from GoF, 19 are basically bad implementations of features we've got "for free" in functional programming languages

Basically, every time you need to do anything in OOP, you first have to "use OOP to get rid of OOP", to create a "functional solution using OO mechanisms", which of course for obvious reasons inflates your codebase to absurdity ...

Facts are, any paradigm that forces you to write 22 times as much code is literally fundamentally broken. This is because time literally is money when it comes to software development, and you are morally obligated to choosing the least time consuming path as you implement anything your manager tells you to implement. Implying ...

Using OOP professionally is the equivalent of STEALING from your employer!

Below is the C# code in case you don't believe me. And at this point I know some of you will say "it's not about creating the code, it's about maintaining the code". This of course is a rubbish argument, and I can pretty much guarantee you that my 5 lines of Hyperlambda code is also at least one order of magnitude more easily maintained than its C# equivalent.

C# code

Below you can find the result ChatGPT gave me, and I can pretty much guarantee you that this is code you'll probably find thousands of times in your employer's codebase, implying your employer has literally paid 22 times as much to develop the project than what he or she had to pay ...

1. Item Entity (EF Data Model)

public class Item
{
    public int Id { get; set; }
    public string Title { get; set; }
    public bool IsCompleted { get; set; }
    public DateTime CreatedAt { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

2. TodoDbContext

using Microsoft.EntityFrameworkCore;

public class TodoDbContext : DbContext
{
    public TodoDbContext(DbContextOptions<TodoDbContext> options) : base(options) { }

    public DbSet<Item> Items { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

3. ViewModel

public class ItemViewModel
{
    public int Id { get; set; }
    public string Title { get; set; }
    public bool IsCompleted { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

4. Repository Interface & Implementation

public interface IItemRepository
{
    Task<IEnumerable<Item>> GetAllAsync();
    Task<Item?> GetByIdAsync(int id);
}
Enter fullscreen mode Exit fullscreen mode

ItemRepository.cs

public class ItemRepository : IItemRepository
{
    private readonly TodoDbContext _context;

    public ItemRepository(TodoDbContext context)
    {
        _context = context;
    }

    public async Task<IEnumerable<Item>> GetAllAsync()
    {
        return await _context.Items.AsNoTracking().ToListAsync();
    }

    public async Task<Item?> GetByIdAsync(int id)
    {
        return await _context.Items.AsNoTracking().FirstOrDefaultAsync(i => i.Id == id);
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Service Layer

public interface IItemService
{
    Task<IEnumerable<ItemViewModel>> GetAllAsync();
    Task<ItemViewModel?> GetByIdAsync(int id);
}
Enter fullscreen mode Exit fullscreen mode

ItemService.cs

public class ItemService : IItemService
{
    private readonly IItemRepository _repository;
    private readonly IMapper _mapper;

    public ItemService(IItemRepository repository, IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }

    public async Task<IEnumerable<ItemViewModel>> GetAllAsync()
    {
        var items = await _repository.GetAllAsync();
        return _mapper.Map<IEnumerable<ItemViewModel>>(items);
    }

    public async Task<ItemViewModel?> GetByIdAsync(int id)
    {
        var item = await _repository.GetByIdAsync(id);
        return item == null ? null : _mapper.Map<ItemViewModel>(item);
    }
}
Enter fullscreen mode Exit fullscreen mode

6. AutoMapper Profile

using AutoMapper;

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Item, ItemViewModel>();
    }
}
Enter fullscreen mode Exit fullscreen mode

7. Controller (Read-Only)

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ItemsController : ControllerBase
{
    private readonly IItemService _service;

    public ItemsController(IItemService service)
    {
        _service = service;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<ItemViewModel>>> GetAll()
    {
        var items = await _service.GetAllAsync();
        return Ok(items);
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<ItemViewModel>> GetById(int id)
    {
        var item = await _service.GetByIdAsync(id);
        return item == null ? NotFound() : Ok(item);
    }
}
Enter fullscreen mode Exit fullscreen mode

Facts are, OOP is based upon a superstitious belief system, created to facilitate for more easily maintained code - While the result becomes the exact opposite. Don't believe me? Scroll up and read my Hyperlambda code next to its C# equivalent one more time ...

Top comments (10)

Collapse
 
dyfet profile image
David Sugar

While the broader argument may be more valid, I feel this is a contrived use case. The big clue is Entity Framework, which I would never touch. EF maps and translates a SQL problem, which is itself a functional language, into an OOP problem, and then back again. It's like driving a broken steam locomotive to haul lawn chairs around. If I were considering this in C# I would just use a simple direct SQL query and maybe something like Dapper to auto-map fields instead.

Collapse
 
polterguy profile image
Thomas Hansen

You are correct, and I had ChatGPT port it to Dapper, which reduced the LOC count - But I was still at 105 LOC, while the HL example is only 5 LOC. For the record, it reduced the number of files from 9 to 1, which significantly increases readability and maintenance, but it does little to reduce LOC count actually ...

But I agree, EF is the crime boss ... ;)

Collapse
 
dyfet profile image
David Sugar • Edited

If you write it from scratch it can be much smaller, but in any case yes, it would not be a LOC of 5 because C# doesn't abstract tables or other things inside it, so there is boilerplate wrappers and struct definitions no matter what you do. If I wanted a similar low LOC I would actually use Ruby, which is also OOP but has fully mutable objects and some functional aspects.

The dirty secret in Ruby is you often reduce everything to a dsl, and then express the problem in that, so you might be able to do or potentially prototype something like Hyperlambda expressed with Ruby syntax ;). Since the class struct is fully mutable in Ruby, you could probably have it auto-generate the Ruby mapped table objects directly from the db schema, too. Ruby does something very similar to this for very small LOCs for web apps with Sinatra, which decomposes the entire api and json payloads by directly constructing Ruby objects automatically and expressing it in Ruby syntax for you. Ruby SQL ORBs may already do much to all of this as is, too.

A dsl implemented as Ruby syntax can also drop other boiler-plate syntax, too, like ();s around function arguments, etc, much like found in ocaml, so you can get a similarly very pure expression of the problem in the dsl. It may be a cheat, but Ruby is OOP ;).

Collapse
 
pandres95 profile image
Pablo Andrés Dorado Suárez • Edited

Nope!

I would say this is proof that OOP is not a Swiss knife, not anything else.

As with any paradigm (or design style), there are problems where you get the benefits from using OOP. Thousands of Libraries, SDKs, Frameworks, and higher-order systems get the benefits of using OOP.

OF COURSE, simple solutions (like the one below) might be better not using OOP. But that just proves that simple problems require simple solutions. Doesn't mean OOP is brain-damaged or anything like that.

Also, now that I'm seeing it better, this is a TODO List example. If something, the above is a tutorial on how to do C# best practices in the form of a TODO List example. So, by comparing an example to illustrate how to follow some architectural style in C# vs. how to do a simple fetch in Hyperlambda, is just missing the point 😂

P.S. At first, I was having second thoughts about writing this comment (thought it would be just some kind of meme), but then I saw your post on the comments in your LinkedIn post. Worth the clarification, because your argument is utterly wrong, and dangerously misleading to newcomers (a.k.a. the majority of the audience of dev.to). Good luck ChatGPT-ing new articles, because it did the trick, I actually lost 5 minutes of my time (at least I was waiting for some CI Checks meanwhile, but I could have perfectly seen some reels on IG instead).

Collapse
 
polterguy profile image
Thomas Hansen

Doesn't mean OOP is brain-damaged or anything like that

OOP was sold as a general purpose paradigm. I'll agree with you on that it works for some problem domains.

OF COURSE, simple solutions (like the one below) might be better not using OOP

If you take away the parts I had to write in this article to simply get you to comment, this is basically what you're left with :)

is just missing the point

If you're an enterprise software developer, this is literally 80% of all the code you have ever seen in your professional life ...

because your argument is utterly wrong

Errh, you actually admitted previously in your own comment that my main argument was correct ...

Collapse
 
polterguy profile image
Thomas Hansen

how to do a simple fetch in Hyperlambda, is just missing the point

Best practices Hyperlambda, and best practices C#. I tried creating a minimal API with pure ADO, and it ended up at 105 LOC.

Collapse
 
filip_klaric_1d8a1caa2af4 profile image
Filip Klaric

Really? Good luck with making functional app without at least service layer. Also, so say hello to taking 2 hours to find why your app doesn't work when someone does DB change and some of your properties disappear without warning.

Collapse
 
polterguy profile image
Thomas Hansen

Good luck with making functional app without at least service layer

The Hyperlambda literally is that functioning app ...

Collapse
 
dwd profile image
Dave Cridland

If I repeat your prompt but with c# changed for hyperlambda, I also get a load of code.

Maybe that C# is doing a lot more than your hyperlambda is? Maybe you don't need it, of course - but you absolutely did ask for it.

Collapse
 
polterguy profile image
Thomas Hansen

If I repeat your prompt but with c# changed for hyperlambda, I also get a load of code

ChatGPT has no idea how to create Hyperlambda. What you're getting is some bastardised hallucination between Bash and Python. Try it here, and tell it to for instance; "Generate Hyperlambda for an HTTP endpoint that read contacts from crm database", or something ...

Maybe that C# is doing a lot more than your hyperlambda is

Nope, I've been a C# developer for 25 years, and I invented Hyperlambda. The C# code doesn't do anything my code doesn't do.

but you absolutely did ask for it

Nope, I asked it to generate code I've seen a bajillion times in a bajillion companies, and the stuff that all senior C# devs would churn out without even thinking about it ...

This is the "standard best practices C# example" for doing what it did ...