Skip to content

Implement abstract classes - #3

Open
amomchilov wants to merge 2 commits into
mainfrom
Alex/abstract-classes
Open

Implement abstract classes#3
amomchilov wants to merge 2 commits into
mainfrom
Alex/abstract-classes

Conversation

@amomchilov

@amomchilov amomchilov commented Feb 19, 2026

Copy link
Copy Markdown
Contributor

This PR is already pretty large and complicated, so I descoped several things that would make it even worse:

  1. Class methods like abstract def self.foo

  2. abstract! on the singleton class, like:

    class C
      class << self
        abstract!
      end
    end

There are tests to ensure those unsupported features raise. I'll open follow-up PRs which implement them.

This was referenced Feb 19, 2026
@amomchilov

amomchilov commented Feb 19, 2026

Copy link
Copy Markdown
Contributor Author

@amomchilov
amomchilov force-pushed the Alex/interfaces branch 2 times, most recently from cf42ec7 to 998bf60 Compare February 19, 2026 23:25
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from f15c512 to 1c4ff1e Compare February 20, 2026 00:32
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from 1c4ff1e to d755cfd Compare February 20, 2026 16:34
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from d755cfd to 4d2d914 Compare February 20, 2026 17:08
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch 2 times, most recently from aab5377 to f0da94c Compare February 21, 2026 00:19
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from f0da94c to ae69e75 Compare February 23, 2026 23:18
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from ae69e75 to 0b95499 Compare February 23, 2026 23:53
@amomchilov
amomchilov force-pushed the Alex/interfaces branch 2 times, most recently from a66d3fb to 860b7f9 Compare February 24, 2026 01:33
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from 0b95499 to 51dcab6 Compare February 24, 2026 01:33
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from 51dcab6 to 2dae200 Compare February 24, 2026 23:32
@amomchilov amomchilov mentioned this pull request Feb 24, 2026
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from 2dae200 to b2735fd Compare February 25, 2026 00:06
@amomchilov
amomchilov marked this pull request as ready for review February 25, 2026 15:07
@amomchilov
amomchilov requested a review from a team February 25, 2026 15:07
Comment thread lib/type_toolkit/abstract_class.rb Outdated
Comment thread lib/type_toolkit/has_abstract_methods.rb
Comment thread README.md Outdated
Comment thread lib/type_toolkit/abstract_class.rb Outdated
Comment thread README.md
Comment thread benchmark/abstract_class_new.rb Outdated
Comment thread benchmark/abstract_class_new.rb Outdated
Comment thread spec/interface_spec.rb Outdated
@amomchilov
amomchilov changed the base branch from Alex/interfaces to graphite-base/3 February 25, 2026 17:52
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from a60ddb0 to a294201 Compare February 25, 2026 22:47
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch 2 times, most recently from 4a9414e to d850751 Compare March 5, 2026 00:56
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from d850751 to 8b250d5 Compare March 5, 2026 01:17
Base automatically changed from Alex/interfaces to main March 5, 2026 01:18
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch 2 times, most recently from fdc6412 to 90a8907 Compare August 21, 2026 00:14
@amomchilov
amomchilov requested a review from soutaro August 21, 2026 00:38

# Restores the original `.new` implementation for the direct subclasses of an abstract class.
#: (Class[AbstractClass]) -> void
def inherited(subclass) # :nodoc:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relying on this inherited method means we only restore .new when user-defined self.inherited hooks call super. If an abstract class defines an inherited hook without super, concrete subclasses keep the abstract .new and can't be instantiated:

class Base
  abstract!

  def self.inherited(_subclass)
    # no super
  end

  abstract def call; end
end

class Impl < Base
  def call; end
end

Impl.new # raises CannotInstantiateAbstractClassError

I wonder if we should install this as a prepended singleton hook, or otherwise wrap/chain the existing hook, and add a regression test so subclass instantiation doesn't depend on user hooks calling super.

@amomchilov amomchilov Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good find. I'll come back to this, because:

  1. Diagnosing and raising in that case requires more state variables, which an upstack branch will have and make trivial
  2. Fixing it requires a bunch more anonymous modules and prepending stuff which is icky and I want to punt on.

It's doable, but not calling super is a skill issue, as the kids would say. There's even a default cop called Lint/MissingSuper which catches it.

For now I just added a test case that documents the broken behaviour.

Tracked in #45

Comment thread lib/type_toolkit/has_abstract_methods.rb Outdated
# typed: strict
# frozen_string_literal: true

class Class

@Morriar Morriar Aug 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amomchilov is it meant to be required standalone?

require "type_toolkit/ext/class"

like we do for not_nil!

require "type_toolkit/ext/nil_assertions"

If yes 2 comments:

  1. We should require lib/type_toolkit/abstract_class.rb‎ in there to avoid undefined constant
  2. We should rename it as type_toolkit/ext/abstract.

I'm not a fan of requiring files under ext directly, it would be cleaner to require features and let them require the internal plumbing properly:

require "type_toolkit/abstract"
require "type_toolkit/nil_assertions"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See https://github.com/Shopify/type_toolkit#cherry-picking-features

  1. True.
  2. Conventionally, ext files are named after the class they extend, not what they do. See https://github.com/rails/rails/tree/main/activesupport/lib/active_support/core_ext for example

@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch 5 times, most recently from 42fe7ab to 02a3678 Compare August 21, 2026 16:35
@amomchilov
amomchilov force-pushed the Alex/abstract-classes branch from 02a3678 to 4dfac15 Compare August 21, 2026 17:06
@amomchilov
amomchilov marked this pull request as ready for review August 21, 2026 17:14
@amomchilov
amomchilov requested a review from Morriar August 21, 2026 17:17
@Morriar
Morriar requested a review from a team August 21, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants