GSoC 2026 Proposal Draft: Support boolean and enum types #27
Replies: 2 comments 3 replies
-
|
A lot of thought has gone into this and it shows, thanks!
Type checking is recursive (I think), so hopefully this just works out of the box. Just write a test first and see if it passes Bdef inspect
"Enum[#{@values.map(&:inspect).join(', ')}]"
end
alias to_s inspectOverriding core Ruby methods may cause a situation where a future debugger goes crazy because they don't see the full object when they Cdef complex_type?(expression:)
Low::Types::COMPLEX_TYPES.include?(expression) ||
expression.is_a?(Low::Types::Enum::Definition) ||
typed_array?(expression:) ||
typed_hash?(expression:)
endI think a generalised
Ddef type_matches_value?(type:, value:, proxy:)
if type.instance_of?(Class)
return type.match?(value:) if Low::TypeQuery.complex_type?(expression: type)
return type == value.class
elsif Low::TypeQuery.complex_type?(expression: type) && type.respond_to?(:match?)
return type.match?(value:)
elsif type.instance_of?(::Low::TypeExpression)
type.validate!(value:, proxy:)
return true
end
false
endMake def type_matches_value?(type:, value:, proxy:)
if Low::TypeQuery.complex_type?(expression: type)
return type.match?(value:)
elsif type.instance_of?(Class)
return type == value.class
elsif type.instance_of?(::Low::TypeExpression)
type.validate!(value:, proxy:)
return true
end
false
endFUse |
Beta Was this translation helpful? Give feedback.
-
|
@maedi |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Project Proposal
1. Project Title
Support Boolean and Enum Types in LowType- Mentored by @maedi2. Project Size
Small (~90 hours)This scope is appropriate because the project extends LowType's existing type-expression pipeline instead of introducing a new parser or a new external integration. The work is focused and well-bounded:
Booleancomplex typeEnum[*values]typeThe proposal deliberately keeps parser changes in
Lowkeyout of scope and concentrates onLowTypeevaluation and validation.3. Project Length
Small project (
~90 hours) over8 weeks.4. Abstract
LowType provides optional inline type annotations and runtime type checking in Ruby without introducing a build step. This project will add two important missing type constructs: a strict
Booleancomplex type and a parameterizedEnum[*values]type for closed sets of literal or mixed Ruby values. The implementation will follow LowType's existing three-phase architecture:File Loadvia Lowkey parsing,Class Loadvia expression evaluation and method redefinition, andRuntimevalidation per call. The final result will let users express common Ruby constraints such as flags and finite allowed-value sets with natural LowType syntax, backed by tests and documentation.5. Problem Statement
Today, LowType already supports standard Ruby classes such as
String,Integer,Array, andHash, along with framework-aware or domain-aware complex types such asTuple,Status, andHeaders. However, two highly practical type constructs are still missing:Boolean, even though it is already documented as an unreleased complex typeEnum[*values], which is currently only at concept stageThese gaps matter because they represent common real-world Ruby constraints:
trueorfalseWithout first-class support, users must approximate boolean behavior with ad hoc unions and cannot express closed-value sets as part of LowType's inline type syntax. That weakens LowType's goal of being a lightweight, expressive, Ruby-native typing tool.
There is also an architectural requirement specific to
Enum: invalid defaults should fail early during LowType'sClass Loadevaluation phase rather than only when the method is called later at runtime. Supporting this cleanly is important for correctness, predictability, and developer feedback.6. Proposed Solution
Architecture Fit
This work fits LowType's current architecture and does not require a parser rewrite.
The current flow is:
LowType.includedloads those proxies,Low::Evaluatorevaluates the stored Ruby expressions, andLow::Redefinerredefines methods around the evaluated expressions.Low::TypeExpression#validate!validates method arguments, local typed values, accessors, and return values on each call.That flow already exists in the codebase:
The key design choice in this proposal is to add
BooleanandEnumas values that can already be evaluated duringClass Load, instead of extending Lowkey's parser. Since Lowkey stores the original expression string andLow::Evaluatorlater runs:supporting
BooleanandEnum[...]is primarily an evaluation/validation problem inside LowType.Expected User-Facing Syntax
The syntax targeted by this proposal is:
The goal is for these expressions to work anywhere LowType already accepts type expressions:
type()expressionsI tested nested type expressions first, and the current array validation path is already recursive. That suggests nested forms such as
Array[Boolean]orArray[Enum[:draft, :published]]should likely work onceBooleanandEnum[...]are integrated into the existing type-expression machinery. I would still keep explicit tests for those cases.A. Dedicated
BooleanComplex TypeBooleanis already listed inlib/types/complex_types.rb, but the current placeholder uses the generic factory:That is not a correct model for Ruby booleans.
trueandfalseare instances ofTrueClassandFalseClass, soBooleanneeds a dedicated implementation instead of a generated subclass ofObject.A likely implementation is:
This keeps the external type surface clean:
Boolean, notTrueClass | FalseClassBoolean | nilremains possible through the existing union/default-value syntaxdisplayrather than overriding Ruby's coreinspectorto_sExpected usage:
B. Parameterized
Enum[*values]Enum[...]would be a parameterized type object. LowType already hints at this pattern throughStatus[...], where calling[]returns an object rather than the bareStatusclass.Enum[...]should fit that broader model rather than introducing a one-off special case.The syntax should be:
I would represent
Enum[...]as a definition object created duringClass Load:Note that
displayis used instead of overriding Ruby's coreinspectorto_smethods. This keeps debugger output intact while still giving a clean user-facing representation for error messages.This design has a few advantages:
Enum[:symbol, "string", 123]==)Enum[...]is just a class method call evaluated duringClass LoadExpected valid usage:
C. Type Recognition in
TypeQueryAt the moment, LowType recognizes:
COMPLEX_TYPESThere is an existing gap here.
Low::Types::COMPLEX_TYPES.include?(expression)checks against the type class itself — for exampleStatus. But when a user writesStatus[200], that returns a value object rather than theStatusclass. SoCOMPLEX_TYPES.include?(Status[200])returnsfalse, meaningStatus[200]may not currently be recognized as a type expression.Enum[...]would have the same problem if handled naively.The fix is to introduce a generalized
typed_definition?method, as maedi suggested, so parameterized LowType objects are recognized through a shared mechanism rather than one explicit check per type. To keep runtime validation coherent, I would also separate match-based complex-type recognition from structural typed-array/hash recognition:With that split,
complex_type?stays focused on types that are expected to answermatch?, whiletyped_array?andtyped_hash?remain their own structural cases. The exact implementation oftyped_definition?should be confirmed with mentor during Community Bonding. The important part is that it should recognize parameterized LowType objects such asStatus[...]andEnum[...]via one shared protocol or internal convention, not via a brittle class-name check.D. Runtime Validation in
TypeExpressionThe main validation path already exists in
Low::TypeExpression#validate!, which delegates element-by-element checks totype_matches_value?.Following maedi's suggestion, if
Statusis made to supportmatch?at the type level,type_matches_value?can be simplified toward:This is cleaner than the previous version because:
respond_to?(:match?)— the contract guaranteesmatch?exists on every complex typeBoolean,Enum::Definition,Status, and any future parameterized typeTyped arrays and hashes would remain on their existing validation path and would no longer be grouped under
complex_type?in this design, since they are not themselvesmatch?-based types. The core direction is to make parameterized types and complex types converge on the samematch?contract.E. Class Load Validation for Invalid Enum Defaults
The issue requires invalid enum defaults to fail early — during
Class Load, not at runtime when the method is first called.Expected invalid example:
The default
"blue"is not in["red", "orange", "yellow"]and should be rejected immediately when LowType evaluates the expression, not later.The current evaluator stores expressions but does not eagerly validate defaults. I would add an explicit validation step after the expression is built:
Even if type recognition is generalized, I would initially scope this eager validation to enum-backed expressions so that existing semantics for other defaults are not unintentionally changed. If mentor feedback suggests generalizing this further, that can be discussed during implementation.
F. Error Rendering and Developer Feedback
LowType's proxies already build error messages from
@expression.valid_types. The goal is to make all parameterized type objects render cleanly through that existing path using theirdisplaymethod, without special-casing any specific type.Current-style errors look like:
"Invalid argument type 'Integer' for parameter 'greeting'. Valid types: 'String'"Expected enum/boolean output would be:
The
valid_typesmethod should usedisplaygenerically for any type that responds to it, rather than special-casingEnum::Definitionor any other specific class:This means:
Enum::Definitionrenders viadisplay→Enum["red", "orange", "yellow"]Booleanrenders viadisplay→Booleandisplayinspectis never overridden, so debugger output remains intactG. Test Strategy
I would mirror the current testing style rather than introducing a new spec structure.
Unit-level coverage:
Booleanspec modeled afterspec/units/types/status_spec.rbEnumspec forEnum[...]construction, matching, and formattingTypeQueryandTypeExpressionbehavior where neededFeature-level coverage:
spec/fixtures/spec/features/type(), nested arrays, invalid defaults, and error messagesExample fixture/spec pair for eager enum validation:
Using
loadrather thanrequire_relativefor these negative class-load cases would make the failure explicit and repeatable within the test suite.Main Areas of the Codebase
Based on my codebase study, the project will primarily touch:
lib/types/complex_types.rblib/queries/type_query.rblib/definitions/evaluator.rblib/expressions/type_expression.rblib/types/, likelyboolean.rbandenum.rbspec/units/types/status_spec.rbas the reference pattern for type-specific specsspec/features/andspec/fixtures/for end-to-end behavior coverageScope Boundaries
The following are intentionally out of scope for this proposal:
LowkeyEnum[...]and existing type-expression model7. Project Deliverables
The expected deliverables are:
Booleancomplex typeEnum[*values]type supporting mixed Ruby valuesClass Loadvalidation for invalid enum declarations/defaultstype()usageIf the implementation integrates naturally with all existing expression entry points, support should work consistently anywhere LowType already accepts type expressions, including method parameters, typed accessors, return types where relevant, and
type().8. Timeline / Milestones
Community Bonding
Enum[*values]with mentortyped_definition?implementation andmatch?contract with mentormainbranchWeek 1 — Boolean: Implementation + Tests
BooleanbehaviorTypeFactory.complex_type(Object)placeholder with a dedicatedBooleanclass inlib/types/boolean.rblib/types/complex_types.rbtype()Week 2 — Enum: Core Implementation
Enum[*values]Enum::Definitionclass inlib/types/enum.rbtyped_definition?inLow::TypeQuerycomplex_type?to usetyped_definition?Week 3 — Enum: Validation + TypeExpression
type_matches_value?using Maedi's cleaner versionStatushas a class-levelmatch?to satisfy the shared contracttype()==Week 4 — Class Load Validation for Enum Defaults
Class Loadvalidation for invalid enum defaultsloadin specsWeek 5 — Error Messages + Edge Cases
displayonBooleanandEnum::Definitionvalid_typesto userespond_to?(:display)genericallyArray[Boolean]work out of the boxWeek 6 — Review + Refinement
Week 7 — Documentation + Cleanup
BooleanandEnumusageWeek 8 — Buffer
9. Related Work
Typed boolean and closed-value enum constructs already exist in most modern typed languages. TypeScript supports
booleanand literal unions such as"red" | "orange" | "yellow". Python provides similar expressiveness throughtyping.Literal["red", "orange"]. In both cases, these are primarily static-analysis or compile-time constructs.In Ruby, the closest existing approaches are
RBS/Steep,Sorbet, anddry-types:RBSandSteepprovide static type checking, but typically require separate signature files and an external checking workflowSorbetoffers gradual typing, but also introduces a larger annotation and tooling modeldry-typesprovides rich runtime constraints, but through an explicit DSL rather than LowType's inline method-signature styleLowType's approach is different: it evaluates type expressions inline during
Class Load, using normal Ruby method default-value syntax and then performs optional runtime validation. That makes it especially well suited to Ruby's dynamic and open-class model. AddingBooleanandEnumas complex types extends this existing design rather than introducing a new syntax, a separate schema language, or a parser-heavy type layer.10. Previous Contributions
LowType — PR #23: Support empty hash return type when key => value defined
While studying the codebase in preparation for this proposal, I identified and fixed a bug where empty hash literals (
{}) were not recognized as valid typed-hash expressions in union type expressions. For example:This would fail before the fix. The changes touched
lib/queries/type_query.rbandlib/expressions/type_expression.rb— the same files central to this proposal — and included regression coverage in the existing fixture and feature spec files.Codebase Study
Beyond the PR, I read through the full LowType source including:
lib/definitions/evaluator.rblib/expressions/type_expression.rblib/queries/type_query.rblib/types/complex_types.rbspec/units/andspec/features/I also studied the architecture split between Lowkey (parsing) and LowType (evaluation/validation) introduced in the recent
mainbranch refactor that has not yet been released as a gem.Mentor Discussion
Discussed design choices for
BooleanandEnumdirectly with @maedi, covering strict boolean semantics, mixed-value enum support, and the Class Load vs Runtime validation phase distinction.11. About You
My name is Piyush Goenka. I am a second-year B.Sc. Computer Science student at BITS Pilani.
I was drawn to LowType because it solves a real problem elegantly — bringing optional runtime type safety to Ruby without a build step or a separate schema language. Most typing solutions in Ruby feel like they are fighting the language. LowType feels like it belongs in it. That philosophy of staying lightweight and Ruby-native is what made this project stand out to me over other GSoC ideas.
Open Source
I am an active open source contributor. I contributed extensively to the Palisadoes Foundation — the organization behind the Talawa project, a modular open source platform used by community-based organizations worldwide. My contributions were consistent and substantial enough that I was made a maintainer of the organization. This means I have real experience navigating large unfamiliar codebases, following contributor conventions, writing reviewable code, and taking responsibility for the quality of a shared project.
Achievements
I won Smart India Hackathon (SIH), India's largest national-level hackathon, which required building and shipping a working technical solution under a tight deadline — a skill directly relevant to a GSoC project with fixed weekly milestones.
I am comfortable navigating real codebases I did not write, contributing before I fully understand every detail, and communicating clearly with maintainers. This project is a natural next step for me.
12. Availability
Timezone: IST (UTC+5:30)
Hours per week: I can commit 10–12 hours per week during the coding period, which is sufficient for a small (~90 hour) project over 8 weeks. I have no internships, travel, or other major commitments planned for this summer, so GSoC will be my primary focus during the coding period.
Exam conflicts: BITS Pilani's academic calendar may overlap slightly with the start of the GSoC period. Any such overlap is manageable and will not affect my ability to meet weekly milestones. I will flag any schedule constraints to the mentor during Community Bonding well in advance.
13. Communication Plan
I plan to communicate in a lightweight and consistent way throughout the project:
I am on Discord and reachable on GitHub. IST (UTC+5:30) is my timezone and I can adjust communication windows to overlap with the mentor's availability during Community Bonding.
14. Additional Notes
This proposal is intentionally scoped as a focused improvement to LowType's complex type system rather than a general parser or type-system overhaul. The work is bounded, the extension points are clear, and the existing
Statusimplementation provides a solid model to follow.If I complete the planned deliverables ahead of schedule, I would use the remaining time for:
Beyond GSoC
GSoC is not the end goal for me — it is a starting point. I am genuinely interested in LowType as a project and in the broader problem of making Ruby more expressive without sacrificing its dynamic nature. I intend to continue contributing after the summer, whether that means tackling other open issues, reviewing PRs, helping new contributors understand the codebase, or working on the next feature. I want to be a long-term contributor to this project, not just a summer one.
Beta Was this translation helpful? Give feedback.
All reactions