The blog post "Frankenstein's __init__
" explores the complexities and potential pitfalls of Python's __init__
method, particularly when dealing with inheritance. It argues against placing complex logic or side effects within __init__
, as this can lead to unpredictable behavior and violate the principle of least astonishment, especially in scenarios involving inheritance and multiple inheritance. The author advocates for using factory functions or a separate post_init
method for such logic, leading to more transparent, testable, and maintainable code. This approach decouples object creation from initialization logic, allowing for greater flexibility and avoiding unexpected interactions between parent and child class initializers.
While often derided for its verbosity and perceived outdatedness, Objective-C possesses a unique charm for some developers. Its Smalltalk-inspired message-passing paradigm, dynamic nature, and human-readable syntax foster a sense of playfulness and expressiveness that can be missing in more rigid languages. This article argues that Objective-C's idiosyncrasies, including its use of square brackets and descriptive method names, contribute to a more approachable and understandable coding experience, particularly for those coming from a less technical background. Despite its decline in popularity since Swift's arrival, Objective-C's enduring legacy and distinct character continue to resonate with a dedicated community who appreciate its subjective appeal.
HN commenters largely agree that Objective-C's verbosity, while initially appearing cumbersome, contributes to its readability and maintainability. Several users appreciate the explicit nature of message passing and how it clarifies code intention. Some argue that modern Objective-C, with features like literals and blocks, addresses many of the verbosity complaints. The dynamic nature of the language and the power of its runtime are also highlighted as benefits. A few commenters express nostalgia for Objective-C, contrasting it with Swift, which they perceive as less enjoyable or flexible, despite its modern syntax. There's also a discussion around the challenges of learning Objective-C and the impact of Apple's transition to Swift.
Guy Steele's "Growing a Language" advocates for designing programming languages with extensibility in mind, enabling them to evolve gracefully over time. He argues against striving for a "perfect" initial design, instead favoring a core language with powerful mechanisms for growth, akin to biological evolution. These mechanisms include higher-order functions, allowing users to effectively extend the language themselves, and a flexible syntax capable of accommodating new constructs. Steele emphasizes the importance of "bottom-up" growth, where new features emerge from practical usage and are integrated into the language organically, rather than being imposed top-down by designers. This allows the language to adapt to unforeseen needs and remain relevant as the programming landscape changes.
Hacker News users discuss Guy Steele's "Growing a Language" lecture, focusing on its relevance even decades later. Several commenters praise Steele's insights into language design, particularly his emphasis on evolving languages organically rather than rigidly adhering to initial specifications. The concept of "worse is better" is highlighted, along with a discussion of how seemingly inferior initial designs can sometimes win out due to their adaptability and ease of implementation. The challenge of backward compatibility in evolving languages is also a key theme, with commenters noting the tension between maintaining existing code and incorporating new features. Steele's humor and engaging presentation style are also appreciated. One commenter links to a video of the lecture, while others lament that more modern programming languages haven't fully embraced the principles Steele advocates.
"Architecture Patterns with Python" introduces practical architectural patterns for structuring Python applications beyond simple scripts. It focuses on Domain-Driven Design (DDD) principles and demonstrates how to implement them alongside architectural patterns like dependency injection and the repository pattern to create well-organized, testable, and maintainable code. The book guides readers through building a realistic application, iteratively improving its architecture to handle increasing complexity and evolving requirements. It emphasizes using Python's strengths effectively while promoting best practices for software design, ultimately enabling developers to create robust and scalable applications.
Hacker News users generally expressed interest in "Architecture Patterns with Python," praising its clear writing and practical approach. Several commenters highlighted the book's focus on domain-driven design and its suitability for bridging the gap between simple scripts and complex applications. Some appreciated the free online availability, while others noted the value of supporting the authors by purchasing the book. A few users compared it favorably to other architecture resources, emphasizing its Python-specific examples. The discussion also touched on testing strategies and the balance between architecture and premature optimization. A couple of commenters pointed out the book's emphasis on using readily available tools and libraries rather than introducing new frameworks.
The blog post "You Need Subtyping" argues that subtyping, despite sometimes being viewed as complex or unnecessary, is a crucial tool for writing flexible and maintainable code. It emphasizes that subtyping allows for writing generic algorithms that operate on a range of related types without needing modification for each specific type. The author illustrates this through examples using shapes and animal sounds, demonstrating how subtyping enables reusable functions that handle different subtypes without explicit type checks. The post further champions subtype polymorphism as a superior alternative to approaches like typeclasses or enums for handling diverse data types, highlighting its ability to gracefully accommodate future type extensions without altering existing code. Ultimately, the author advocates for embracing subtyping as a fundamental concept for building robust and adaptable software systems.
HN users generally disagreed with the premise that subtyping is needed. Several commenters argued that subtyping adds complexity, especially in larger projects, and that its benefits are often overstated. Alternatives like composition and pattern matching were suggested as potentially superior approaches. Some argued that the author conflated subtyping with polymorphism, while others pointed out that the benefits mentioned in the article, like code reuse and extensibility, could be achieved without subtyping. A few commenters discussed the specific example used in the blog post, highlighting its contrived nature and suggesting better alternatives. The overall sentiment was that subtyping is a tool, sometimes useful, but not a necessity.
This post advocates for using Ruby's built-in features like Struct
and immutable data structures (via freeze
) to create simple, efficient value objects. It argues against using more complex approaches like dry-struct
or Virtus
for basic cases, highlighting that the lightweight, idiomatic approach often provides sufficient functionality with minimal overhead. The article illustrates how Struct
provides concise syntax for defining attributes and automatic equality and hashing based on those attributes, fulfilling the core requirements of value objects. Finally, it demonstrates how to enforce immutability by freezing instances, ensuring predictable behavior and preventing unintended side effects.
HN users largely criticized the article for misusing or misunderstanding the term "Value Object." Commenters pointed out that true Value Objects are immutable and compared by value, not identity. They argued that the article's examples, particularly using mutable hashes and relying on equal?
, were not representative of Value Objects and promoted bad practices. Several users suggested alternative approaches like using Struct
or creating immutable classes with custom equality methods. The discussion also touched on the performance implications of immutable objects in Ruby and the nuances of defining equality for more complex objects. Some commenters felt the title was misleading, promoting a non-idiomatic approach.
This post advocates for using Ruby's built-in features, specifically Struct
, to create value objects. It argues against using gems like Virtus
or hand-rolling complex classes, emphasizing simplicity and performance. The author demonstrates how Struct
provides concise syntax for defining immutable attributes, automatic equality comparisons based on attribute values, and a convenient way to represent data structures focused on holding values rather than behavior. This approach aligns with Ruby's philosophy of minimizing boilerplate and leveraging existing tools for common patterns. By using Struct
, developers can create lightweight, efficient value objects without sacrificing readability or conciseness.
HN commenters largely criticized the article for misusing or misunderstanding the term "value object." They argued that true value objects are defined by their attributes and compared by value, not identity, using examples like 5 == 5
even if they are different instances of the integer 5
. They pointed out that the author's use of Comparable
and overriding ==
based on specific attributes leaned more towards a Data Transfer Object (DTO) or a record. Some questioned the practical value of the approach presented, suggesting simpler alternatives like using structs or plain Ruby objects with attribute readers. A few commenters offered different ways to implement proper value objects in Ruby, including using the Values
gem and leveraging immutable data structures.
The blog post explores how C, despite lacking built-in object-oriented features like polymorphism, achieves similar functionality through clever struct design and function pointers. It uses examples from the Linux kernel and FFmpeg to demonstrate this. Specifically, it showcases how defining structs with common initial members (akin to base classes) and using function pointers within these structs allows different "derived" structs to implement their own versions of specific operations, effectively mimicking virtual methods. This enables flexible and extensible code that can handle various data types or operations without needing to know the specific concrete type at compile time, achieving runtime polymorphism.
Hacker News users generally praised the article for its clear explanation of polymorphism in C, particularly how FFmpeg and the Linux kernel utilize function pointers and structs to achieve object-oriented-like designs. Several commenters pointed out the trade-offs of this approach, highlighting the increased complexity for debugging and the potential performance overhead compared to simpler C code or using C++. One commenter shared personal experience working with FFmpeg's codebase, confirming the article's description of its design. Another noted the value in understanding these techniques even if using higher-level languages, as it helps with interacting with C libraries and understanding lower-level system design. Some discussion focused on the benefits and drawbacks of C++'s object model compared to C's approach, with some suggesting modern C++ offers a more manageable way to achieve polymorphism. A few commenters mentioned other examples of similar techniques in different C projects, broadening the context of the article.
Steve Losh's blog post explores leveraging the Common Lisp Object System (CLOS) for dependency management within Lisp applications. Instead of relying on external systems, Losh advocates using CLOS's built-in dependent maintenance protocol to automatically track and update derived values based on changes to their dependencies. He demonstrates this by creating a depending
macro that simplifies defining these dependencies and automatically invalidates cached values when necessary. This approach offers a tightly integrated, efficient, and inherently Lisp-y solution to dependency tracking, reducing the need for external libraries or complex build processes. By handling dependencies within the language itself, this technique enhances code clarity and simplifies the overall development workflow.
Hacker News users discussed the complexity of Common Lisp's dependency system, particularly its use of the CLOS dependent maintenance protocol. Some found the system overly complex for simple tasks, arguing simpler dependency tracking mechanisms would suffice. Others highlighted the power and flexibility of CLOS for managing complex dependencies, especially in larger projects. The discussion also touched on the trade-offs between declarative and imperative approaches to dependency management, with some suggesting a hybrid approach could be beneficial. Several commenters appreciated the blog post for illuminating a lesser-known aspect of Common Lisp. A few users expressed interest in exploring other dependency management solutions within the Lisp ecosystem.
The blog post "Inheritance and Subtyping" argues that inheritance and subtyping are distinct concepts often conflated, leading to inflexible and brittle code. Inheritance, a mechanism for code reuse, creates a tight coupling between classes, whereas subtyping, focused on behavioral compatibility, allows substitutability. The author advocates for composition over inheritance, suggesting interfaces and delegation as preferred alternatives for achieving polymorphism and code reuse. This approach promotes looser coupling, increased flexibility, and easier maintainability, ultimately leading to more robust and adaptable software design.
Hacker News users generally agree with the author's premise that inheritance is often misused, especially when subtyping isn't the goal. Several commenters point out that composition and interfaces are generally preferable, offering greater flexibility and avoiding the tight coupling inherent in inheritance. One commenter highlights the "fragile base class problem," where changes in a parent class can unexpectedly break child classes. Others discuss the nuances of Liskov Substitution Principle and how it relates to proper inheritance usage. One user specifically calls out Java's overuse of inheritance, citing the infamous AbstractSingletonProxyFactoryBean
. A few dissenting opinions mention that inheritance can be a useful tool when used judiciously, especially in domains like game development where hierarchical relationships are naturally occurring.
Summary of Comments ( 24 )
https://news.ycombinator.com/item?id=43735724
HN users largely discuss the impracticality and contrived nature of the example in the article, which explores creating an object through a Frankensteinian assembly of
__init__
components. Several commenters find the exploration interesting but ultimately useless, highlighting how it obfuscates code and introduces unnecessary complexity. The prevailing sentiment is that while conceptually intriguing, such a method is counterproductive to writing clear, maintainable code and would likely never be used in a real-world scenario. Some find the exploration of metaprogramming and the inner workings of Python mildly interesting, but the overall consensus leans towards viewing the article's content as a clever but impractical exercise.The Hacker News post titled "Frankenstein's
__init__
" sparked a discussion with several insightful comments revolving around the complexities and potential pitfalls of inheritance in object-oriented programming, specifically in Python. The conversation largely agrees with the author's premise about the awkwardness of large, complex__init__
methods often necessitated by inheritance.Several commenters highlight the tension between adhering to Liskov's Substitution Principle and the practical challenges of designing class hierarchies. One commenter points out the difficulties encountered when subclasses require different initialization parameters than their parent class, leading to unwieldy
**kwargs
usage and obscure error handling. This resonates with the article's concerns about the "Frankenstein" nature of such constructors. They further argue that forcing conformance to a rigid structure through inheritance can be detrimental to code clarity and maintainability, suggesting composition as a more flexible alternative.Another commenter emphasizes the importance of careful consideration when designing class hierarchies and choosing between inheritance and composition. They propose that simpler designs are often preferable and that the need for complex inheritance structures might indicate a flaw in the overall design. They also caution against overusing inheritance solely for code reuse, reiterating the benefits of composition in such scenarios.
The idea of "role interfaces" is brought up as a potential solution to the inheritance dilemma. This approach involves defining smaller, more focused interfaces that classes can implement, allowing for greater flexibility and composability. This is presented as a way to avoid the rigid constraints of traditional inheritance while still maintaining a degree of structure and ensuring substitutability.
One commenter, focusing on the specific example in the original article, questions whether abstract base classes would be a suitable solution to address the initialization challenges. This sparks a brief discussion about the nuances of abstract methods and their role in enforcing certain behaviors within a class hierarchy.
Finally, a recurring theme in the comments is the preference for simplicity and avoiding over-engineering. Several commenters express the view that complex inheritance hierarchies often add unnecessary complexity and advocate for keeping designs as simple as possible. This aligns with the overall sentiment that Frankensteinian
__init__
methods are a symptom of a deeper design issue.